From b1247d2d0d51108c910a73891ff3116e5f032ab1 Mon Sep 17 00:00:00 2001 From: "Kali Kaneko (leap communications)" Date: Sat, 12 Jan 2019 18:39:45 +0100 Subject: [pkg] all your deps are vendored to us --- vendor/github.com/getlantern/context/context.go | 309 ++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 vendor/github.com/getlantern/context/context.go (limited to 'vendor/github.com/getlantern/context/context.go') diff --git a/vendor/github.com/getlantern/context/context.go b/vendor/github.com/getlantern/context/context.go new file mode 100644 index 0000000..c77f129 --- /dev/null +++ b/vendor/github.com/getlantern/context/context.go @@ -0,0 +1,309 @@ +// Package context provides a mechanism for transparently tracking contextual +// state associated to the current goroutine and even across goroutines. +package context + +import ( + "sync" +) + +// Manager provides the ability to create and access Contexts. +type Manager interface { + // Enter enters a new level on the current Context stack, creating a new Context + // if necessary. + Enter() Context + + // Go starts the given function on a new goroutine but sharing the context of + // the current goroutine (if it has one). + Go(func()) + + // PutGlobal puts the given key->value pair into the global context. + PutGlobal(key string, value interface{}) + + // PutGlobalDynamic puts a key->value pair into the global context where the + // value is generated by a function that gets evaluated at every Read. If the + // value is a map[string]interface{}, we will unpack the map and set each + // contained key->value pair independently. + PutGlobalDynamic(key string, valueFN func() interface{}) + + // AsMap returns a map containing all values from the supplied obj if it is a + // Contextual, plus any addition values from along the stack, plus globals if so + // specified. + AsMap(obj interface{}, includeGlobals bool) Map +} + +type manager struct { + contexts map[uint64]*context + mxContexts sync.RWMutex + global Map + mxGlobal sync.RWMutex +} + +// NewManager creates a new Manager +func NewManager() Manager { + return &manager{ + contexts: make(map[uint64]*context), + global: make(Map), + } +} + +// Contextual is an interface for anything that maintains its own context. +type Contextual interface { + // Fill fills the given Map with all of this Contextual's context + Fill(m Map) +} + +// Map is a map of key->value pairs. +type Map map[string]interface{} + +// Fill implements the method from the Contextual interface. +func (_m Map) Fill(m Map) { + for key, value := range _m { + m[key] = value + } +} + +// Context is a context containing key->value pairs +type Context interface { + // Enter enters a new level on this Context stack. + Enter() Context + + // Go starts the given function on a new goroutine. + Go(fn func()) + + // Exit exits the current level on this Context stack. + Exit() + + // Put puts a key->value pair into the current level of the context stack. + Put(key string, value interface{}) Context + + // PutIfAbsent puts the given key->value pair into the current level of the + // context stack if and only if that key is defined nowhere within the context + // stack (including parent contexts). + PutIfAbsent(key string, value interface{}) Context + + // PutDynamic puts a key->value pair into the current level of the context + // stack where the value is generated by a function that gets evaluated at + // every Read. If the value is a map[string]interface{}, we will unpack the + // map and set each contained key->value pair independently. + PutDynamic(key string, valueFN func() interface{}) Context + + // Fill fills the given map with data from this Context + Fill(m Map) + + // AsMap returns a map containing all values from the supplied obj if it is a + // Contextual, plus any addition values from along the stack, plus globals if + // so specified. + AsMap(obj interface{}, includeGlobals bool) Map +} + +type context struct { + cm *manager + id uint64 + parent *context + branchedFrom *context + data Map + mx sync.RWMutex +} + +type dynval struct { + fn func() interface{} +} + +func (cm *manager) Enter() Context { + return cm.enter(curGoroutineID()) +} + +func (cm *manager) enter(id uint64) *context { + cm.mxContexts.Lock() + parentOrNil := cm.contexts[id] + c := cm.makeContext(id, parentOrNil, nil) + cm.contexts[id] = c + cm.mxContexts.Unlock() + return c +} + +func (cm *manager) exit(id uint64, parent *context) { + cm.mxContexts.Lock() + if parent == nil { + delete(cm.contexts, id) + } else { + cm.contexts[id] = parent + } + cm.mxContexts.Unlock() +} + +func (cm *manager) branch(id uint64, from *context) { + next := cm.makeContext(id, nil, from) + cm.mxContexts.Lock() + cm.contexts[id] = next + cm.mxContexts.Unlock() +} + +func (cm *manager) merge(id uint64) { + cm.mxContexts.Lock() + delete(cm.contexts, id) + cm.mxContexts.Unlock() +} + +func (c *context) Enter() Context { + c.mx.RLock() + id := c.id + c.mx.RUnlock() + return c.cm.enter(id) +} + +func (c *context) Go(fn func()) { + go func() { + id := curGoroutineID() + c.cm.branch(id, c) + fn() + c.cm.merge(id) + }() +} + +func (cm *manager) Go(fn func()) { + c := cm.currentContext() + if c != nil { + c.Go(fn) + } else { + go fn() + } +} + +func (cm *manager) makeContext(id uint64, parent *context, branchedFrom *context) *context { + return &context{ + cm: cm, + id: id, + parent: parent, + branchedFrom: branchedFrom, + data: make(Map), + } +} + +func (c *context) Exit() { + c.mx.RLock() + id := c.id + parent := c.parent + c.mx.RUnlock() + c.cm.exit(id, parent) +} + +func (c *context) Put(key string, value interface{}) Context { + c.mx.Lock() + c.data[key] = value + c.mx.Unlock() + return c +} + +func (c *context) PutIfAbsent(key string, value interface{}) Context { + for ctx := c; ctx != nil; { + ctx.mx.RLock() + _, exists := ctx.data[key] + next := ctx.parent + if next == nil { + next = ctx.branchedFrom + } + ctx.mx.RUnlock() + if exists { + return c + } + ctx = next + } + + // Value not set, set it + return c.Put(key, value) +} + +func (c *context) PutDynamic(key string, valueFN func() interface{}) Context { + value := &dynval{valueFN} + c.mx.Lock() + c.data[key] = value + c.mx.Unlock() + return c +} + +func (cm *manager) PutGlobal(key string, value interface{}) { + cm.mxGlobal.Lock() + cm.global[key] = value + cm.mxGlobal.Unlock() +} + +func (cm *manager) PutGlobalDynamic(key string, valueFN func() interface{}) { + value := &dynval{valueFN} + cm.mxGlobal.Lock() + cm.global[key] = value + cm.mxGlobal.Unlock() +} + +func (c *context) Fill(m Map) { + for ctx := c; ctx != nil; { + ctx.mx.RLock() + fill(m, ctx.data) + next := ctx.parent + if next == nil { + next = ctx.branchedFrom + } + ctx.mx.RUnlock() + ctx = next + } +} + +func (cm *manager) AsMap(obj interface{}, includeGlobals bool) Map { + return cm.currentContext().asMap(cm, obj, includeGlobals) +} + +func (c *context) AsMap(obj interface{}, includeGlobals bool) Map { + return c.asMap(c.cm, obj, includeGlobals) +} + +func (c *context) asMap(cm *manager, obj interface{}, includeGlobals bool) Map { + result := make(Map, 0) + cl, ok := obj.(Contextual) + if ok { + cl.Fill(result) + } + if c != nil { + c.Fill(result) + } + if includeGlobals { + cm.mxGlobal.RLock() + fill(result, cm.global) + cm.mxGlobal.RUnlock() + } + return result +} + +func fill(m Map, from Map) { + if m != nil { + doFill := func(key string, _value interface{}) { + switch value := _value.(type) { + case map[string]interface{}: + for k, v := range value { + m[k] = v + } + default: + m[key] = value + } + } + + for key, value := range from { + _, alreadyRead := m[key] + if !alreadyRead { + switch v := value.(type) { + case *dynval: + doFill(key, v.fn()) + default: + doFill(key, v) + } + } + } + } +} + +func (cm *manager) currentContext() *context { + id := curGoroutineID() + cm.mxContexts.RLock() + c := cm.contexts[id] + cm.mxContexts.RUnlock() + return c +} -- cgit v1.2.3