1 | /* |
2 | * Copyright 2014-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #pragma once |
18 | |
19 | #include <cassert> |
20 | #include <climits> |
21 | |
22 | #include <folly/Function.h> |
23 | #include <folly/Utility.h> |
24 | |
25 | namespace folly { |
26 | |
27 | using Func = Function<void()>; |
28 | |
29 | /// An Executor accepts units of work with add(), which should be |
30 | /// threadsafe. |
31 | class Executor { |
32 | public: |
33 | // Workaround for a linkage problem with explicitly defaulted dtor t22914621 |
34 | virtual ~Executor() {} |
35 | |
36 | /// Enqueue a function to executed by this executor. This and all |
37 | /// variants must be threadsafe. |
38 | virtual void add(Func) = 0; |
39 | |
40 | /// Enqueue a function with a given priority, where 0 is the medium priority |
41 | /// This is up to the implementation to enforce |
42 | virtual void addWithPriority(Func, int8_t priority); |
43 | |
44 | virtual uint8_t getNumPriorities() const { |
45 | return 1; |
46 | } |
47 | |
48 | static const int8_t LO_PRI = SCHAR_MIN; |
49 | static const int8_t MID_PRI = 0; |
50 | static const int8_t HI_PRI = SCHAR_MAX; |
51 | |
52 | template <typename ExecutorT = Executor> |
53 | class KeepAlive { |
54 | public: |
55 | KeepAlive() = default; |
56 | |
57 | ~KeepAlive() { |
58 | reset(); |
59 | } |
60 | |
61 | KeepAlive(KeepAlive&& other) noexcept |
62 | : executorAndDummyFlag_(exchange(other.executorAndDummyFlag_, 0)) {} |
63 | |
64 | template < |
65 | typename OtherExecutor, |
66 | typename = typename std::enable_if< |
67 | std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type> |
68 | /* implicit */ KeepAlive(KeepAlive<OtherExecutor>&& other) noexcept |
69 | : KeepAlive(other.get(), other.executorAndDummyFlag_ & kDummyFlag) { |
70 | other.executorAndDummyFlag_ = 0; |
71 | } |
72 | |
73 | KeepAlive& operator=(KeepAlive&& other) { |
74 | reset(); |
75 | executorAndDummyFlag_ = exchange(other.executorAndDummyFlag_, 0); |
76 | return *this; |
77 | } |
78 | |
79 | template < |
80 | typename OtherExecutor, |
81 | typename = typename std::enable_if< |
82 | std::is_convertible<OtherExecutor*, ExecutorT*>::value>::type> |
83 | KeepAlive& operator=(KeepAlive<OtherExecutor>&& other) { |
84 | return *this = KeepAlive(std::move(other)); |
85 | } |
86 | |
87 | void reset() { |
88 | if (Executor* executor = get()) { |
89 | if (exchange(executorAndDummyFlag_, 0) & kDummyFlag) { |
90 | return; |
91 | } |
92 | executor->keepAliveRelease(); |
93 | } |
94 | } |
95 | |
96 | explicit operator bool() const { |
97 | return executorAndDummyFlag_; |
98 | } |
99 | |
100 | ExecutorT* get() const { |
101 | return reinterpret_cast<ExecutorT*>( |
102 | executorAndDummyFlag_ & kExecutorMask); |
103 | } |
104 | |
105 | ExecutorT& operator*() const { |
106 | return *get(); |
107 | } |
108 | |
109 | ExecutorT* operator->() const { |
110 | return get(); |
111 | } |
112 | |
113 | KeepAlive copy() const { |
114 | return getKeepAliveToken(get()); |
115 | } |
116 | |
117 | // Creates a dummy copy of this KeepAlive token, which doesn't increment |
118 | // the ref-count. Should only be used if this KeepAlive token is known to |
119 | // outlive such dummy copy. |
120 | KeepAlive copyDummy() const { |
121 | return KeepAlive(get(), true); |
122 | } |
123 | |
124 | private: |
125 | static constexpr intptr_t kDummyFlag = 1; |
126 | static constexpr intptr_t kExecutorMask = ~kDummyFlag; |
127 | |
128 | friend class Executor; |
129 | template <typename OtherExecutor> |
130 | friend class KeepAlive; |
131 | |
132 | KeepAlive(ExecutorT* executor, bool dummy) |
133 | : executorAndDummyFlag_( |
134 | reinterpret_cast<intptr_t>(executor) | (dummy ? kDummyFlag : 0)) { |
135 | assert(executor); |
136 | assert( |
137 | (reinterpret_cast<intptr_t>(executor) & kExecutorMask) == |
138 | reinterpret_cast<intptr_t>(executor)); |
139 | } |
140 | |
141 | intptr_t executorAndDummyFlag_{reinterpret_cast<intptr_t>(nullptr)}; |
142 | }; |
143 | |
144 | template <typename ExecutorT> |
145 | static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) { |
146 | static_assert( |
147 | std::is_base_of<Executor, ExecutorT>::value, |
148 | "getKeepAliveToken only works for folly::Executor implementations." ); |
149 | if (!executor) { |
150 | return {}; |
151 | } |
152 | folly::Executor* executorPtr = executor; |
153 | if (executorPtr->keepAliveAcquire()) { |
154 | return makeKeepAlive<ExecutorT>(executor); |
155 | } |
156 | return makeKeepAliveDummy<ExecutorT>(executor); |
157 | } |
158 | |
159 | template <typename ExecutorT> |
160 | static KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT& executor) { |
161 | static_assert( |
162 | std::is_base_of<Executor, ExecutorT>::value, |
163 | "getKeepAliveToken only works for folly::Executor implementations." ); |
164 | return getKeepAliveToken(&executor); |
165 | } |
166 | |
167 | protected: |
168 | /** |
169 | * Returns true if the KeepAlive is constructed from an executor that does |
170 | * not support the keep alive ref-counting functionality |
171 | */ |
172 | template <typename ExecutorT> |
173 | static bool isKeepAliveDummy(const KeepAlive<ExecutorT>& keepAlive) { |
174 | return reinterpret_cast<intptr_t>(keepAlive.executorAndDummyFlag_) & |
175 | KeepAlive<ExecutorT>::kDummyFlag; |
176 | } |
177 | |
178 | // Acquire a keep alive token. Should return false if keep-alive mechanism |
179 | // is not supported. |
180 | virtual bool keepAliveAcquire(); |
181 | // Release a keep alive token previously acquired by keepAliveAcquire(). |
182 | // Will never be called if keepAliveAcquire() returns false. |
183 | virtual void keepAliveRelease(); |
184 | |
185 | template <typename ExecutorT> |
186 | static KeepAlive<ExecutorT> makeKeepAlive(ExecutorT* executor) { |
187 | static_assert( |
188 | std::is_base_of<Executor, ExecutorT>::value, |
189 | "makeKeepAlive only works for folly::Executor implementations." ); |
190 | return KeepAlive<ExecutorT>{executor, false}; |
191 | } |
192 | |
193 | private: |
194 | template <typename ExecutorT> |
195 | static KeepAlive<ExecutorT> makeKeepAliveDummy(ExecutorT* executor) { |
196 | static_assert( |
197 | std::is_base_of<Executor, ExecutorT>::value, |
198 | "makeKeepAliveDummy only works for folly::Executor implementations." ); |
199 | return KeepAlive<ExecutorT>{executor, true}; |
200 | } |
201 | }; |
202 | |
203 | /// Returns a keep-alive token which guarantees that Executor will keep |
204 | /// processing tasks until the token is released (if supported by Executor). |
205 | /// KeepAlive always contains a valid pointer to an Executor. |
206 | template <typename ExecutorT> |
207 | Executor::KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT* executor) { |
208 | static_assert( |
209 | std::is_base_of<Executor, ExecutorT>::value, |
210 | "getKeepAliveToken only works for folly::Executor implementations." ); |
211 | return Executor::getKeepAliveToken(executor); |
212 | } |
213 | |
214 | template <typename ExecutorT> |
215 | Executor::KeepAlive<ExecutorT> getKeepAliveToken(ExecutorT& executor) { |
216 | static_assert( |
217 | std::is_base_of<Executor, ExecutorT>::value, |
218 | "getKeepAliveToken only works for folly::Executor implementations." ); |
219 | return getKeepAliveToken(&executor); |
220 | } |
221 | |
222 | } // namespace folly |
223 | |