move options to dedicated package
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
63
util/ctx/ctx.go
Normal file
63
util/ctx/ctx.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package ctx
|
||||
|
||||
import "context"
|
||||
|
||||
// Mixin mixes a typically shorter-lived (service, etc.) context passed in
|
||||
// “shortctx” into a long-living context “longctx”, returning a derived “mixed”
|
||||
// context. The “mixed” context is derived from the long-living context and
|
||||
// additionally gets the deadline (if any) and cancellation of the mixed-in
|
||||
// “shortctx”.
|
||||
//
|
||||
// Please note that it is essential to always call the additionally returned
|
||||
// cancel function in order to not leak go routines. Calling this cancel
|
||||
// function won't cancel the long-living and short-lived contexts, just clean
|
||||
// up. This follows the established context pattern of [context.WithCancel],
|
||||
// [context.WithDeadline] and [context.WithTimeout].
|
||||
func Mixin(longctx, shortctx context.Context) (mixedctx context.Context, mixedcancel context.CancelFunc) {
|
||||
if longctx == nil {
|
||||
panic("wye.Mixin: cannot mix into nil context")
|
||||
}
|
||||
if shortctx == nil {
|
||||
panic("wye.Mixin: cannot mix-in nil context")
|
||||
}
|
||||
mixedctx = longctx
|
||||
shortDone := shortctx.Done()
|
||||
if shortDone == nil {
|
||||
// In case the shorter-living context isn't cancellable at all, then we
|
||||
// cannot cancel it and the cancel function returned must be a "no
|
||||
// operation". There's no need to mix in something, so we can pass on
|
||||
// the long-lived context.
|
||||
mixedcancel = func() {}
|
||||
return
|
||||
}
|
||||
// Nota bene: cancelled contexts "trickle down", so if a context higher up
|
||||
// the hierarchy was cancelled, this will automatically propagate down to
|
||||
// all child contexts, and so on.
|
||||
//
|
||||
// In case the shorter-lived context has a deadline, we need to carry it
|
||||
// over into the final mixed context.
|
||||
if deadline, ok := shortctx.Deadline(); ok {
|
||||
mixedctx, mixedcancel = context.WithDeadline(mixedctx, deadline)
|
||||
}
|
||||
// As the shorter-living context can be cancelled, we will need to supervise
|
||||
// it so we notice when it gets cancelled and then cancel the mixed context.
|
||||
mixedctx, mixedcancel = context.WithCancel(mixedctx)
|
||||
go func(ctx context.Context, cancel context.CancelFunc) {
|
||||
select {
|
||||
case <-shortDone:
|
||||
// In case the shorter-living context was cancelled (but it did
|
||||
// not pass a deadline) then we need to propagate this mixed
|
||||
// context. Please note this correctly won't cancel the original
|
||||
// longer context, because that's a long-living context we
|
||||
// shouldn't interfere with.
|
||||
if shortctx.Err() == context.Canceled {
|
||||
cancel()
|
||||
}
|
||||
case <-ctx.Done():
|
||||
// The final mixed context was either cancelled itself or its parent
|
||||
// deadline context met its fate; here, do not touch short-lived
|
||||
// context.
|
||||
}
|
||||
}(mixedctx, mixedcancel)
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user