Git Product home page Git Product logo

tomb's People

Contributors

hartzell avatar niemeyer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

tomb's Issues

Wait() never unblocks when no goroutine is running

Hi,

I am using tomb to monitor rather short-lived goroutines. It is possible that no goroutine is running when I do Kill(nil) and Wait(), or even no goroutines has ever been started. I find it strange and not very intuitive that in this case Wait() blocks forever. Wouldn't it make more sense to close the dead channel immediately when no registered goroutine is running?

Wait() hangs forever if no goroutine was run with Go()

This program:

package main

import "gopkg.in/tomb.v2"

func main() {
	tomb := &tomb.Tomb{}
	tomb.Kill(nil)
	tomb.Wait()
}

crashes with:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
gopkg.in/tomb%2ev2.(*Tomb).Wait(0xc42004a050, 0x0, 0x0)
	/home/free/go/src/gopkg.in/tomb.v2/tomb.go:126 +0x56
main.main()
	/srv/src/go/src/bar.go:8 +0x60
exit status 2

I think tomb should either provide a way for callers to get the number of alive goroutines (as per #12), so they can skip the Wait() call.

Would be nice to have `Running()` and `Terminating()`

Consider below pseudo code

type Job struct {
    tomb *tomb.Tomb
}

func (j *Job) Start() error {
    if j.tomb.Alive() {
        return errors.New("job is still running")
    }
    j.tomb = &tomb.Tomb{}
    j.tomb.Go()
}

I want to create service that has long running goroutine, but only one, above code is not safe because Alive() can return false when tomb is in Dying state. So, I would like to know if tomb is Running (so dying or alive) or not. And there doesn't seem to be a way to actually do this with tomb.

I could read Dead() chan, but this would work only when tomb died, but if it never was started then it would hang forever as it is initialized as open chan.

Easy way would be something like this:

func (t *Tomb) Running() bool {
	t.m.Lock()
	defer t.m.Unlock()
	return t.alive > 0
}

Terminating() would be also useful since we don't always wait on channels in goroutines, but this can be easily done even without modifications to tomb.

func (t *Tomb) Terminating() bool {
        select {
            case <-t.dying:
                return true
            default:
                return false 
        }
}

please make a release

It would be nice to be able to point to a specific release tag instead of a commit hash to ensure build reproducibility. Could you please make a release (i.e. tag the last commit as v2.0)? Thanks

tomb kills self after the last goroutine completes

hi, i would like to understand the rationale of this block of code.

func (t *Tomb) run(f func() error) {
        err := f()
        t.m.Lock()
        defer t.m.Unlock()
        t.alive--
        if t.alive == 0 || err != nil {
                t.kill(err)
                if t.alive == 0 {
                        close(t.dead)
                }
        }
}

i am fitting Tomb into a long running service where goroutines are started and completed. the Tomb will kill itself after the last goroutine finishes while the service is still running and will still launch more goroutines. what is the expected usage of Tomb in this case? what will happen if the code is changed to

func (t *Tomb) run(f func() error) {
        err := f()
        t.m.Lock()
        defer t.m.Unlock()
        t.alive--
        if err != nil {
                t.kill(err)
                if t.alive == 0 {
                        close(t.dead)
                }
                return
        }
        if t.alive == 0 {
                select {
                default:
                        return
                case <-t.dying:
                        close(t.dead)
                }
        }
}

Bug: Tomb `Wait` blocks forever when no Goroutine is ever spawned.

If you call tmb.Wait() on a tomb that has never called tmb.Go, it blocks forever.

Here's a failing test case to reproduce the bug:

package tomb_test

import (
	"testing"
	"time"

	tomb "gopkg.in/tomb.v2"
)

func TestTombWaitDoesNotBlockWhenNoGoRoutinesSpawned(t *testing.T) {
	var tmb tomb.Tomb

	tmb.Kill(nil)

	done := make(chan struct{})
	go func() {
		err := tmb.Wait()
		if err != nil {
			t.Fatal(err)
		}
		close(done)
	}()

	select {
	case <-time.After(5 * time.Second):
		t.Fatal("Tomb did not exit in time.")
	case <-done:
		// Success!
	}
}

Here's the output of the test case using tomb d5d1b58:

$ go test
--- FAIL: TestTombWaitDoesNotBlockWhenNoGoRoutinesSpawned (5.00s)
	tomb_test.go:26: Tomb did not exit in time.
FAIL
exit status 1
FAIL	github.com/jpittis/tombbug	5.008s

`Wait()` deadlocks after `Kill()` if `Go()` has never been called

If the following occurs:

  1. a tomb is declared
  2. the Kill() method is called on it (without having first called Go())
  3. the Wait() method is called

then a deadlock occurs, as Wait() is waiting for the dead channel to be closed, which is done by the final terminating goroutine, which in this case have never been created.

As a result, a later call to Go() does not result in a runtime panic, like the documentation could be interpreted to suggest it should:

Calling the Go method after all tracked goroutines return causes a runtime panic.

Similarly, the documentation for Wait():

Wait blocks until all goroutines have finished running, and then returns the reason for their death.

could also be interpreted to mean a deadlock should not occur, as it is vacuously true that "all goroutines have finished running" if no goroutines were started in the first place.

See the following code snippet:

	var t tomb.Tomb
	t.Kill(errors.New("foo"))
	t.Go(func() error {
		time.Sleep(time.Second)
		fmt.Println("I'm alive")
		return errors.New("shouldn't have happened")
	})
	fmt.Printf("Received from dead tomb: %v\n", t.Wait())

Here are some ready-made Go Playground examples:

This is either a convenient feature (guarantee that first Go() call will not panic), or a bug (inconsistent behavior of Wait() depending on whether Go() has previously been called).

I'm inclined to favor the former, as I think it makes tombs safer to use in most cases. It is a problem if one assumes (as I did) that Wait() will not deadlock if no tracked goroutines have been created. I think the documentation for Go(), Wait(), and Dead() could to be updated to make this a bit clearer to users. What do you think about this?

Usage Question / panic, tomb.go called after all goroutines terminated

Getting:

2015/10/30 23:10:40 http: panic serving [::1]:56269: tomb.Go called after all goroutines terminated

The use case may be not quite what you had envisioned.

I'm waiting on a usb/serial device and want to be able to cancel waiting (ctrl-c to kill the process hangs indefinitely right now because I can't close the port while I'm still waiting on an outstanding read that will never arrive).

so the flow is:

DoSomeProcessing()
...
func (msngr *Messenger) ReceiveAsync() (Message, error) {
    ch := make(chan recieveAsync)
    msngr.logger.Info.Println("Entering Tomb")
    msngr.tomb.Go(
        func() error {
            message, err := msngr.receive()
            messageRecieved := recieveAsync{Msg: message, Err: err}

            select {
            case ch <- messageRecieved:
                close(ch)
            case <-msngr.tomb.Dying():
                msngr.logger.Info.Println("Got dying from tomb")
                rec := recieveAsync{Msg: Message{}, Err: errors.New("Received Cancellation")}
                ch <- rec
                close(ch)
            }
            return nil
        })

    rec := <-ch
    msngr.logger.Info.Println("exiting tomb")
    return rec.Msg, rec.Err
}

So during execution the process should enter that, block on the rec:=<- ch multiple times, but also provide me the ability to kill it externall (on ctrl-c in this case).

At least that's the theory and it works the first time, my logs seem to indicate that the failure occurs after multiple reads, I don't see an overlap and an overlap shouldn't happen in normal process flow.

I'm pretty sure i'm missing something fundamental here, just not sure what it is. I'm also not returning a useful error at this point, I'm not sure how to approach that just yet.

License BSD3

It would be nice to set the license explicitly in the repository. It seems to be BSD-3.

Panics can happen if the first callback finishes very quickly

Consider the somewhat contrived example:

        nothing := func() error { return nil }
        tb := &tomb.Tomb{}
        tb.Go(nothing)
        time.Sleep(100 * time.Millisecond)
        tb.Go(nothing)
        tb.Wait()

If you run this, it will panic. You could certainly make the argument that there's no need for using a goroutine if your callback can return immediately, but imagine the case where the callback does some validation and error checking and fails quickly if problems are found but otherwise does some longer running operation.

Seemingly impossible panic: tomb.Go called after all goroutines terminated

Firstly: thanks for the awesome package.

Secondly, I must be missing something obvious here, but the following call stack would seem, from the panic, to be impossible:

panic: tomb.Go called after all goroutines terminated [recovered]
        panic: tomb.Go called after all goroutines terminated

goroutine 937 [running]:
github.com/govim/govim.(*govimImpl).DoProto.func1(0xc000171d40, 0xc00044fdf8)
        /home/myitcv/gostuff/src/github.com/myitcv/govim/channel_cmds.go:127 +0x1b6
panic(0x674360, 0x738b40)
        /home/myitcv/gos/src/runtime/panic.go:961 +0x15d
gopkg.in/tomb%2ev2.(*Tomb).Go(0xc000171de8, 0xc0004fcac0)
        /home/myitcv/gostuff/pkg/mod/gopkg.in/[email protected]/tomb.go:155 +0x106
github.com/govim/govim.(*govimImpl).run(0xc000171d40, 0xc0004d2360, 0xc0004cad80)
        /home/myitcv/gostuff/src/github.com/myitcv/govim/govim.go:472 +0x1c78
github.com/govim/govim.(*govimImpl).Run.func1(0x4d95e3, 0xc000218d70)
        /home/myitcv/gostuff/src/github.com/myitcv/govim/govim.go:404 +0x2a
github.com/govim/govim.(*govimImpl).DoProto(0xc000171d40, 0xc0003b0e20, 0x0, 0x0)
        /home/myitcv/gostuff/src/github.com/myitcv/govim/channel_cmds.go:131 +0x6a
github.com/govim/govim.(*govimImpl).Run(0xc000171d40, 0x6f89e0, 0xc0003b0f20)
        /home/myitcv/gostuff/src/github.com/myitcv/govim/govim.go:403 +0x52
github.com/govim/govim/testdriver.(*TestDriver).runGovim(0xc0001717a0, 0x0, 0x0)
        /home/myitcv/gostuff/src/github.com/myitcv/govim/testdriver/testdriver.go:383 +0x73
github.com/govim/govim/testdriver.(*TestDriver).tombgo.func1(0x0, 0x0)
        /home/myitcv/gostuff/src/github.com/myitcv/govim/testdriver/testdriver.go:326 +0x41
gopkg.in/tomb%2ev2.(*Tomb).run(0xc000171868, 0xc0005fc0a0)
        /home/myitcv/gostuff/pkg/mod/gopkg.in/[email protected]/tomb.go:163 +0x38
created by gopkg.in/tomb%2ev2.(*Tomb).Go
        /home/myitcv/gostuff/pkg/mod/gopkg.in/[email protected]/tomb.go:159 +0xba

Because the very fact the call stack is as shown surely indicates there is at least one go routine that has not terminated, i.e. the one that is attempting to call tomb.Go?

Waiting when no tomb.Go call has been made

I just tried this and it seems that the behaviour is to just block forever. Is this intentional?

I tried this because I thought of being clever and do this in my secondary main function:

func rmain() (err error) {
    // ...
    var t tomb.Tomb
    defer func() {
        t.Kill(err)
        err = t.Wait()
    }

    // ...

   return
}

Such that whenever I returned, the main function would always kill the managed goroutines and wait for them to exit. However. Sometimes an error occurs before I even perform a Go call, which makes the function never return.

go test error

go test error
jlzhang:tomb.v1/ (v1) $ go version
go version go1.13.3 linux/mips64le

jlzhang:tomb.v1/ (v1) $ go test

gopkg.in/tomb.v1_test

./tomb_test.go:42:3: Fatalf format %q reads arg #2, but call has 1 arg
FAIL gopkg.in/tomb.v1 [build failed]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.