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.
23 #include <type_traits>
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>
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.
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
>;
54 static Synchronized
<F14FastMap
<std::string
, uint32_t>>& getCache();
59 std::is_trivially_destructible
<RequestToken
>::value
,
60 "must be trivially destructible");
66 struct hash
<folly::RequestToken
> {
67 size_t operator()(const folly::RequestToken
& token
) const {
68 return hash
<uint32_t>()(token
.token_
);
75 // Some request context that follows an async request through a process
76 // Everything in the context must be thread safe
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
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() {}
106 int refCount() { return keepAliveCounter_
.load(std::memory_order_acquire
); }
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.
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
{
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; }
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
{
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_
; }
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".
204 const RequestToken
& token
, std::unique_ptr
<RequestData
> data
);
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
));
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
;
268 RequestContext(const RequestContext
& ctx
) = default;
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};
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 */>;
291 class StaticContextAccessor
{
293 using Inner
= StaticContextThreadLocal::Accessor
;
294 using IteratorBase
= Inner::Iterator
;
295 using IteratorTag
= std::bidirectional_iterator_tag
;
299 explicit StaticContextAccessor(Inner
&& inner
) noexcept
300 : inner_(std::move(inner
)) {}
303 friend class RequestContext
;
305 class Iterator
: public detail::IteratorAdaptor
<
310 using Super
= detail::
311 IteratorAdaptor
<Iterator
, IteratorBase
, StaticContext
, IteratorTag
>;
316 StaticContext
& dereference() const { return *base(); }
318 RootIdInfo
getRootIdInfo() const {
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
,
355 void overwriteContextData(
356 const std::string
& val
,
357 std::unique_ptr
<RequestData
> data
,
359 overwriteContextData(RequestToken(val
), std::move(data
), safe
);
362 enum class DoSetBehaviour
{
368 bool doSetContextDataHelper(
369 const RequestToken
& token
,
370 std::unique_ptr
<RequestData
>& data
,
371 DoSetBehaviour behaviour
,
373 bool doSetContextDataHelper(
374 const std::string
& val
,
375 std::unique_ptr
<RequestData
>& data
,
376 DoSetBehaviour behaviour
,
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
385 // Hazard pointer-protected combined structure for request data
388 hazptr_obj_cohort
<> cohort_
; // For destruction order
389 std::atomic
<Combined
*> combined_
{nullptr};
393 State(const State
& o
);
394 State(State
&&) = delete;
395 State
& operator=(const State
&) = delete;
396 State
& operator=(State
&&) = delete;
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
,
417 bool hasContextData(const RequestToken
& token
) const;
418 RequestData
* getContextData(const RequestToken
& token
);
419 const RequestData
* getContextData(const RequestToken
& token
) const;
422 void clearContextData(const RequestToken
& token
);
423 SetContextDataResult
doSetContextDataHelper(
424 const RequestToken
& token
,
425 std::unique_ptr
<RequestData
>& data
,
426 DoSetBehaviour behaviour
,
428 Combined
* eraseOldData(
430 const RequestToken
& token
,
431 RequestData
* oldData
,
433 Combined
* insertNewData(
435 const RequestToken
& token
,
436 std::unique_ptr
<RequestData
>& data
,
440 // Shallow copies keep a note of the root context
445 * Note: you probably want to use ShallowCopyRequestContextScopeGuard
446 * This resets all other RequestData for the duration of the scope!
448 class RequestContextScopeGuard
{
450 std::shared_ptr
<RequestContext
> prev_
;
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
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
&&) =
524 ShallowCopyRequestContextScopeGuard
& operator=(
525 ShallowCopyRequestContextScopeGuard
&&) = delete;
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);
538 void(_
{0, (go(item
), 0)...});
541 std::shared_ptr
<RequestContext
> prev_
;