mctx: add Root function, and update some docs
This commit is contained in:
parent
cb65787f37
commit
47061cec4e
72
mctx/ctx.go
72
mctx/ctx.go
@ -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{}{}
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user