mctx: add Root function, and update some docs

This commit is contained in:
Brian Picciano 2018-11-30 15:08:39 -05:00
parent cb65787f37
commit 47061cec4e
2 changed files with 49 additions and 31 deletions

View File

@ -1,10 +1,12 @@
// Package mctx provides a framework, based around the Context type, for // Package mctx extends the builtin context package to organize Contexts into a
// managing configuration, initialization, runtime, and shutdown of a binary. // heirarchy. Each node in the hierarchy is given a name and is aware of all of
// The framework allows components to manage these aspects individually by means // its ancestors.
// of a heirarchical system.
// //
// Each node in the hierarchy is given a name and is aware of all of its // This package also provides extra functionality which allows contexts
// ancestors, and can incorporate this information into its functionality. // to be more useful when used in the heirarchy.
//
// All functions and methods in this package are thread-safe unless otherwise
// noted.
package mctx package mctx
import ( import (
@ -54,6 +56,7 @@ type ctxState struct {
parent Context parent Context
children map[string]Context children map[string]Context
mutL sync.RWMutex
mutVals map[interface{}]interface{} mutVals map[interface{}]interface{}
} }
@ -102,10 +105,27 @@ func Parent(ctx Context) Context {
return getCtxState(ctx).parent return getCtxState(ctx).parent
} }
// 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.
func Root(ctx Context) Context {
for {
s := getCtxState(ctx)
if s.parent == nil {
return ctx
}
ctx = s.parent
}
}
// ChildOf creates a child of the given context with the given name and returns // 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 // 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 // name appended to it. The Children function can be called on the parent to
// retrieve all children which have been made using this function. // retrieve all children which have been made using this function.
//
// TODO If the given Context already has a child with the given name that child
// will be returned.
func ChildOf(ctx Context, name string) Context { func ChildOf(ctx Context, name string) Context {
s, childS := getCtxState(ctx), new(ctxState) s, childS := getCtxState(ctx), new(ctxState)
@ -120,17 +140,6 @@ func ChildOf(ctx Context, name string) Context {
// set child's parent field // set child's parent field
childS.parent = ctx childS.parent = ctx
// set up child's logger
//pathStr := "/"
//if len(childS.path) > 0 {
// pathStr += path.Join(childS.path...)
//}
//childS.logger = s.logger.Clone()
//childS.logger.SetWriteFn(func(w io.Writer, msg mlog.Message) error {
// msg.Msg = "(" + pathStr + ") " + msg.Msg
// return mlog.DefaultWriteFn(w, msg)
//})
// create child's ctx and store it in parent // create child's ctx and store it in parent
childCtx := withCtxState(ctx, childS) childCtx := withCtxState(ctx, childS)
if s.children == nil { if s.children == nil {
@ -140,15 +149,12 @@ func ChildOf(ctx Context, name string) Context {
return childCtx return childCtx
} }
// TODO these might not be worth the effort, they're very subject to
// race-conditions
// MutableValue acts like the Value method, except that it only deals with // MutableValue acts like the Value method, except that it only deals with
// keys/values set by SetMutableValue. // keys/values set by SetMutableValue.
func MutableValue(ctx Context, key interface{}) interface{} { func MutableValue(ctx Context, key interface{}) interface{} {
s := getCtxState(ctx) s := getCtxState(ctx)
s.l.RLock() s.mutL.RLock()
defer s.l.RUnlock() defer s.mutL.RUnlock()
if s.mutVals == nil { if s.mutVals == nil {
return nil return nil
} }
@ -157,15 +163,19 @@ func MutableValue(ctx Context, key interface{}) interface{} {
// GetSetMutableValue is used to interact with a mutable value on the context in // 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. // a thread-safe way. The key's value is retrieved and passed to the callback.
// The value returned from the callback is stored to back into the context as // The value returned from the callback is stored back into the context as well
// well as being returned from this function. // as being returned from this function.
// //
// If noCallbackIfSet is set to true, then this function will only return the // If noCallbackIfSet is set to true, then if the key is already set the value
// value for the key if it's already set and not use the callback at all. // will be returned without calling the callback.
// //
// The callback returning nil is equivalent to unsetting the value. // The callback returning nil is equivalent to unsetting the value.
// //
// Children of this context will _not_ inherit any of its mutable values. // Children of this context will _not_ inherit any of its mutable values.
//
// Within the callback it is fine to call other functions/methods on the
// Context, except for those related to mutable values (e.g. MutableValue and
// SetMutableValue).
func GetSetMutableValue( func GetSetMutableValue(
ctx Context, noCallbackIfSet bool, ctx Context, noCallbackIfSet bool,
key interface{}, fn func(interface{}) interface{}, key interface{}, fn func(interface{}) interface{},
@ -173,16 +183,16 @@ func GetSetMutableValue(
s := getCtxState(ctx) s := getCtxState(ctx)
if noCallbackIfSet { if noCallbackIfSet {
s.l.RLock() s.mutL.RLock()
if s.mutVals != nil && s.mutVals[key] != nil { if s.mutVals != nil && s.mutVals[key] != nil {
defer s.l.RUnlock() defer s.mutL.RUnlock()
return s.mutVals[key] return s.mutVals[key]
} }
s.l.RUnlock() s.mutL.RUnlock()
} }
s.l.Lock() s.mutL.Lock()
defer s.l.Unlock() defer s.mutL.Unlock()
if s.mutVals == nil { if s.mutVals == nil {
s.mutVals = map[interface{}]interface{}{} s.mutVals = map[interface{}]interface{}{}

View File

@ -43,6 +43,14 @@ func TestInheritance(t *T) {
massert.Equal(Parent(ctx1b), ctx1), massert.Equal(Parent(ctx1b), ctx1),
massert.Equal(Parent(ctx2), ctx), massert.Equal(Parent(ctx2), ctx),
)) ))
massert.Fatal(t, massert.All(
massert.Equal(Root(ctx), ctx),
massert.Equal(Root(ctx1), ctx),
massert.Equal(Root(ctx1a), ctx),
massert.Equal(Root(ctx1b), ctx),
massert.Equal(Root(ctx2), ctx),
))
} }
func TestMutableValues(t *T) { func TestMutableValues(t *T) {