Re-sync with internal repository
[hiphop-php.git] / third-party / folly / src / folly / io / async / Request.h
blob451239e0194bb556a2609821d2514bba9e3f2fb3
1 /*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #pragma once
19 #include <atomic>
20 #include <memory>
21 #include <mutex>
22 #include <string>
23 #include <type_traits>
24 #include <utility>
26 #include <folly/SingletonThreadLocal.h>
27 #include <folly/Synchronized.h>
28 #include <folly/container/F14Map.h>
29 #include <folly/detail/Iterators.h>
30 #include <folly/synchronization/Hazptr.h>
32 namespace folly {
35 * A token to be used to fetch data from RequestContext.
36 * Generally you will want this to be a static, created only once using a
37 * string, and then only copied. The string constructor is expensive.
39 class RequestToken {
40 public:
41 RequestToken() = default;
42 explicit RequestToken(const std::string& str);
44 bool operator==(const RequestToken& other) const {
45 return token_ == other.token_;
48 // Slow, use only for debug log messages.
49 std::string getDebugString() const;
51 friend struct std::hash<folly::RequestToken>;
53 private:
54 static Synchronized<F14FastMap<std::string, uint32_t>>& getCache();
56 uint32_t token_;
58 static_assert(
59 std::is_trivially_destructible<RequestToken>::value,
60 "must be trivially destructible");
62 } // namespace folly
64 namespace std {
65 template <>
66 struct hash<folly::RequestToken> {
67 size_t operator()(const folly::RequestToken& token) const {
68 return hash<uint32_t>()(token.token_);
71 } // namespace std
73 namespace folly {
75 // Some request context that follows an async request through a process
76 // Everything in the context must be thread safe
78 class RequestData {
79 public:
80 virtual ~RequestData() = default;
82 // Avoid calling RequestContext::setContextData, setContextDataIfAbsent, or
83 // clearContextData from these callbacks. Doing so will cause deadlock. We
84 // could fix these deadlocks, but only at significant performance penalty, so
85 // just don't do it!
87 // hasCallback() applies only to onSet() and onUnset().
88 // onClear() is always executed exactly once.
89 virtual bool hasCallback() = 0;
90 // Callback executed when setting RequestContext. Make sure your RequestData
91 // instance overrides the hasCallback method to return true otherwise
92 // the callback will not be executed
93 virtual void onSet() {}
94 // Callback executed when unsetting RequestContext. Make sure your RequestData
95 // instance overrides the hasCallback method to return true otherwise
96 // the callback will not be executed
97 virtual void onUnset() {}
98 // Callback executed exactly once upon the release of the last
99 // reference to the request data (as a result of either a call to
100 // clearContextData or the destruction of a request context that
101 // contains a reference to the data). It can be overridden in
102 // derived classes. There may be concurrent executions of onSet()
103 // and onUnset() with that of onClear().
104 virtual void onClear() {}
105 // For debugging
106 int refCount() { return keepAliveCounter_.load(std::memory_order_acquire); }
108 private:
109 // For efficiency, RequestContext provides a raw ptr interface.
110 // To support shallow copy, we need a shared ptr.
111 // To keep it as safe as possible (even if a raw ptr is passed back),
112 // the counter lives directly in RequestData.
114 friend class RequestContext;
116 static constexpr int kDeleteCount = 0x1;
117 static constexpr int kClearCount = 0x1000;
119 // Reference-counting functions.
120 // Increment the reference count.
121 void acquireRef();
122 // Decrement the reference count. Clear only if last.
123 void releaseRefClearOnly();
124 // Decrement the reference count. Delete only if last.
125 void releaseRefDeleteOnly();
126 // Decrement the reference count. Clear and delete if last.
127 void releaseRefClearDelete();
128 void releaseRefClearDeleteSlow();
130 std::atomic<int> keepAliveCounter_{0};
134 * ImmutableRequestData is a folly::RequestData that holds an immutable value.
135 * It is thread-safe (a requirement of RequestData) because it is immutable.
137 template <typename T>
138 class ImmutableRequestData : public folly::RequestData {
139 public:
140 template <
141 typename... Args,
142 typename = typename std::enable_if<
143 std::is_constructible<T, Args...>::value>::type>
144 explicit ImmutableRequestData(Args&&... args) noexcept(
145 std::is_nothrow_constructible<T, Args...>::value)
146 : val_(std::forward<Args>(args)...) {}
148 const T& value() const { return val_; }
150 bool hasCallback() override { return false; }
152 private:
153 const T val_;
156 using RequestDataItem = std::pair<RequestToken, std::unique_ptr<RequestData>>;
158 // If you do not call create() to create a unique request context,
159 // this default request context will always be returned, and is never
160 // copied between threads.
161 class RequestContext {
162 public:
163 RequestContext();
164 RequestContext(RequestContext&& ctx) = delete;
165 RequestContext& operator=(const RequestContext&) = delete;
166 RequestContext& operator=(RequestContext&&) = delete;
168 // copy ctor is disabled, use copyAsRoot/copyAsChild instead.
169 static std::shared_ptr<RequestContext> copyAsRoot(
170 const RequestContext& ctx, intptr_t rootid);
171 static std::shared_ptr<RequestContext> copyAsChild(const RequestContext& ctx);
173 // Create a unique request context for this request.
174 // It will be passed between queues / threads (where implemented),
175 // so it should be valid for the lifetime of the request.
176 static void create() { setContext(std::make_shared<RequestContext>()); }
178 // Get the current context.
179 static RequestContext* get();
181 // Get the current context, if it has already been created, or nullptr.
182 static RequestContext* try_get();
184 intptr_t getRootId() const { return rootId_; }
186 struct RootIdInfo {
187 intptr_t id;
188 std::thread::id tid;
189 uint64_t tidOS;
191 static std::vector<RootIdInfo> getRootIdsFromAllThreads();
193 // The following APIs are used to add, remove and access RequestData instance
194 // in the RequestContext instance, normally used for per-RequestContext
195 // tracking or callback on set and unset. These APIs are Thread-safe.
196 // These APIs are performance sensitive, so please ask if you need help
197 // profiling any use of these APIs.
199 // Add RequestData instance "data" to this RequestContext instance, with
200 // string identifier "val". If the same string identifier has already been
201 // used, will print a warning message for the first time, clear the existing
202 // RequestData instance for "val", and **not** add "data".
203 void setContextData(
204 const RequestToken& token, std::unique_ptr<RequestData> data);
205 void setContextData(
206 const std::string& val, std::unique_ptr<RequestData> data) {
207 setContextData(RequestToken(val), std::move(data));
210 // Add RequestData instance "data" to this RequestContext instance, with
211 // string identifier "val". If the same string identifier has already been
212 // used, return false and do nothing. Otherwise add "data" and return true.
213 bool setContextDataIfAbsent(
214 const RequestToken& token, std::unique_ptr<RequestData> data);
215 bool setContextDataIfAbsent(
216 const std::string& val, std::unique_ptr<RequestData> data) {
217 return setContextDataIfAbsent(RequestToken(val), std::move(data));
220 // Remove the RequestData instance with string identifier "val", if it exists.
221 void clearContextData(const RequestToken& val);
222 void clearContextData(const std::string& val) {
223 clearContextData(RequestToken(val));
226 // Returns true if and only if the RequestData instance with string identifier
227 // "val" exists in this RequestContext instnace.
228 bool hasContextData(const RequestToken& val) const;
229 bool hasContextData(const std::string& val) const {
230 return hasContextData(RequestToken(val));
233 // Get (constant) raw pointer of the RequestData instance with string
234 // identifier "val" if it exists, otherwise returns null pointer.
235 RequestData* getContextData(const RequestToken& val);
236 const RequestData* getContextData(const RequestToken& val) const;
237 RequestData* getContextData(const std::string& val) {
238 return getContextData(RequestToken(val));
240 const RequestData* getContextData(const std::string& val) const {
241 return getContextData(RequestToken(val));
244 void onSet();
245 void onUnset();
247 // The following API is used to pass the context through queues / threads.
248 // saveContext is called to get a shared_ptr to the context, and
249 // setContext is used to reset it on the other side of the queue.
251 // Whenever possible, use RequestContextScopeGuard instead of setContext
252 // to make sure that RequestContext is reset to the original value when
253 // we exit the scope.
255 // A shared_ptr is used, because many request may fan out across
256 // multiple threads, or do post-send processing, etc.
257 static std::shared_ptr<RequestContext> setContext(
258 std::shared_ptr<RequestContext> const& ctx);
259 static std::shared_ptr<RequestContext> setContext(
260 std::shared_ptr<RequestContext>&& newCtx_);
262 static std::shared_ptr<RequestContext> saveContext() {
263 return getStaticContext().requestContext;
266 private:
267 struct Tag {};
268 RequestContext(const RequestContext& ctx) = default;
270 public:
271 RequestContext(const RequestContext& ctx, intptr_t rootid, Tag tag);
272 RequestContext(const RequestContext& ctx, Tag tag);
273 explicit RequestContext(intptr_t rootId);
275 struct StaticContext {
276 std::shared_ptr<RequestContext> requestContext;
277 std::atomic<intptr_t> rootId{0};
280 private:
281 static StaticContext& getStaticContext();
282 static StaticContext* tryGetStaticContext();
283 static std::shared_ptr<RequestContext> setContextHelper(
284 std::shared_ptr<RequestContext>& newCtx, StaticContext& staticCtx);
286 using StaticContextThreadLocal = SingletonThreadLocal<
287 RequestContext::StaticContext,
288 RequestContext /* Tag */>;
290 public:
291 class StaticContextAccessor {
292 private:
293 using Inner = StaticContextThreadLocal::Accessor;
294 using IteratorBase = Inner::Iterator;
295 using IteratorTag = std::bidirectional_iterator_tag;
297 Inner inner_;
299 explicit StaticContextAccessor(Inner&& inner) noexcept
300 : inner_(std::move(inner)) {}
302 public:
303 friend class RequestContext;
305 class Iterator : public detail::IteratorAdaptor<
306 Iterator,
307 IteratorBase,
308 StaticContext,
309 IteratorTag> {
310 using Super = detail::
311 IteratorAdaptor<Iterator, IteratorBase, StaticContext, IteratorTag>;
313 public:
314 using Super::Super;
316 StaticContext& dereference() const { return *base(); }
318 RootIdInfo getRootIdInfo() const {
319 return {
320 base()->rootId.load(std::memory_order_relaxed),
321 base().getThreadId(),
322 base().getOSThreadId()};
326 StaticContextAccessor(const StaticContextAccessor&) = delete;
327 StaticContextAccessor& operator=(const StaticContextAccessor&) = delete;
328 StaticContextAccessor(StaticContextAccessor&&) = default;
329 StaticContextAccessor& operator=(StaticContextAccessor&&) = default;
331 Iterator begin() const { return Iterator(inner_.begin()); }
332 Iterator end() const { return Iterator(inner_.end()); }
334 // Returns an accessor object that blocks the construction and destruction of
335 // StaticContext objects on all threads. This is useful to quickly introspect
336 // the context from all threads while ensuring that their thread-local
337 // StaticContext object is not destroyed.
338 static StaticContextAccessor accessAllThreads();
340 // Start shallow copy guard implementation details:
341 // All methods are private to encourage proper use
342 friend struct ShallowCopyRequestContextScopeGuard;
344 // This sets a shallow copy of the current context as current,
345 // then return the previous context (so it can be reset later).
346 static std::shared_ptr<RequestContext> setShallowCopyContext();
348 // For functions with a parameter safe, if safe is true then the
349 // caller guarantees that there are no concurrent readers or writers
350 // accessing the structure.
351 void overwriteContextData(
352 const RequestToken& token,
353 std::unique_ptr<RequestData> data,
354 bool safe = false);
355 void overwriteContextData(
356 const std::string& val,
357 std::unique_ptr<RequestData> data,
358 bool safe = false) {
359 overwriteContextData(RequestToken(val), std::move(data), safe);
362 enum class DoSetBehaviour {
363 SET,
364 SET_IF_ABSENT,
365 OVERWRITE,
368 bool doSetContextDataHelper(
369 const RequestToken& token,
370 std::unique_ptr<RequestData>& data,
371 DoSetBehaviour behaviour,
372 bool safe = false);
373 bool doSetContextDataHelper(
374 const std::string& val,
375 std::unique_ptr<RequestData>& data,
376 DoSetBehaviour behaviour,
377 bool safe = false) {
378 return doSetContextDataHelper(RequestToken(val), data, behaviour, safe);
381 // State implementation with single-writer multi-reader data
382 // structures protected by hazard pointers for readers and a lock
383 // for writers.
384 struct State {
385 // Hazard pointer-protected combined structure for request data
386 // and callbacks.
387 struct Combined;
388 hazptr_obj_cohort<> cohort_; // For destruction order
389 std::atomic<Combined*> combined_{nullptr};
390 std::mutex mutex_;
392 State();
393 State(const State& o);
394 State(State&&) = delete;
395 State& operator=(const State&) = delete;
396 State& operator=(State&&) = delete;
397 ~State();
399 private:
400 friend class RequestContext;
402 struct SetContextDataResult {
403 bool changed; // Changes were made
404 bool unexpected; // Update was unexpected
405 Combined* replaced; // The combined structure was replaced
408 Combined* combined() const;
409 Combined* ensureCombined(); // Lazy allocation if needed
410 void setCombined(Combined* p);
411 Combined* expand(Combined* combined);
412 bool doSetContextData(
413 const RequestToken& token,
414 std::unique_ptr<RequestData>& data,
415 DoSetBehaviour behaviour,
416 bool safe);
417 bool hasContextData(const RequestToken& token) const;
418 RequestData* getContextData(const RequestToken& token);
419 const RequestData* getContextData(const RequestToken& token) const;
420 void onSet();
421 void onUnset();
422 void clearContextData(const RequestToken& token);
423 SetContextDataResult doSetContextDataHelper(
424 const RequestToken& token,
425 std::unique_ptr<RequestData>& data,
426 DoSetBehaviour behaviour,
427 bool safe);
428 Combined* eraseOldData(
429 Combined* cur,
430 const RequestToken& token,
431 RequestData* oldData,
432 bool safe);
433 Combined* insertNewData(
434 Combined* cur,
435 const RequestToken& token,
436 std::unique_ptr<RequestData>& data,
437 bool found);
438 }; // State
439 State state_;
440 // Shallow copies keep a note of the root context
441 intptr_t rootId_;
445 * Note: you probably want to use ShallowCopyRequestContextScopeGuard
446 * This resets all other RequestData for the duration of the scope!
448 class RequestContextScopeGuard {
449 private:
450 std::shared_ptr<RequestContext> prev_;
452 public:
453 RequestContextScopeGuard(const RequestContextScopeGuard&) = delete;
454 RequestContextScopeGuard& operator=(const RequestContextScopeGuard&) = delete;
455 RequestContextScopeGuard(RequestContextScopeGuard&&) = delete;
456 RequestContextScopeGuard& operator=(RequestContextScopeGuard&&) = delete;
458 // Create a new RequestContext and reset to the original value when
459 // this goes out of scope.
460 RequestContextScopeGuard() : prev_(RequestContext::saveContext()) {
461 RequestContext::create();
464 // Set a RequestContext that was previously captured by saveContext(). It will
465 // be automatically reset to the original value when this goes out of scope.
466 explicit RequestContextScopeGuard(std::shared_ptr<RequestContext> const& ctx)
467 : prev_(RequestContext::setContext(ctx)) {}
468 explicit RequestContextScopeGuard(std::shared_ptr<RequestContext>&& ctx)
469 : prev_(RequestContext::setContext(std::move(ctx))) {}
471 ~RequestContextScopeGuard() { RequestContext::setContext(std::move(prev_)); }
475 * This guard maintains all the RequestData pointers of the parent.
476 * This allows to overwrite a specific RequestData pointer for the
477 * scope's duration, without breaking others.
479 * Only modified pointers will have their set/onset methods called
481 struct ShallowCopyRequestContextScopeGuard {
482 ShallowCopyRequestContextScopeGuard()
483 : prev_(RequestContext::setShallowCopyContext()) {}
486 * Shallow copy then overwrite one specific RequestData
488 * Helper constructor which is a more efficient equivalent to
489 * "clearRequestData" then "setRequestData" after the guard.
491 ShallowCopyRequestContextScopeGuard(
492 const RequestToken& token, std::unique_ptr<RequestData> data)
493 : ShallowCopyRequestContextScopeGuard() {
494 RequestContext::get()->overwriteContextData(token, std::move(data), true);
496 ShallowCopyRequestContextScopeGuard(
497 const std::string& val, std::unique_ptr<RequestData> data)
498 : ShallowCopyRequestContextScopeGuard() {
499 RequestContext::get()->overwriteContextData(val, std::move(data), true);
503 * Shallow copy then overwrite multiple RequestData instances
505 * Helper constructor which is more efficient than using multiple scope guards
506 * Accepts iterators to a container of <string/RequestToken, RequestData
507 * pointer> pairs
509 template <typename... Item>
510 explicit ShallowCopyRequestContextScopeGuard(
511 RequestDataItem&& first, Item&&... rest)
512 : ShallowCopyRequestContextScopeGuard(MultiTag{}, first, rest...) {}
514 ~ShallowCopyRequestContextScopeGuard() {
515 RequestContext::setContext(std::move(prev_));
518 ShallowCopyRequestContextScopeGuard(
519 const ShallowCopyRequestContextScopeGuard&) = delete;
520 ShallowCopyRequestContextScopeGuard& operator=(
521 const ShallowCopyRequestContextScopeGuard&) = delete;
522 ShallowCopyRequestContextScopeGuard(ShallowCopyRequestContextScopeGuard&&) =
523 delete;
524 ShallowCopyRequestContextScopeGuard& operator=(
525 ShallowCopyRequestContextScopeGuard&&) = delete;
527 private:
528 struct MultiTag {};
529 template <typename... Item>
530 explicit ShallowCopyRequestContextScopeGuard(MultiTag, Item&... item)
531 : ShallowCopyRequestContextScopeGuard() {
532 auto rc = RequestContext::get();
533 auto go = [&](RequestDataItem& i) {
534 rc->overwriteContextData(i.first, std::move(i.second), true);
537 using _ = int[];
538 void(_{0, (go(item), 0)...});
541 std::shared_ptr<RequestContext> prev_;
544 } // namespace folly