2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_RDS_LOCAL_H_
18 #define incl_HPHP_RDS_LOCAL_H_
20 #include "hphp/util/alloc.h"
21 #include "hphp/util/assertions.h"
22 #include "hphp/util/optional.h"
23 #include "hphp/util/thread-local.h"
27 struct RequestEventHandler
;
34 * RDSLocals are stored in RDS on request threads, and in a malloc'ed buffer on
35 * non request threads. Their destructor is called as a RDS is cleaned up, or
36 * as the backing space is cleaned up. They expose the same API as thread
37 * locals, and can be used in a similar manner. The core difference is that
38 * they will be tied to a request data segment on request threads, rather than
39 * a thread local section.
40 * There are two options for how RDS locals should be initialized:
42 * - Similar to standard thread locals.
44 * - Similar to thread local no check.
46 * RDSLocals that inherit from RequestEventHandler have their requestInit, and
47 * requestEnd methods called at first use in a request, and at RequestFini.
48 * For that reason, they cannot be tagged as explicitly being initialized.
49 * This is to support legacy behavior. Favor using RDS locals that do not
50 * inherit from RequestEventHandler. Init and destroy logic can be done in
51 * InitFiniNodes and extension initialisation.
53 * Finally, for performance purposes, extremely hot RDSLocals can be added to
54 * the HotRDSLocals struct, which stores the data flat in thread local storage.
55 * Swapping RDS sections requires copying this data in and out of backing RDS
56 * storage. Avoid using these if possible.
58 * How to use rds local macros:
60 * Use RDS_LOCAL to declare a *static* class field or global as RDS local
62 * static RDS_LOCAL(SomeFieldType, field);
64 * extern RDS_LOCAL(SomeGlobalType, rl_myGlobal);
66 * Use RDS_LOCAL in the cpp file to implement the field/global:
67 * RDS_LOCAL(SomeFieldType, SomeClass::field);
68 * RDS_LOCAL(SomeGlobalType, rl_myGlobal);
71 ///////////////////////////////////////////////////////////////////////////////
74 // How to use hot RDS local macros:
77 // extern DECLARE_RDS_LOCAL_HOTVALUE(SomeType, rl_hotGlobal);
79 // In an implementation file:
80 // IMPLEMENT_RDS_LOCAL_HOTVALUE(SomeType, rl_hotGlobal);
82 // Finally add rl_hotGlobal as a member to the HotRDSLocals struct.
85 #define DECLARE_RDS_LOCAL_HOTVALUE(T, f) \
86 struct RLHotWrapper_ ## f { \
87 RLHotWrapper_ ## f& operator=(T&& v) { \
88 ::HPHP::rds::local::detail::rl_hotSection.f = v; \
92 return ::HPHP::rds::local::detail::rl_hotSection.f; \
96 #define IMPLEMENT_RDS_LOCAL_HOTVALUE(T, f) \
100 struct HotRDSLocals
{
106 TYPE_SCAN_IGNORE_FIELD(rdslocal_base
);
107 TYPE_SCAN_IGNORE_FIELD(g_context
);
109 static_assert(sizeof(HotRDSLocals
) <= 64,
110 "It is essential HotRDSLocals is small. Consider using "
111 "normal rds locals if possible. Hot rds locals are copied "
112 "every user level context switch.");
114 extern __thread HotRDSLocals rl_hotSection
;
115 extern uint32_t s_usedbytes
;
119 ///////////////////////////////////////////////////////////////////////////////
122 #define RDS_LOCAL(T, f) \
123 ::HPHP::rds::local::RDSLocal<T, ::HPHP::rds::local::Initialize::FirstUse> f
125 #define RDS_LOCAL_NO_CHECK(T, f) \
126 ::HPHP::rds::local::RDSLocal<T, ::HPHP::rds::local::Initialize::Explicitly> f
130 struct RDSLocalNode
{
131 virtual ~RDSLocalNode() {}
133 virtual void init() = 0;
134 virtual void fini() = 0;
136 virtual void* nodeLocation() = 0;
137 virtual size_t nodeSize() = 0;
138 virtual type_scan::Index
nodeTypeIndex() = 0;
141 RDSLocalNode
* m_next
;
142 // s_RDSLocalsBase is the base of the RDSLocal section in RDS. Its only
143 // used for computing RDS offsets for use in the JIT.
144 static uint32_t s_RDSLocalsBase
;
146 template<typename Fn
>
147 void iterateRoot(Fn fn
) {
148 fn(nodeLocation(), nodeSize(), nodeTypeIndex());
152 extern RDSLocalNode
* head
;
154 template<typename Fn
>
155 void iterate(Fn fn
) {
156 for (auto p
= head
; p
!= nullptr; p
= p
->m_next
) {
161 void initializeRequestEventHandler(RequestEventHandler
* h
);
163 template<typename T
, typename Enable
= void>
167 struct RDSLocalBase
<T
, std::enable_if_t
<std::is_copy_constructible
<T
>::value
>>
169 Optional
<T
> m_defaultValue
;
170 void setDefault(const T
& v
) {
173 Optional
<T
> getDefault() const {
174 return m_defaultValue
;
179 struct RDSLocalBase
<T
, std::enable_if_t
<!std::is_copy_constructible
<T
>::value
>>
181 void setDefault(const T
& v
) {}
182 Optional
<T
> getDefault() const { return std::nullopt
; }
185 ///////////////////////////////////////////////////////////////////////////////
188 struct Configuration
{
189 // Returns a handle to the base of the RDS locals in the RDS region. If no
190 // RDS section is used and RDS locals are backed by other allocation, this
191 // can be left as nullptr. The parameter is the space required for the
193 uint32_t(*rdsInitFunc
)(size_t) = nullptr;
194 // Initializes a pointer to the base of the RDS locals area to be used by the
195 // calling thread/fiber. It is passed the size required for the RDS locals
196 // (guaranteed to be identical to the size that was passed to rdsInitFunc),
197 // and the handle that rdsInitFunc returned.
198 void*(*initFunc
)(size_t, uint32_t) = nullptr;
199 // Call to deallocate the region passed as the parameter. The parameter is
200 // guaranteed to be a pointer returned by initFunc earlier. It also will
202 void(*finiFunc
)(void*) = nullptr;
203 // Returns true if the region passed as a parameter lives within RDS. If
204 // RDS is not in use, this may be left as nullptr.
205 bool(*inRdsFunc
)(void*, size_t) = nullptr;
206 // Register the parameter to have its requestInit, and requestShutdown
207 // methods called at request start and end. If no RequestEventHandlers are
208 // stored in RDS locals, then this may be left as nullptr.
209 void(*initRequestEventHandler
)(RequestEventHandler
*) = nullptr;
213 extern Configuration g_config
;
216 struct RegisterConfig
{
217 explicit RegisterConfig(Configuration
&& config
) {
218 detail::g_config
= std::move(config
);
222 // RDSInit is called to configure offsets into the rds local area.
224 // init is called to allocate and initialize the rdslocals.
226 // fini deallocates the rdslocals.
227 void fini(bool inrds
= false);
229 template<typename Fn
>
230 void iterateRoots(Fn fn
) {
231 detail::iterate([&](detail::RDSLocalNode
* p
) {
236 enum class Initialize
: uint8_t {
241 template<typename T
, Initialize Init
>
242 struct RDSLocal
: private detail::RDSLocalBase
<T
> {
243 static auto constexpr REH
= std::is_base_of
<RequestEventHandler
, T
>::value
;
244 template<typename T1
>
245 using REH_t
= std::enable_if_t
<REH
, T1
>;
246 template<typename T1
>
247 using NREH_t
= std::enable_if_t
<!REH
, T1
>;
248 static_assert(Init
!= Initialize::Explicitly
|| !REH
,
249 "Can't explicilty initialize a subtype of RequestEventHandler");
253 template<typename T1
= T
>
256 std::enable_if_t
<std::is_copy_constructible
<T1
>::value
>* = 0)
261 NEVER_INLINE
void create();
265 bool isNull() const { return !(detail::rl_hotSection
.rdslocal_base
&&
266 node().has_value()); }
267 explicit operator bool() const { return !isNull(); }
269 template<typename T1
= T
, typename
= decltype(std::declval
<T1
&>()[0])>
270 decltype(std::declval
<T1
&>()[0]) operator[](size_t i
) {
274 T
* operator->() const { return get(); }
275 T
& operator*() const { return *get(); }
280 template<typename T1
= T
*>
281 NREH_t
<T1
> getNoCheck() const {
283 return &node().value();
286 template<typename B
= bool>
287 NREH_t
<B
> getInited() const {
291 template<typename B
= bool>
292 REH_t
<B
> getInited() const {
293 return !isNull() && node().value().getInited();
296 size_t getRDSOffset() const {
297 return this->s_RDSLocalsBase
+ m_offset
;
300 size_t getRawOffset() const {
304 template <typename
... Args
>
305 void emplace(Args
&& ...args
);
308 template<typename T1
= T
>
309 std::enable_if_t
<!std::is_copy_constructible
<T1
>::value
> defaultInit() {}
310 template<typename T1
= T
>
311 std::enable_if_t
<std::is_copy_constructible
<T1
>::value
> defaultInit() {
312 if (auto const& defaultValue
= this->getDefault()) {
313 emplace(*defaultValue
);
317 void init() override
{
318 assertx(detail::rl_hotSection
.rdslocal_base
);
319 // Initialize the Node so that it is unset.
320 new (&node()) Node();
323 void fini() override
{
327 template<typename T1
= T
*>
328 REH_t
<T1
> getNoCheck() const {
330 return &node().value();
333 template<typename V
= void>
334 REH_t
<V
> rehInit() const {
336 assertx(detail::g_config
.initRequestEventHandler
);
337 detail::g_config
.initRequestEventHandler(getNoCheck());
340 template<typename V
= void>
341 NREH_t
<V
> rehInit() const {}
343 // Node reimplements parts of what Optional supplies, however it does
344 // so trusting that the user will check values are present as appropriate.
346 Node() : hasValue(false) {}
348 bool has_value() const noexcept
{ return hasValue
; }
349 T
& value() noexcept
{
353 template<typename
... Args
>
354 void emplace(Args
&&... args
) {
356 const void* ptr
= &storage
;
357 new (const_cast<void*>(ptr
)) T(std::forward
<Args
>(args
)...);
360 template<typename T1
= T
>
361 std::enable_if_t
<!std::is_trivially_destructible
<T1
>::value
, void> clear() {
367 template<typename T1
= T
>
368 std::enable_if_t
<std::is_trivially_destructible
<T1
>::value
, void> clear() {
376 scanner
.scan(storage
);
387 virtual Node
& node() const {
388 return *reinterpret_cast<Node
*>(
389 ((char*)detail::rl_hotSection
.rdslocal_base
+ m_offset
));
392 void* nodeLocation() override
{ return &node(); }
393 size_t nodeSize() override
{ return sizeof(Node
); }
394 type_scan::Index
nodeTypeIndex() override
{
395 return type_scan::getIndexForScan
<Node
>();
401 // The initialisation of the RDS locals will happen at first use, or through
402 // explicit initialisation calls. RDS locals are stored in the rds local
403 // section on request threads, so they are not assigned generation numbers, and
404 // are not invalidated at the end of a request.
406 // On non request threads RDS locals are stored in a block allocated in the
407 // heap. This is destroyed as rds::local::fini is called.
409 template<typename T
, Initialize Init
>
410 RDSLocal
<T
, Init
>::RDSLocal() {
411 this->m_next
= detail::head
;
413 auto const align
= folly::nextPowTwo(alignof(T
)) - 1;
414 always_assert(align
< 16);
415 detail::s_usedbytes
= (detail::s_usedbytes
+ align
) & ~align
;
416 m_offset
= detail::s_usedbytes
;
417 detail::s_usedbytes
+= sizeof(Node
);
420 template<typename T
, Initialize Init
>
421 void RDSLocal
<T
, Init
>::create() {
425 template<typename T
, Initialize Init
>
426 void RDSLocal
<T
, Init
>::destroy() {
432 template<typename T
, Initialize Init
>
433 void RDSLocal
<T
, Init
>::nullOut() {
439 template<typename T
, Initialize Init
>
440 T
* RDSLocal
<T
, Init
>::getCheck() const {
441 assertx(detail::rl_hotSection
.rdslocal_base
);
442 if (!node().has_value()) {
443 const_cast<RDSLocal
*>(this)->create();
449 template<typename T
, Initialize Init
>
450 T
* RDSLocal
<T
, Init
>::get() const {
451 if (Init
== Initialize::Explicitly
) {
457 template<typename T
, Initialize Init
>
458 template<typename
... Args
>
459 void RDSLocal
<T
, Init
>::emplace(Args
&&... args
) {
460 node().emplace(std::forward
<Args
>(args
) ... );
464 // This aliased rds local type is the same as an rds local, except it also
465 // stores a pointer to the rds storage in the hot rds locals struct. This
466 // saves up to 1 instruction and 1 load per access. g_context serves as an
467 // example for how it is used. It should be used sparingly as it requires
468 // storing a pointer in HotRDSLocals.
469 template<typename T
, Initialize Init
,
470 void* detail::HotRDSLocals::*ptr
>
471 struct AliasedRDSLocal
: RDSLocal
<T
, Init
> {
472 void init() override
{
473 detail::rl_hotSection
.*ptr
= &RDSLocal
<T
, Init
>::node();
474 RDSLocal
<T
, Init
>::init();
477 void fini() override
{
478 RDSLocal
<T
, Init
>::fini();
479 detail::rl_hotSection
.*ptr
= nullptr;
482 typename RDSLocal
<T
, Init
>::Node
& node() const override
{
483 assertx(detail::rl_hotSection
.*ptr
);
484 return *(typename RDSLocal
<T
, Init
>::Node
*)(detail::rl_hotSection
.*ptr
);
489 ///////////////////////////////////////////////////////////////////////////////
490 // Legacy Request Local Macros (DEPRECATED)
492 // These are implemented using the same structures as above. Please avoid
495 #define IMPLEMENT_REQUEST_LOCAL(T,f) \
496 ::HPHP::rds::local::RDSLocal<T, ::HPHP::rds::local::Initialize::FirstUse> f
498 #define DECLARE_STATIC_REQUEST_LOCAL(T,f) \
499 static ::HPHP::rds::local::RDSLocal< \
501 ::HPHP::rds::local::Initialize::FirstUse \
504 #define IMPLEMENT_STATIC_REQUEST_LOCAL(T,f) \
505 static ::HPHP::rds::local::RDSLocal< \
507 ::HPHP::rds::local::Initialize::FirstUse \
510 #define DECLARE_EXTERN_REQUEST_LOCAL(T,f) \
511 extern ::HPHP::rds::local::RDSLocal< \
513 ::HPHP::rds::local::Initialize::FirstUse \
518 * Old documentation for request locals:
520 * vvvvvv DEPRECATED vvvvvv
521 * A RequestLocal<T> is automatically thread local, plus it has two handlers
522 * to do extra work on request init and shutdown times. T needs to derive from
523 * RequestEventHandler, so it will register itself with execution engines to
524 * be called at request shutdown time.
528 * struct MyRequestLocalClass final : RequestEventHandler {
529 * void requestInit() override {...}
530 * void requestShutdown() override {...}
532 * IMPLEMENT_STATIC_REQUEST_LOCAL(MyRequestLocalClass, s_data);
534 * How to use the request-local macros:
536 * Use DECLARE_STATIC_REQUEST_LOCAL to declare a *static* class field as
539 * DECLARE_STATIC_REQUEST_LOCAL(SomeFieldType, f);
542 * Use IMPLEMENT_STATIC_REQUEST_LOCAL in the cpp file to implement the field:
543 * IMPLEMENT_STATIC_REQUEST_LOCAL(SomeFieldType, SomeClass::f);
545 * The DECLARE_REQUEST_LOCAL and IMPLEMENT_REQUEST_LOCAL macros are provided
546 * for declaring/implementing request locals in the global scope.
548 * Remember: *Never* write IMPLEMENT_STATIC_REQUEST_LOCAL in a header file.
549 * ^^^^^^ DEPRECATED ^^^^^^
552 ///////////////////////////////////////////////////////////////////////////////
555 #endif // incl_HPHP_RDS_LOCAL_H_