// add starts two goroutines that (1) kill the catacomb's tomb with any // error encountered by the worker; and (2) kill the worker when the // catacomb starts dying. func (catacomb *Catacomb) add(w worker.Worker) { // The coordination via stopped is not reliably observable, and hence not // tested, but it's yucky to leave the second goroutine running when we // don't need to. stopped := make(chan struct{}) catacomb.wg.Add(1) go func() { defer catacomb.wg.Done() defer close(stopped) if err := w.Wait(); err != nil { catacomb.Kill(err) } }() go func() { select { case <-stopped: case <-catacomb.tomb.Dying(): w.Kill() } }() }
// gotStarted updates the engine to reflect the creation of a worker. It must // only be called from the loop goroutine. func (engine *engine) gotStarted(name string, worker worker.Worker) { // Copy current info; check preconditions and abort the workers if we've // already been asked to stop it. info := engine.current[name] switch { case info.worker != nil: engine.tomb.Kill(errors.Errorf("fatal: unexpected %q manifold worker start", name)) fallthrough case info.stopping, engine.isDying(): logger.Debugf("%q manifold worker no longer required", name) worker.Kill() default: // It's fine to use this worker; update info and copy back. logger.Infof("%q manifold worker started", name) info.starting = false info.worker = worker engine.current[name] = info // Any manifold that declares this one as an input needs to be restarted. engine.bounceDependents(name) } }
func stopWorker(w worker.Worker) error { w.Kill() return w.Wait() }
// CheckKill Kill()s the supplied worker and Wait()s for its error, which it // returns for further analysis, or fails the test after a timeout expires. // It doesn't Assert and is therefore suitable for use from any goroutine. func CheckKill(c *gc.C, w worker.Worker) error { w.Kill() return CheckKilled(c, w) }
func kill(w worker.Worker) error { w.Kill() return w.Wait() }