1 // Copyright 2021 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef GRPC_SRC_CORE_LIB_PROMISE_CONTEXT_H
16 #define GRPC_SRC_CORE_LIB_PROMISE_CONTEXT_H
17
18 #include <grpc/support/port_platform.h>
19
20 #include <utility>
21
22 #include "absl/log/check.h"
23 #include "absl/meta/type_traits.h"
24 #include "src/core/util/down_cast.h"
25
26 namespace grpc_core {
27
28 // To avoid accidentally creating context types, we require an explicit
29 // specialization of this template per context type. The specialization need
30 // not contain any members, only exist.
31 // The reason for avoiding this is that context types each use a thread local.
32 template <typename T>
33 struct ContextType;
34
35 // Some contexts can be subclassed. If the subclass is set as that context
36 // then GetContext<Base>() will return the base, and GetContext<Derived>() will
37 // DownCast to the derived type.
38 // Specializations of this type should be created for each derived type, and
39 // should have a single using statement Base pointing to the derived base class.
40 // Example:
41 // class SomeContext {};
42 // class SomeDerivedContext : public SomeContext {};
43 // template <> struct ContextType<SomeContext> {};
44 // template <> struct ContextSubclass<SomeDerivedContext> {
45 // using Base = SomeContext;
46 // };
47 template <typename Derived>
48 struct ContextSubclass;
49
50 namespace promise_detail {
51
52 template <typename T, typename = void>
53 class Context;
54
55 template <typename T>
56 class ThreadLocalContext : public ContextType<T> {
57 public:
ThreadLocalContext(T * p)58 explicit ThreadLocalContext(T* p) : old_(current_) { current_ = p; }
~ThreadLocalContext()59 ~ThreadLocalContext() { current_ = old_; }
60 ThreadLocalContext(const ThreadLocalContext&) = delete;
61 ThreadLocalContext& operator=(const ThreadLocalContext&) = delete;
62
get()63 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static T* get() { return current_; }
64
65 private:
66 T* const old_;
67 static thread_local T* current_;
68 };
69
70 template <typename T>
71 thread_local T* ThreadLocalContext<T>::current_;
72
73 template <typename T>
74 class Context<T, absl::void_t<decltype(ContextType<T>())>>
75 : public ThreadLocalContext<T> {
76 using ThreadLocalContext<T>::ThreadLocalContext;
77 };
78
79 template <typename T>
80 class Context<T, absl::void_t<typename ContextSubclass<T>::Base>>
81 : public Context<typename ContextSubclass<T>::Base> {
82 public:
83 using Context<typename ContextSubclass<T>::Base>::Context;
get()84 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION static T* get() {
85 return DownCast<T*>(Context<typename ContextSubclass<T>::Base>::get());
86 }
87 };
88
89 template <typename T, typename F>
90 class WithContext {
91 public:
WithContext(F f,T * context)92 WithContext(F f, T* context) : context_(context), f_(std::move(f)) {}
93
operator()94 decltype(std::declval<F>()()) operator()() {
95 Context<T> ctx(context_);
96 return f_();
97 }
98
99 private:
100 T* context_;
101 F f_;
102 };
103
104 } // namespace promise_detail
105
106 // Return true if a context of type T is currently active.
107 template <typename T>
HasContext()108 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline bool HasContext() {
109 return promise_detail::Context<T>::get() != nullptr;
110 }
111
112 // Retrieve the current value of a context, or abort if the value is unset.
113 template <typename T>
GetContext()114 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline T* GetContext() {
115 auto* p = promise_detail::Context<T>::get();
116 DCHECK_NE(p, nullptr);
117 return p;
118 }
119
120 // Retrieve the current value of a context, or nullptr if the value is unset.
121 template <typename T>
MaybeGetContext()122 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline T* MaybeGetContext() {
123 return promise_detail::Context<T>::get();
124 }
125
126 template <typename T>
SetContext(T * p)127 GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION inline void SetContext(T* p) {
128 promise_detail::Context<T>::set(p);
129 }
130
131 // Given a promise and a context, return a promise that has that context set.
132 template <typename T, typename F>
WithContext(F f,T * context)133 promise_detail::WithContext<T, F> WithContext(F f, T* context) {
134 return promise_detail::WithContext<T, F>(std::move(f), context);
135 }
136
137 } // namespace grpc_core
138
139 #endif // GRPC_SRC_CORE_LIB_PROMISE_CONTEXT_H
140