|
|
- // Package gls implements goroutine-local storage.
- package gls
-
- import (
- "sync"
- )
-
- const (
- maxCallers = 64
- )
-
- var (
- stackTagPool = &idPool{}
- mgrRegistry = make(map[*ContextManager]bool)
- mgrRegistryMtx sync.RWMutex
- )
-
- // Values is simply a map of key types to value types. Used by SetValues to
- // set multiple values at once.
- type Values map[interface{}]interface{}
-
- // ContextManager is the main entrypoint for interacting with
- // Goroutine-local-storage. You can have multiple independent ContextManagers
- // at any given time. ContextManagers are usually declared globally for a given
- // class of context variables. You should use NewContextManager for
- // construction.
- type ContextManager struct {
- mtx sync.RWMutex
- values map[uint]Values
- }
-
- // NewContextManager returns a brand new ContextManager. It also registers the
- // new ContextManager in the ContextManager registry which is used by the Go
- // method. ContextManagers are typically defined globally at package scope.
- func NewContextManager() *ContextManager {
- mgr := &ContextManager{values: make(map[uint]Values)}
- mgrRegistryMtx.Lock()
- defer mgrRegistryMtx.Unlock()
- mgrRegistry[mgr] = true
- return mgr
- }
-
- // Unregister removes a ContextManager from the global registry, used by the
- // Go method. Only intended for use when you're completely done with a
- // ContextManager. Use of Unregister at all is rare.
- func (m *ContextManager) Unregister() {
- mgrRegistryMtx.Lock()
- defer mgrRegistryMtx.Unlock()
- delete(mgrRegistry, m)
- }
-
- // SetValues takes a collection of values and a function to call for those
- // values to be set in. Anything further down the stack will have the set
- // values available through GetValue. SetValues will add new values or replace
- // existing values of the same key and will not mutate or change values for
- // previous stack frames.
- // SetValues is slow (makes a copy of all current and new values for the new
- // gls-context) in order to reduce the amount of lookups GetValue requires.
- func (m *ContextManager) SetValues(new_values Values, context_call func()) {
- if len(new_values) == 0 {
- context_call()
- return
- }
-
- tags := readStackTags(1)
-
- m.mtx.Lock()
- values := new_values
- for _, tag := range tags {
- if existing_values, ok := m.values[tag]; ok {
- // oh, we found existing values, let's make a copy
- values = make(Values, len(existing_values)+len(new_values))
- for key, val := range existing_values {
- values[key] = val
- }
- for key, val := range new_values {
- values[key] = val
- }
- break
- }
- }
- new_tag := stackTagPool.Acquire()
- m.values[new_tag] = values
- m.mtx.Unlock()
- defer func() {
- m.mtx.Lock()
- delete(m.values, new_tag)
- m.mtx.Unlock()
- stackTagPool.Release(new_tag)
- }()
-
- addStackTag(new_tag, context_call)
- }
-
- // GetValue will return a previously set value, provided that the value was set
- // by SetValues somewhere higher up the stack. If the value is not found, ok
- // will be false.
- func (m *ContextManager) GetValue(key interface{}) (value interface{}, ok bool) {
-
- tags := readStackTags(1)
- m.mtx.RLock()
- defer m.mtx.RUnlock()
- for _, tag := range tags {
- if values, ok := m.values[tag]; ok {
- value, ok := values[key]
- return value, ok
- }
- }
- return "", false
- }
-
- func (m *ContextManager) getValues() Values {
- tags := readStackTags(2)
- m.mtx.RLock()
- defer m.mtx.RUnlock()
- for _, tag := range tags {
- if values, ok := m.values[tag]; ok {
- return values
- }
- }
- return nil
- }
-
- // Go preserves ContextManager values and Goroutine-local-storage across new
- // goroutine invocations. The Go method makes a copy of all existing values on
- // all registered context managers and makes sure they are still set after
- // kicking off the provided function in a new goroutine. If you don't use this
- // Go method instead of the standard 'go' keyword, you will lose values in
- // ContextManagers, as goroutines have brand new stacks.
- func Go(cb func()) {
- mgrRegistryMtx.RLock()
- defer mgrRegistryMtx.RUnlock()
-
- for mgr, _ := range mgrRegistry {
- values := mgr.getValues()
- if len(values) > 0 {
- mgr_copy := mgr
- cb_copy := cb
- cb = func() { mgr_copy.SetValues(values, cb_copy) }
- }
- }
-
- go cb()
- }
|