context.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // Package gls implements goroutine-local storage.
  2. package gls
  3. import (
  4. "runtime"
  5. "sync"
  6. )
  7. const (
  8. maxCallers = 64
  9. )
  10. var (
  11. stackTagPool = &idPool{}
  12. mgrRegistry = make(map[*ContextManager]bool)
  13. mgrRegistryMtx sync.RWMutex
  14. )
  15. // Values is simply a map of key types to value types. Used by SetValues to
  16. // set multiple values at once.
  17. type Values map[interface{}]interface{}
  18. func currentStack(skip int) []uintptr {
  19. stack := make([]uintptr, maxCallers)
  20. return stack[:runtime.Callers(2+skip, stack)]
  21. }
  22. // ContextManager is the main entrypoint for interacting with
  23. // Goroutine-local-storage. You can have multiple independent ContextManagers
  24. // at any given time. ContextManagers are usually declared globally for a given
  25. // class of context variables. You should use NewContextManager for
  26. // construction.
  27. type ContextManager struct {
  28. mtx sync.RWMutex
  29. values map[uint]Values
  30. }
  31. // NewContextManager returns a brand new ContextManager. It also registers the
  32. // new ContextManager in the ContextManager registry which is used by the Go
  33. // method. ContextManagers are typically defined globally at package scope.
  34. func NewContextManager() *ContextManager {
  35. mgr := &ContextManager{values: make(map[uint]Values)}
  36. mgrRegistryMtx.Lock()
  37. defer mgrRegistryMtx.Unlock()
  38. mgrRegistry[mgr] = true
  39. return mgr
  40. }
  41. // Unregister removes a ContextManager from the global registry, used by the
  42. // Go method. Only intended for use when you're completely done with a
  43. // ContextManager. Use of Unregister at all is rare.
  44. func (m *ContextManager) Unregister() {
  45. mgrRegistryMtx.Lock()
  46. defer mgrRegistryMtx.Unlock()
  47. delete(mgrRegistry, m)
  48. }
  49. // SetValues takes a collection of values and a function to call for those
  50. // values to be set in. Anything further down the stack will have the set
  51. // values available through GetValue. SetValues will add new values or replace
  52. // existing values of the same key and will not mutate or change values for
  53. // previous stack frames.
  54. // SetValues is slow (makes a copy of all current and new values for the new
  55. // gls-context) in order to reduce the amount of lookups GetValue requires.
  56. func (m *ContextManager) SetValues(new_values Values, context_call func()) {
  57. if len(new_values) == 0 {
  58. context_call()
  59. return
  60. }
  61. tags := readStackTags(currentStack(1))
  62. m.mtx.Lock()
  63. values := new_values
  64. for _, tag := range tags {
  65. if existing_values, ok := m.values[tag]; ok {
  66. // oh, we found existing values, let's make a copy
  67. values = make(Values, len(existing_values)+len(new_values))
  68. for key, val := range existing_values {
  69. values[key] = val
  70. }
  71. for key, val := range new_values {
  72. values[key] = val
  73. }
  74. break
  75. }
  76. }
  77. new_tag := stackTagPool.Acquire()
  78. m.values[new_tag] = values
  79. m.mtx.Unlock()
  80. defer func() {
  81. m.mtx.Lock()
  82. delete(m.values, new_tag)
  83. m.mtx.Unlock()
  84. stackTagPool.Release(new_tag)
  85. }()
  86. addStackTag(new_tag, context_call)
  87. }
  88. // GetValue will return a previously set value, provided that the value was set
  89. // by SetValues somewhere higher up the stack. If the value is not found, ok
  90. // will be false.
  91. func (m *ContextManager) GetValue(key interface{}) (value interface{}, ok bool) {
  92. tags := readStackTags(currentStack(1))
  93. m.mtx.RLock()
  94. defer m.mtx.RUnlock()
  95. for _, tag := range tags {
  96. if values, ok := m.values[tag]; ok {
  97. value, ok := values[key]
  98. return value, ok
  99. }
  100. }
  101. return "", false
  102. }
  103. func (m *ContextManager) getValues() Values {
  104. tags := readStackTags(currentStack(2))
  105. m.mtx.RLock()
  106. defer m.mtx.RUnlock()
  107. for _, tag := range tags {
  108. if values, ok := m.values[tag]; ok {
  109. return values
  110. }
  111. }
  112. return nil
  113. }
  114. // Go preserves ContextManager values and Goroutine-local-storage across new
  115. // goroutine invocations. The Go method makes a copy of all existing values on
  116. // all registered context managers and makes sure they are still set after
  117. // kicking off the provided function in a new goroutine. If you don't use this
  118. // Go method instead of the standard 'go' keyword, you will lose values in
  119. // ContextManagers, as goroutines have brand new stacks.
  120. func Go(cb func()) {
  121. mgrRegistryMtx.RLock()
  122. defer mgrRegistryMtx.RUnlock()
  123. for mgr, _ := range mgrRegistry {
  124. values := mgr.getValues()
  125. if len(values) > 0 {
  126. mgr_copy := mgr
  127. cb_copy := cb
  128. cb = func() { mgr_copy.SetValues(values, cb_copy) }
  129. }
  130. }
  131. go cb()
  132. }