func (t *Tomb) Go(f func() error) error {
	t.m.Lock()
	defer t.m.Unlock()
	select {
	case <-t.dead:
		return errors.New("called after all goroutines terminated")
	default:
	}
	t.alive++
	go t.run(f)
	return nil
}
Go runs f in a new goroutine and tracks its termination.

If f returns a non-nil error, t.Kill is called with that
error as the death reason parameter.

It is f's responsibility to monitor the tomb and return
appropriately once it is in a dying state.

It is safe for the f function to call the Go method again
to create additional tracked goroutines. Once all tracked
goroutines return, the Dead channel is closed and the
Wait method unblocks and returns the death reason.

Calling the Go method after all tracked goroutines return
causes a runtime panic. For that reason, calling the Go
method a second time out of a tracked goroutine is unsafe.
package tomb
import (
	"context"
	"errors"
	"fmt"
	"sync"
)
var (
	ErrStillAlive = errors.New("still alive")
	ErrDying = errors.New("dying")
)
type Tomb struct {
	m      sync.Mutex
	alive  int
	dying  chan struct{}
	dead   chan struct{}
	reason error
	parent interface{}
	child  map[interface{}]childContext
}
type childContext struct {
	context interface{}
	cancel  func()
	done    <-chan struct{}
}
func New() *Tomb {
	return &Tomb{
		dead:   make(chan struct{}),
		dying:  make(chan struct{}),
		reason: ErrStillAlive,
	}
}
func (t *Tomb) Dead() <-chan struct{} {
	return t.dead
}
func (t *Tomb) Dying() <-chan struct{} {
	return t.dying
}
func (t *Tomb) Wait() error {
	<-t.dead
	t.m.Lock()
	reason := t.reason
	t.m.Unlock()
	return reason
}