123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- // Package gls implements goroutine-local storage.
- package gls
- import (
- "sync"
- )
- var (
- 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.Mutex
- 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
- }
- mutated_keys := make([]interface{}, 0, len(new_values))
- mutated_vals := make(Values, len(new_values))
- EnsureGoroutineId(func(gid uint) {
- m.mtx.Lock()
- state, found := m.values[gid]
- if !found {
- state = make(Values, len(new_values))
- m.values[gid] = state
- }
- m.mtx.Unlock()
- for key, new_val := range new_values {
- mutated_keys = append(mutated_keys, key)
- if old_val, ok := state[key]; ok {
- mutated_vals[key] = old_val
- }
- state[key] = new_val
- }
- defer func() {
- if !found {
- m.mtx.Lock()
- delete(m.values, gid)
- m.mtx.Unlock()
- return
- }
- for _, key := range mutated_keys {
- if val, ok := mutated_vals[key]; ok {
- state[key] = val
- } else {
- delete(state, key)
- }
- }
- }()
- 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) {
- gid, ok := GetGoroutineId()
- if !ok {
- return nil, false
- }
- m.mtx.Lock()
- state, found := m.values[gid]
- m.mtx.Unlock()
- if !found {
- return nil, false
- }
- value, ok = state[key]
- return value, ok
- }
- func (m *ContextManager) getValues() Values {
- gid, ok := GetGoroutineId()
- if !ok {
- return nil
- }
- m.mtx.Lock()
- state, _ := m.values[gid]
- m.mtx.Unlock()
- return state
- }
- // 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 {
- cb = func(mgr *ContextManager, cb func()) func() {
- return func() { mgr.SetValues(values, cb) }
- }(mgr, cb)
- }
- }
- go cb()
- }
|