You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
4.1 KiB

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