github.com/haraldrudell/parl@v0.4.176/g0/go-context.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package g0 7 8 import ( 9 "context" 10 "sync/atomic" 11 12 "github.com/haraldrudell/parl" 13 "github.com/haraldrudell/parl/pdebug" 14 ) 15 16 const ( 17 g1ccSkipFrames = 1 18 g1ccPrepend = "— " 19 ) 20 21 // goContext is a promotable private field 22 // - public methods: Cancel() Context() EntityID() 23 // - goContext is based on parl.NewCancelContext 24 type goContext struct { 25 goEntityID // EntityID() 26 wg parl.WaitGroup 27 // - updatable, therefore must be atomic access 28 ctxp atomic.Pointer[context.Context] 29 // cancelListener is function invoked immediately prior to 30 // parl.InvokeCancel 31 cancelListener atomic.Pointer[func()] 32 } 33 34 // newGoContext returns a subordinate context with Cancel and Context methods 35 // - uses non-pointer atomics 36 func newGoContext(fieldp *goContext, ctx context.Context) (g *goContext) { 37 if ctx == nil { 38 panic(parl.NilError("ctx")) 39 } 40 if fieldp != nil { 41 g = fieldp 42 *g = goContext{goEntityID: *newGoEntityID()} 43 } else { 44 g = &goContext{goEntityID: *newGoEntityID()} 45 } 46 var ctx2 = parl.NewCancelContext(ctx) 47 g.ctxp.Store(&ctx2) 48 return 49 } 50 51 // Cancel signals shutdown to all threads of a thread-group. 52 func (c *goContext) Cancel() { 53 if f := c.cancelListener.Load(); f != nil { 54 (*f)() 55 } 56 // if caller is debug, debug-print cancel action 57 if parl.IsThisDebugN(g1ccSkipFrames) { 58 parl.GetDebug(g1ccSkipFrames)("CancelAndContext.Cancel:\n" + pdebug.NewStack(g1ccSkipFrames).Shorts(g1ccPrepend)) 59 } 60 parl.InvokeCancel(*c.ctxp.Load()) 61 } 62 63 // Context returns the context of this cancelAndContext. 64 // - Context is used to detect cancel using the receive channel Context.Done. 65 // - Context cancellation has happened when Context.Err is non-nil. 66 func (c *goContext) Context() (ctx context.Context) { 67 return *c.ctxp.Load() 68 } 69 70 // addNotifier adds a stack trace to every Cancel invocation 71 // 72 // Usage: 73 // 74 // threadGroup := g0.NewGoGroup(ctx) 75 // threadGroup.(*g0.GoGroup).addNotifier(func(slice pruntime.StackSlice) { 76 // parl.D("CANCEL %s %s\n\n\n\n\n", g0.GoChain(threadGroup), slice) 77 // }) 78 // func (c *goContext) addNotifier(notifier func(slice pruntime.StackSlice)) { 79 // for { 80 // var ctxp0 = c.ctxp.Load() 81 // var ctx = parl.AddNotifier1(*ctxp0, notifier) 82 // if c.ctxp.CompareAndSwap(ctxp0, &ctx) { 83 // return 84 // } 85 // } 86 // } 87 88 func (c *goContext) setCancelListener(f func()) { 89 c.cancelListener.Store(&f) 90 }