mctx: GetSetMutableValue. Not sure if I'm really going to use this or not

This commit is contained in:
Brian Picciano 2018-10-28 22:17:33 -04:00
parent e349d0fbd8
commit cb65787f37
2 changed files with 76 additions and 34 deletions

View File

@ -131,14 +131,6 @@ func ChildOf(ctx Context, name string) Context {
// return mlog.DefaultWriteFn(w, msg) // return mlog.DefaultWriteFn(w, msg)
//}) //})
// copy mutable values
if len(s.mutVals) > 0 {
childS.mutVals = make(map[interface{}]interface{}, len(s.mutVals))
for key, val := range s.mutVals {
childS.mutVals[key] = val
}
}
// 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 {
@ -151,19 +143,6 @@ func ChildOf(ctx Context, name string) Context {
// TODO these might not be worth the effort, they're very subject to // TODO these might not be worth the effort, they're very subject to
// race-conditions // race-conditions
// SetMutableValue is like WithMutable, except rather than leaving the original
// ctx unaffected it modifies the value in that context. Children of this
// context will inherit an independent copy of its immutable values.
func SetMutableValue(ctx Context, key, value interface{}) {
s := getCtxState(ctx)
s.l.Lock()
defer s.l.Unlock()
if s.mutVals == nil {
s.mutVals = map[interface{}]interface{}{}
}
s.mutVals[key] = value
}
// 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{} {
@ -175,3 +154,53 @@ func MutableValue(ctx Context, key interface{}) interface{} {
} }
return s.mutVals[key] return s.mutVals[key]
} }
// 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.
// The value returned from the callback is stored to back into the context as
// well as being returned from this function.
//
// If noCallbackIfSet is set to true, then this function will only return the
// value for the key if it's already set and not use the callback at all.
//
// The callback returning nil is equivalent to unsetting the value.
//
// Children of this context will _not_ inherit any of its mutable values.
func GetSetMutableValue(
ctx Context, noCallbackIfSet bool,
key interface{}, fn func(interface{}) interface{},
) interface{} {
s := getCtxState(ctx)
if noCallbackIfSet {
s.l.RLock()
if s.mutVals != nil && s.mutVals[key] != nil {
defer s.l.RUnlock()
return s.mutVals[key]
}
s.l.RUnlock()
}
s.l.Lock()
defer s.l.Unlock()
if s.mutVals == nil {
s.mutVals = map[interface{}]interface{}{}
}
val := s.mutVals[key]
// 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.
if noCallbackIfSet && val != nil {
return val
}
val = fn(val)
if val == nil {
delete(s.mutVals, key)
} else {
s.mutVals[key] = val
}
return val
}

View File

@ -6,15 +6,11 @@ import (
"github.com/mediocregopher/mediocre-go-lib/mtest/massert" "github.com/mediocregopher/mediocre-go-lib/mtest/massert"
) )
func TestContext(t *T) { func TestInheritance(t *T) {
ctx := New() ctx := New()
SetMutableValue(ctx, "one", 1)
ctx1 := ChildOf(ctx, "1") ctx1 := ChildOf(ctx, "1")
ctx1a := ChildOf(ctx1, "a") ctx1a := ChildOf(ctx1, "a")
SetMutableValue(ctx1, "one", 2)
ctx1b := ChildOf(ctx1, "b") ctx1b := ChildOf(ctx1, "b")
SetMutableValue(ctx1b, "one", 3)
ctx2 := ChildOf(ctx, "2") ctx2 := ChildOf(ctx, "2")
massert.Fatal(t, massert.All( massert.Fatal(t, massert.All(
@ -47,12 +43,29 @@ func TestContext(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(MutableValue(ctx, "one"), 1), func TestMutableValues(t *T) {
massert.Equal(MutableValue(ctx1, "one"), 2), fn := func(v interface{}) interface{} {
massert.Equal(MutableValue(ctx1a, "one"), 1), if v == nil {
massert.Equal(MutableValue(ctx1b, "one"), 3), return 0
massert.Equal(MutableValue(ctx2, "one"), 1), }
)) return v.(int) + 1
}
var aa []massert.Assertion
ctx := New()
aa = append(aa, massert.Equal(GetSetMutableValue(ctx, false, 0, fn), 0))
aa = append(aa, massert.Equal(GetSetMutableValue(ctx, false, 0, fn), 1))
aa = append(aa, massert.Equal(GetSetMutableValue(ctx, true, 0, fn), 1))
aa = append(aa, massert.Equal(MutableValue(ctx, 0), 1))
ctx1 := ChildOf(ctx, "one")
aa = append(aa, massert.Equal(GetSetMutableValue(ctx1, true, 0, fn), 0))
aa = append(aa, massert.Equal(GetSetMutableValue(ctx1, false, 0, fn), 1))
aa = append(aa, massert.Equal(GetSetMutableValue(ctx1, true, 0, fn), 1))
massert.Fatal(t, massert.All(aa...))
} }