2018-11-30 20:08:39 +00:00
|
|
|
// Package mctx extends the builtin context package to organize Contexts into a
|
2018-12-06 04:40:46 +00:00
|
|
|
// hierarchy. Each node in the hierarchy is given a name and is aware of all of
|
2018-11-30 20:08:39 +00:00
|
|
|
// its ancestors.
|
2018-10-28 23:34:26 +00:00
|
|
|
//
|
2018-11-30 20:08:39 +00:00
|
|
|
// This package also provides extra functionality which allows contexts
|
2018-12-06 04:40:46 +00:00
|
|
|
// to be more useful when used in the hierarchy.
|
2018-11-30 20:08:39 +00:00
|
|
|
//
|
|
|
|
// All functions and methods in this package are thread-safe unless otherwise
|
|
|
|
// noted.
|
2018-10-28 23:34:26 +00:00
|
|
|
package mctx
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
"time"
|
2019-02-04 00:25:46 +00:00
|
|
|
|
|
|
|
goctx "context"
|
2018-10-28 23:34:26 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Context is the same as the builtin type, but is used to indicate that the
|
|
|
|
// Context originally came from this package (aka New or ChildOf).
|
2019-02-04 00:25:46 +00:00
|
|
|
type Context goctx.Context
|
2018-10-28 23:34:26 +00:00
|
|
|
|
|
|
|
// CancelFunc is a direct alias of the type from the context package, see its
|
|
|
|
// docs.
|
2019-02-04 00:25:46 +00:00
|
|
|
type CancelFunc = goctx.CancelFunc
|
2018-10-28 23:34:26 +00:00
|
|
|
|
|
|
|
// WithValue mimics the function from the context package.
|
|
|
|
func WithValue(parent Context, key, val interface{}) Context {
|
2019-02-04 00:25:46 +00:00
|
|
|
return Context(goctx.WithValue(goctx.Context(parent), key, val))
|
2018-10-28 23:34:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// WithCancel mimics the function from the context package.
|
|
|
|
func WithCancel(parent Context) (Context, CancelFunc) {
|
2019-02-04 00:25:46 +00:00
|
|
|
ctx, fn := goctx.WithCancel(goctx.Context(parent))
|
2018-10-28 23:34:26 +00:00
|
|
|
return Context(ctx), fn
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithDeadline mimics the function from the context package.
|
|
|
|
func WithDeadline(parent Context, t time.Time) (Context, CancelFunc) {
|
2019-02-04 00:25:46 +00:00
|
|
|
ctx, fn := goctx.WithDeadline(goctx.Context(parent), t)
|
2018-10-28 23:34:26 +00:00
|
|
|
return Context(ctx), fn
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithTimeout mimics the function from the context package.
|
|
|
|
func WithTimeout(parent Context, d time.Duration) (Context, CancelFunc) {
|
2019-02-04 00:25:46 +00:00
|
|
|
ctx, fn := goctx.WithTimeout(goctx.Context(parent), d)
|
2018-10-28 23:34:26 +00:00
|
|
|
return Context(ctx), fn
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-01-10 22:22:58 +00:00
|
|
|
type mutVal struct {
|
|
|
|
l sync.RWMutex
|
|
|
|
v interface{}
|
|
|
|
}
|
|
|
|
|
2019-02-04 00:25:46 +00:00
|
|
|
type context struct {
|
|
|
|
goctx.Context
|
|
|
|
|
2018-10-28 23:34:26 +00:00
|
|
|
path []string
|
|
|
|
l sync.RWMutex
|
2019-02-04 00:25:46 +00:00
|
|
|
parent *context
|
2018-10-28 23:34:26 +00:00
|
|
|
children map[string]Context
|
|
|
|
|
2018-11-30 20:08:39 +00:00
|
|
|
mutL sync.RWMutex
|
2019-01-10 22:22:58 +00:00
|
|
|
mutVals map[interface{}]*mutVal
|
2018-10-28 23:34:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New returns a new context which can be used as the root context for all
|
|
|
|
// purposes in this framework.
|
|
|
|
func New() Context {
|
2019-02-04 00:25:46 +00:00
|
|
|
return &context{Context: goctx.Background()}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getCtx(Ctx Context) *context {
|
|
|
|
ctx, ok := Ctx.(*context)
|
|
|
|
if !ok {
|
|
|
|
panic("non-conforming Context used")
|
|
|
|
}
|
|
|
|
return ctx
|
2018-10-28 23:34:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Path returns the sequence of names which were used to produce this context
|
|
|
|
// via the ChildOf function.
|
2019-02-04 00:25:46 +00:00
|
|
|
func Path(Ctx Context) []string {
|
|
|
|
return getCtx(Ctx).path
|
2018-10-28 23:34:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Children returns all children of this context which have been created by
|
|
|
|
// ChildOf, mapped by their name.
|
2019-02-04 00:25:46 +00:00
|
|
|
func Children(Ctx Context) map[string]Context {
|
|
|
|
ctx := getCtx(Ctx)
|
2018-10-28 23:34:26 +00:00
|
|
|
out := map[string]Context{}
|
2019-02-04 00:25:46 +00:00
|
|
|
ctx.l.RLock()
|
|
|
|
defer ctx.l.RUnlock()
|
|
|
|
for name, childCtx := range ctx.children {
|
2018-10-28 23:34:26 +00:00
|
|
|
out[name] = childCtx
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parent returns the parent Context of the given one, or nil if this is a root
|
|
|
|
// context (i.e. returned from New).
|
2019-02-04 00:25:46 +00:00
|
|
|
func Parent(Ctx Context) Context {
|
|
|
|
return getCtx(Ctx).parent
|
2018-10-28 23:34:26 +00:00
|
|
|
}
|
|
|
|
|
2018-11-30 20:08:39 +00:00
|
|
|
// Root returns the root Context from which this Context and all of its parents
|
|
|
|
// were derived (i.e. the Context which was originally returned from New).
|
|
|
|
//
|
|
|
|
// If the given Context is the root then it is returned as-id.
|
2019-02-04 00:25:46 +00:00
|
|
|
func Root(Ctx Context) Context {
|
|
|
|
ctx := getCtx(Ctx)
|
2018-11-30 20:08:39 +00:00
|
|
|
for {
|
2019-02-04 00:25:46 +00:00
|
|
|
if ctx.parent == nil {
|
2018-11-30 20:08:39 +00:00
|
|
|
return ctx
|
|
|
|
}
|
2019-02-04 00:25:46 +00:00
|
|
|
ctx = ctx.parent
|
2018-11-30 20:08:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-28 23:34:26 +00:00
|
|
|
// ChildOf creates a child of the given context with the given name and returns
|
|
|
|
// it. The Path of the returned context will be the path of the parent with its
|
|
|
|
// name appended to it. The Children function can be called on the parent to
|
|
|
|
// retrieve all children which have been made using this function.
|
2018-11-30 20:08:39 +00:00
|
|
|
//
|
|
|
|
// TODO If the given Context already has a child with the given name that child
|
|
|
|
// will be returned.
|
2019-02-04 00:25:46 +00:00
|
|
|
func ChildOf(Ctx Context, name string) Context {
|
|
|
|
ctx, childCtx := getCtx(Ctx), new(context)
|
2018-10-28 23:34:26 +00:00
|
|
|
|
2019-02-04 00:25:46 +00:00
|
|
|
ctx.l.Lock()
|
|
|
|
defer ctx.l.Unlock()
|
2018-10-28 23:34:26 +00:00
|
|
|
|
|
|
|
// set child's path field
|
2019-02-04 00:25:46 +00:00
|
|
|
childCtx.path = make([]string, 0, len(ctx.path)+1)
|
|
|
|
childCtx.path = append(childCtx.path, ctx.path...)
|
|
|
|
childCtx.path = append(childCtx.path, name)
|
2018-10-28 23:34:26 +00:00
|
|
|
|
|
|
|
// set child's parent field
|
2019-02-04 00:25:46 +00:00
|
|
|
childCtx.parent = ctx
|
2018-10-28 23:34:26 +00:00
|
|
|
|
|
|
|
// create child's ctx and store it in parent
|
2019-02-04 00:25:46 +00:00
|
|
|
if ctx.children == nil {
|
|
|
|
ctx.children = map[string]Context{}
|
2018-10-28 23:34:26 +00:00
|
|
|
}
|
2019-02-04 00:25:46 +00:00
|
|
|
ctx.children[name] = childCtx
|
2018-10-28 23:34:26 +00:00
|
|
|
return childCtx
|
|
|
|
}
|
|
|
|
|
2019-01-30 21:04:58 +00:00
|
|
|
// BreadthFirstVisit visits this Context and all of its children, and their
|
|
|
|
// children, in a breadth-first order. If the callback returns false then the
|
|
|
|
// function returns without visiting any more Contexts.
|
|
|
|
//
|
|
|
|
// The exact order of visitation is non-deterministic.
|
2019-02-04 00:25:46 +00:00
|
|
|
func BreadthFirstVisit(Ctx Context, callback func(Context) bool) {
|
|
|
|
queue := []Context{Ctx}
|
2019-01-30 21:04:58 +00:00
|
|
|
for len(queue) > 0 {
|
|
|
|
if !callback(queue[0]) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, child := range Children(queue[0]) {
|
|
|
|
queue = append(queue, child)
|
|
|
|
}
|
|
|
|
queue = queue[1:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-10 22:22:58 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// code related to mutable values
|
|
|
|
|
2018-10-28 23:34:26 +00:00
|
|
|
// MutableValue acts like the Value method, except that it only deals with
|
|
|
|
// keys/values set by SetMutableValue.
|
2019-02-04 00:25:46 +00:00
|
|
|
func MutableValue(Ctx Context, key interface{}) interface{} {
|
|
|
|
ctx := getCtx(Ctx)
|
|
|
|
ctx.mutL.RLock()
|
|
|
|
defer ctx.mutL.RUnlock()
|
|
|
|
if ctx.mutVals == nil {
|
2018-10-28 23:34:26 +00:00
|
|
|
return nil
|
2019-02-04 00:25:46 +00:00
|
|
|
} else if mVal, ok := ctx.mutVals[key]; ok {
|
2019-01-10 22:22:58 +00:00
|
|
|
mVal.l.RLock()
|
|
|
|
defer mVal.l.RUnlock()
|
|
|
|
return mVal.v
|
2018-10-28 23:34:26 +00:00
|
|
|
}
|
2019-01-10 22:22:58 +00:00
|
|
|
return nil
|
2018-10-28 23:34:26 +00:00
|
|
|
}
|
2018-10-29 02:17:33 +00:00
|
|
|
|
|
|
|
// GetSetMutableValue is used to interact with a mutable value on the context in
|
|
|
|
// a thread-safe way. The key's value is retrieved and passed to the callback.
|
2018-11-30 20:08:39 +00:00
|
|
|
// The value returned from the callback is stored back into the context as well
|
|
|
|
// as being returned from this function.
|
2018-10-29 02:17:33 +00:00
|
|
|
//
|
2018-11-30 20:08:39 +00:00
|
|
|
// If noCallbackIfSet is set to true, then if the key is already set the value
|
|
|
|
// will be returned without calling the callback.
|
2018-10-29 02:17:33 +00:00
|
|
|
//
|
|
|
|
// The callback returning nil is equivalent to unsetting the value.
|
|
|
|
//
|
|
|
|
// Children of this context will _not_ inherit any of its mutable values.
|
2018-11-30 20:08:39 +00:00
|
|
|
//
|
|
|
|
// Within the callback it is fine to call other functions/methods on the
|
2019-01-10 22:22:58 +00:00
|
|
|
// Context, except for those related to mutable values for this same key (e.g.
|
|
|
|
// MutableValue and SetMutableValue).
|
2018-10-29 02:17:33 +00:00
|
|
|
func GetSetMutableValue(
|
2019-02-04 00:25:46 +00:00
|
|
|
Ctx Context, noCallbackIfSet bool,
|
2018-10-29 02:17:33 +00:00
|
|
|
key interface{}, fn func(interface{}) interface{},
|
|
|
|
) interface{} {
|
|
|
|
|
2019-01-10 22:22:58 +00:00
|
|
|
// if noCallbackIfSet, do a fast lookup with MutableValue first.
|
2018-10-29 02:17:33 +00:00
|
|
|
if noCallbackIfSet {
|
2019-02-04 00:25:46 +00:00
|
|
|
if v := MutableValue(Ctx, key); v != nil {
|
2019-01-10 22:22:58 +00:00
|
|
|
return v
|
2018-10-29 02:17:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-04 00:25:46 +00:00
|
|
|
ctx := getCtx(Ctx)
|
|
|
|
ctx.mutL.Lock()
|
|
|
|
if ctx.mutVals == nil {
|
|
|
|
ctx.mutVals = map[interface{}]*mutVal{}
|
2018-10-29 02:17:33 +00:00
|
|
|
}
|
2019-02-04 00:25:46 +00:00
|
|
|
mVal, ok := ctx.mutVals[key]
|
2019-01-10 22:22:58 +00:00
|
|
|
if !ok {
|
|
|
|
mVal = new(mutVal)
|
2019-02-04 00:25:46 +00:00
|
|
|
ctx.mutVals[key] = mVal
|
2019-01-10 22:22:58 +00:00
|
|
|
}
|
2019-02-04 00:25:46 +00:00
|
|
|
ctx.mutL.Unlock()
|
2019-01-10 22:22:58 +00:00
|
|
|
|
|
|
|
mVal.l.Lock()
|
|
|
|
defer mVal.l.Unlock()
|
2018-10-29 02:17:33 +00:00
|
|
|
|
|
|
|
// It's possible something happened between the first check inside the
|
|
|
|
// read-lock and now, so double check this case. It's still good to have the
|
|
|
|
// read-lock check there, it'll handle 99% of the cases.
|
2019-01-10 22:22:58 +00:00
|
|
|
if noCallbackIfSet && mVal.v != nil {
|
|
|
|
return mVal.v
|
2018-10-29 02:17:33 +00:00
|
|
|
}
|
|
|
|
|
2019-01-10 22:22:58 +00:00
|
|
|
mVal.v = fn(mVal.v)
|
|
|
|
|
|
|
|
// TODO if the new v is nil then key could be deleted out of mutVals. But
|
|
|
|
// doing so would be weird in the case that there's another routine which
|
|
|
|
// has already pulled this same mVal out of mutVals and is waiting on its
|
|
|
|
// mutex.
|
|
|
|
return mVal.v
|
2018-10-29 02:17:33 +00:00
|
|
|
}
|