func ctx() *Ctx { c := AppCtx() if c == nil { dbg.Fatal("no context for %d", runtime.AppId()) } return c }
// Return the current app context. func AppCtx() *Ctx { ctxlk.Lock() defer ctxlk.Unlock() id := runtime.AppId() c := ctxs[id] if c == nil { return nil } return c }
// Create a new application context, it's methods are // implicit and accessed using the runtime application context. // It's a panic to call New twice in the same process. // IO, Env, Dot, and Ns are shared with the parent context by default. // Use NewX() or DupX(), to start using new ones or create dups. // The first context created initializes them from the underlying OS. func New() *Ctx { usingApp = true old := runtime.AppId() ctxlk.Lock() pc := ctxs[old] n := len(ctxs) ctxlk.Unlock() runtime.NewApp() id := runtime.AppId() if old == id && n > 0 { panic("runtime bug: old ctx == new ctx") } c := mkCtx(id) ctxlk.Lock() if ctxs[id] != nil { panic("app.new: double call") } ctxs[id] = c ctxlk.Unlock() dprintf("new ctx %d\n", c.Id) if n == 0 { c.Args = os.Args } if pc != nil { pc.io.lk.Lock() pc.io.n++ pc.io.lk.Unlock() pc.lk.Lock() c.env = pc.env c.dot = pc.dot c.ns = pc.ns c.io = pc.io c.bg = pc.bg pc.lk.Unlock() } else { NewEnv(nil) NewDot("") NewNS(nil) NewIO(nil) } dump("new") return c }
func fatal(warn bool, args ...interface{}) { c := AppCtx() dprintf("fatal...\n") dump("fatal") if c == nil { panic("fatal") } ctxlk.Lock() n := len(ctxs) ctxlk.Unlock() sts := mkSts(args...) efn := func() { c.post("exit") c.Sts = sts c.io.closeAll(sts) close(c.Sig, sts) close(c.Wait, sts) ctxlk.Lock() delete(ctxs, runtime.AppId()) ctxlk.Unlock() } if n <= 1 { dprintf("c %d: dbg fatal (%d args)\n", c.Id, len(args)) efn() close(out) close(err) <-outdone <-errdone os.Args = c.Args if warn { dbg.Fatal(args...) // never returns } else { dbg.Exits(args...) // never returns } } dprintf("c %d: app fatal (%d args, %d apps)\n", c.Id, len(args), n) if sts != nil && warn { Warn("%s", sts) } efn() panic("fatal") }
// To be deferred in the main program so it recovers from Fatals and returns. // For safety, Atexits are run by the call to Fatal and not here. func Exiting() { if r := recover(); r != nil { dprintf("exiting: %v...\n", r) if r == "fatal" { return } panic(r) } dprintf("exiting...\n") dump("exiting") ctxlk.Lock() id := runtime.AppId() c := ctxs[id] delete(ctxs, id) ctxlk.Unlock() if c != nil && c.io != nil { c.io.closeAll(nil) } }
func vprintf(fmts string, args ...interface{}) { if !Debug || !Verb { return } dbg.Printf("%d: %s", runtime.AppId(), fmt.Sprintf(fmts, args...)) }
// returns true if the app ctx exited func Exited() bool { ctxlk.Lock() defer ctxlk.Unlock() return ctxs[runtime.AppId()] == nil }