gitea源码

datastore.go 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package reqctx
  4. import (
  5. "context"
  6. "io"
  7. "maps"
  8. "sync"
  9. "code.gitea.io/gitea/modules/process"
  10. )
  11. type ContextDataProvider interface {
  12. GetData() ContextData
  13. }
  14. type ContextData map[string]any
  15. func (ds ContextData) GetData() ContextData {
  16. return ds
  17. }
  18. func (ds ContextData) MergeFrom(other ContextData) ContextData {
  19. maps.Copy(ds, other)
  20. return ds
  21. }
  22. // RequestDataStore is a short-lived context-related object that is used to store request-specific data.
  23. type RequestDataStore interface {
  24. GetData() ContextData
  25. SetContextValue(k, v any)
  26. GetContextValue(key any) any
  27. AddCleanUp(f func())
  28. AddCloser(c io.Closer)
  29. }
  30. type requestDataStoreKeyType struct{}
  31. var RequestDataStoreKey requestDataStoreKeyType
  32. type requestDataStore struct {
  33. data ContextData
  34. mu sync.RWMutex
  35. values map[any]any
  36. cleanUpFuncs []func()
  37. }
  38. func (r *requestDataStore) GetContextValue(key any) any {
  39. if key == RequestDataStoreKey {
  40. return r
  41. }
  42. r.mu.RLock()
  43. defer r.mu.RUnlock()
  44. return r.values[key]
  45. }
  46. func (r *requestDataStore) SetContextValue(k, v any) {
  47. r.mu.Lock()
  48. r.values[k] = v
  49. r.mu.Unlock()
  50. }
  51. // GetData and the underlying ContextData are not thread-safe, callers should ensure thread-safety.
  52. func (r *requestDataStore) GetData() ContextData {
  53. if r.data == nil {
  54. r.data = make(ContextData)
  55. }
  56. return r.data
  57. }
  58. func (r *requestDataStore) AddCleanUp(f func()) {
  59. r.mu.Lock()
  60. r.cleanUpFuncs = append(r.cleanUpFuncs, f)
  61. r.mu.Unlock()
  62. }
  63. func (r *requestDataStore) AddCloser(c io.Closer) {
  64. r.AddCleanUp(func() { _ = c.Close() })
  65. }
  66. func (r *requestDataStore) cleanUp() {
  67. for _, f := range r.cleanUpFuncs {
  68. f()
  69. }
  70. }
  71. type RequestContext interface {
  72. context.Context
  73. RequestDataStore
  74. }
  75. func FromContext(ctx context.Context) RequestContext {
  76. if rc, ok := ctx.(RequestContext); ok {
  77. return rc
  78. }
  79. // here we must use the current ctx and the underlying store
  80. // the current ctx guarantees that the ctx deadline/cancellation/values are respected
  81. // the underlying store guarantees that the request-specific data is available
  82. if store := GetRequestDataStore(ctx); store != nil {
  83. return &requestContext{Context: ctx, RequestDataStore: store}
  84. }
  85. return nil
  86. }
  87. func GetRequestDataStore(ctx context.Context) RequestDataStore {
  88. if req, ok := ctx.Value(RequestDataStoreKey).(*requestDataStore); ok {
  89. return req
  90. }
  91. return nil
  92. }
  93. type requestContext struct {
  94. context.Context
  95. RequestDataStore
  96. }
  97. func (c *requestContext) Value(key any) any {
  98. if v := c.GetContextValue(key); v != nil {
  99. return v
  100. }
  101. return c.Context.Value(key)
  102. }
  103. func NewRequestContext(parentCtx context.Context, profDesc string) (_ context.Context, finished func()) {
  104. ctx, _, processFinished := process.GetManager().AddTypedContext(parentCtx, profDesc, process.RequestProcessType, true)
  105. store := &requestDataStore{values: make(map[any]any)}
  106. reqCtx := &requestContext{Context: ctx, RequestDataStore: store}
  107. return reqCtx, func() {
  108. store.cleanUp()
  109. processFinished()
  110. }
  111. }
  112. // NewRequestContextForTest creates a new RequestContext for testing purposes
  113. // It doesn't add the context to the process manager, nor do cleanup
  114. func NewRequestContextForTest(parentCtx context.Context) RequestContext {
  115. return &requestContext{Context: parentCtx, RequestDataStore: &requestDataStore{values: make(map[any]any)}}
  116. }