package common import ( stdlib_context "context" "sync" "time" ) type Config interface { Name() string Starter() StartFct Children() []Config Add(Config) } type Message interface{} type Services map[string]Context type ContextState interface{} type Context interface { stdlib_context.Context Config() Config Parent() Context Services() Services ChildContext(Config) Context State() ContextState WithState(ContextState) Context Mailbox() chan Message WaitGroup() *sync.WaitGroup Stop() } type FctCtx = func(Context) type StartFct = FctCtx // async Runners func RunCtx(ctx Context, fct FctCtx) { ctx.WaitGroup().Add(1) go func() { defer ctx.WaitGroup().Done() fct(ctx) }() } // Context implementation type context struct { cfg Config parent Context children []Context state ContextState mailbox chan Message } func (ctx *context) Config() Config { return ctx.cfg } func (ctx *context) Parent() Context { return ctx.parent } func (ctx *context) Services() Services { return ctx.parent.Services() } func (ctx *context) ChildContext(cfg Config) Context { cctx := makeCtx(cfg, ctx) ctx.Services()[cfg.Name()] = cctx ctx.children = append(ctx.children, cctx) return cctx } func (ctx *context) State() ContextState { return ctx.state } func (ctx *context) WithState(state ContextState) Context { ctx.state = state return ctx } func (ctx *context) Mailbox() chan Message { return ctx.mailbox } func (ctx *context) WaitGroup() *sync.WaitGroup { return ctx.parent.WaitGroup() } func (ctx *context) Done() <-chan struct{} { return ctx.parent.Done() } func (ctx *context) Stop() { // ctx.Warn("Only application-level service can be stopped } func makeCtx(cfg Config, parent Context) *context { return &context{ cfg: cfg, parent: parent, mailbox: make(chan Message, 10), } } // top-level application context type appContext struct { *context services Services waitgroup *sync.WaitGroup doneCh chan struct{} } func (ctx *appContext) ChildContext(cfg Config) Context { cctx := makeCtx(cfg, ctx) ctx.services[cfg.Name()] = cctx ctx.children = append(ctx.children, cctx) return cctx } func (ctx *appContext) Services() Services { return ctx.services } func (ctx *appContext) WithState(state ContextState) Context { ctx.state = state return ctx } func (ctx *appContext) WaitGroup() *sync.WaitGroup { return ctx.waitgroup } func (ctx *appContext) Done() <-chan struct{} { return ctx.doneCh } func (ctx *appContext) Stop() { close(ctx.doneCh) } func AppContext(cfg Config) Context { ctx := &appContext{ context: makeCtx(cfg, nil), services: Services{}, waitgroup: &sync.WaitGroup{}, doneCh: make(chan struct{}), } ctx.services[cfg.Name()] = ctx return ctx } // implement interface context.Context from standard library func (ctx *context) Deadline() (deadline time.Time, ok bool) { return time.Time{}, false } func (ctx *context) Err() error { select { case _, ok := <-ctx.Done(): if !ok { return stdlib_context.Canceled } default: return nil } return nil } func (ctx *context) Value(key interface{}) interface{} { return nil }