runtime_context.h 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. #pragma once
  4. #include <stddef.h>
  5. #include <algorithm>
  6. #include "opentelemetry/common/macros.h"
  7. #include "opentelemetry/context/context.h"
  8. #include "opentelemetry/context/context_value.h"
  9. #include "opentelemetry/nostd/shared_ptr.h"
  10. #include "opentelemetry/nostd/string_view.h"
  11. #include "opentelemetry/nostd/unique_ptr.h"
  12. #include "opentelemetry/version.h"
  13. OPENTELEMETRY_BEGIN_NAMESPACE
  14. namespace context
  15. {
  16. // The Token object provides is returned when attaching objects to the
  17. // RuntimeContext object and is associated with a context object, and
  18. // can be provided to the RuntimeContext Detach method to remove the
  19. // associated context from the RuntimeContext.
  20. class Token
  21. {
  22. public:
  23. bool operator==(const Context &other) const noexcept { return context_ == other; }
  24. ~Token() noexcept;
  25. private:
  26. friend class RuntimeContextStorage;
  27. // A constructor that sets the token's Context object to the
  28. // one that was passed in.
  29. Token(const Context &context) : context_(context) {}
  30. const Context context_;
  31. };
  32. /**
  33. * RuntimeContextStorage is used by RuntimeContext to store Context frames.
  34. *
  35. * Custom context management strategies can be implemented by deriving from
  36. * this class and passing an initialized RuntimeContextStorage object to
  37. * RuntimeContext::SetRuntimeContextStorage.
  38. */
  39. class OPENTELEMETRY_EXPORT RuntimeContextStorage
  40. {
  41. public:
  42. /**
  43. * Return the current context.
  44. * @return the current context
  45. */
  46. virtual Context GetCurrent() noexcept = 0;
  47. /**
  48. * Set the current context.
  49. * @param the new current context
  50. * @return a token for the new current context. This never returns a nullptr.
  51. */
  52. virtual nostd::unique_ptr<Token> Attach(const Context &context) noexcept = 0;
  53. /**
  54. * Detach the context related to the given token.
  55. * @param token a token related to a context
  56. * @return true if the context could be detached
  57. */
  58. virtual bool Detach(Token &token) noexcept = 0;
  59. virtual ~RuntimeContextStorage() {}
  60. protected:
  61. nostd::unique_ptr<Token> CreateToken(const Context &context) noexcept
  62. {
  63. return nostd::unique_ptr<Token>(new Token(context));
  64. }
  65. };
  66. /**
  67. * Construct and return the default RuntimeContextStorage
  68. * @return a ThreadLocalContextStorage
  69. */
  70. static RuntimeContextStorage *GetDefaultStorage() noexcept;
  71. // Provides a wrapper for propagating the context object globally.
  72. //
  73. // By default, a thread-local runtime context storage is used.
  74. class OPENTELEMETRY_EXPORT RuntimeContext
  75. {
  76. public:
  77. // Return the current context.
  78. static Context GetCurrent() noexcept { return GetRuntimeContextStorage()->GetCurrent(); }
  79. // Sets the current 'Context' object. Returns a token
  80. // that can be used to reset to the previous Context.
  81. static nostd::unique_ptr<Token> Attach(const Context &context) noexcept
  82. {
  83. return GetRuntimeContextStorage()->Attach(context);
  84. }
  85. // Resets the context to a previous value stored in the
  86. // passed in token. Returns true if successful, false otherwise
  87. static bool Detach(Token &token) noexcept { return GetRuntimeContextStorage()->Detach(token); }
  88. // Sets the Key and Value into the passed in context or if a context is not
  89. // passed in, the RuntimeContext.
  90. // Should be used to SetValues to the current RuntimeContext, is essentially
  91. // equivalent to RuntimeContext::GetCurrent().SetValue(key,value). Keep in
  92. // mind that the current RuntimeContext will not be changed, and the new
  93. // context will be returned.
  94. static Context SetValue(nostd::string_view key,
  95. const ContextValue &value,
  96. Context *context = nullptr) noexcept
  97. {
  98. Context temp_context;
  99. if (context == nullptr)
  100. {
  101. temp_context = GetCurrent();
  102. }
  103. else
  104. {
  105. temp_context = *context;
  106. }
  107. return temp_context.SetValue(key, value);
  108. }
  109. // Returns the value associated with the passed in key and either the
  110. // passed in context* or the runtime context if a context is not passed in.
  111. // Should be used to get values from the current RuntimeContext, is
  112. // essentially equivalent to RuntimeContext::GetCurrent().GetValue(key).
  113. static ContextValue GetValue(nostd::string_view key, Context *context = nullptr) noexcept
  114. {
  115. Context temp_context;
  116. if (context == nullptr)
  117. {
  118. temp_context = GetCurrent();
  119. }
  120. else
  121. {
  122. temp_context = *context;
  123. }
  124. return temp_context.GetValue(key);
  125. }
  126. /**
  127. * Provide a custom runtime context storage.
  128. *
  129. * This provides a possibility to override the default thread-local runtime
  130. * context storage. This has to be set before any spans are created by the
  131. * application, otherwise the behavior is undefined.
  132. *
  133. * @param storage a custom runtime context storage
  134. */
  135. static void SetRuntimeContextStorage(
  136. const nostd::shared_ptr<RuntimeContextStorage> &storage) noexcept
  137. {
  138. GetStorage() = storage;
  139. }
  140. /**
  141. * Provide a pointer to const runtime context storage.
  142. *
  143. * The returned pointer can only be used for extending the lifetime of the runtime context
  144. * storage.
  145. *
  146. */
  147. static nostd::shared_ptr<const RuntimeContextStorage> GetConstRuntimeContextStorage() noexcept
  148. {
  149. return GetRuntimeContextStorage();
  150. }
  151. private:
  152. static nostd::shared_ptr<RuntimeContextStorage> GetRuntimeContextStorage() noexcept
  153. {
  154. return GetStorage();
  155. }
  156. OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr<RuntimeContextStorage> &GetStorage() noexcept
  157. {
  158. static nostd::shared_ptr<RuntimeContextStorage> context(GetDefaultStorage());
  159. return context;
  160. }
  161. };
  162. inline Token::~Token() noexcept
  163. {
  164. context::RuntimeContext::Detach(*this);
  165. }
  166. // The ThreadLocalContextStorage class is a derived class from
  167. // RuntimeContextStorage and provides a wrapper for propagating context through
  168. // cpp thread locally. This file must be included to use the RuntimeContext
  169. // class if another implementation has not been registered.
  170. class ThreadLocalContextStorage : public RuntimeContextStorage
  171. {
  172. public:
  173. ThreadLocalContextStorage() noexcept = default;
  174. // Return the current context.
  175. Context GetCurrent() noexcept override { return GetStack().Top(); }
  176. // Resets the context to the value previous to the passed in token. This will
  177. // also detach all child contexts of the passed in token.
  178. // Returns true if successful, false otherwise.
  179. bool Detach(Token &token) noexcept override
  180. {
  181. // In most cases, the context to be detached is on the top of the stack.
  182. if (token == GetStack().Top())
  183. {
  184. GetStack().Pop();
  185. return true;
  186. }
  187. if (!GetStack().Contains(token))
  188. {
  189. return false;
  190. }
  191. while (!(token == GetStack().Top()))
  192. {
  193. GetStack().Pop();
  194. }
  195. GetStack().Pop();
  196. return true;
  197. }
  198. // Sets the current 'Context' object. Returns a token
  199. // that can be used to reset to the previous Context.
  200. nostd::unique_ptr<Token> Attach(const Context &context) noexcept override
  201. {
  202. GetStack().Push(context);
  203. return CreateToken(context);
  204. }
  205. private:
  206. // A nested class to store the attached contexts in a stack.
  207. class Stack
  208. {
  209. friend class ThreadLocalContextStorage;
  210. Stack() noexcept : size_(0), capacity_(0), base_(nullptr) {}
  211. // Pops the top Context off the stack.
  212. void Pop() noexcept
  213. {
  214. if (size_ == 0)
  215. {
  216. return;
  217. }
  218. // Store empty Context before decrementing `size`, to ensure
  219. // the shared_ptr object (if stored in prev context object ) are released.
  220. // The stack is not resized, and the unused memory would be reutilised
  221. // for subsequent context storage.
  222. base_[size_ - 1] = Context();
  223. size_ -= 1;
  224. }
  225. bool Contains(const Token &token) const noexcept
  226. {
  227. for (size_t pos = size_; pos > 0; --pos)
  228. {
  229. if (token == base_[pos - 1])
  230. {
  231. return true;
  232. }
  233. }
  234. return false;
  235. }
  236. // Returns the Context at the top of the stack.
  237. Context Top() const noexcept
  238. {
  239. if (size_ == 0)
  240. {
  241. return Context();
  242. }
  243. return base_[size_ - 1];
  244. }
  245. // Pushes the passed in context pointer to the top of the stack
  246. // and resizes if necessary.
  247. void Push(const Context &context) noexcept
  248. {
  249. size_++;
  250. if (size_ > capacity_)
  251. {
  252. Resize(size_ * 2);
  253. }
  254. base_[size_ - 1] = context;
  255. }
  256. // Reallocates the storage array to the pass in new capacity size.
  257. void Resize(size_t new_capacity) noexcept
  258. {
  259. size_t old_size = size_ - 1;
  260. if (new_capacity == 0)
  261. {
  262. new_capacity = 2;
  263. }
  264. Context *temp = new Context[new_capacity];
  265. if (base_ != nullptr)
  266. {
  267. // vs2015 does not like this construct considering it unsafe:
  268. // - std::copy(base_, base_ + old_size, temp);
  269. // Ref.
  270. // https://stackoverflow.com/questions/12270224/xutility2227-warning-c4996-std-copy-impl
  271. for (size_t i = 0; i < (std::min)(old_size, new_capacity); i++)
  272. {
  273. temp[i] = base_[i];
  274. }
  275. delete[] base_;
  276. }
  277. base_ = temp;
  278. capacity_ = new_capacity;
  279. }
  280. ~Stack() noexcept { delete[] base_; }
  281. size_t size_;
  282. size_t capacity_;
  283. Context *base_;
  284. };
  285. OPENTELEMETRY_API_SINGLETON Stack &GetStack()
  286. {
  287. static thread_local Stack stack_ = Stack();
  288. return stack_;
  289. }
  290. };
  291. static RuntimeContextStorage *GetDefaultStorage() noexcept
  292. {
  293. return new ThreadLocalContextStorage();
  294. }
  295. } // namespace context
  296. OPENTELEMETRY_END_NAMESPACE