1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // A class for values only accessible from a single designated thread.
7 #ifndef mozilla_ThreadBound_h
8 #define mozilla_ThreadBound_h
10 #include "mozilla/Atomics.h"
13 #include <type_traits>
22 template <bool Condition
, typename T
>
28 struct AddConstIf
<true, T
> {
29 using type
= typename
std::add_const
<T
>::type
;
34 // A ThreadBound<T> is a T that can only be accessed by a specific
35 // thread. To enforce this rule, the inner T is only accessible
36 // through a non-copyable, immovable accessor object.
37 // Given a ThreadBound<T> threadBoundData, it can be accessed like so:
39 // auto innerData = threadBoundData.Access();
40 // innerData->DoStuff();
42 // Trying to access a ThreadBound<T> from a different thread will
43 // trigger a MOZ_DIAGNOSTIC_ASSERT.
44 // The encapsulated T is constructed during the construction of the
45 // enclosing ThreadBound<T> by forwarding all of the latter's
46 // constructor parameters to the former. A newly constructed
47 // ThreadBound<T> is bound to the thread it's constructed in. It's
48 // possible to rebind the data to some otherThread by calling
50 // threadBoundData.Transfer(otherThread);
52 // on the thread that threadBoundData is currently bound to, as long
53 // as it's not currently being accessed. (Trying to rebind from
54 // another thread or while an accessor exists will trigger an
57 // Note: A ThreadBound<T> may be destructed from any thread, not just
58 // its designated thread at the time the destructor is invoked.
60 class ThreadBound final
{
62 template <typename
... Args
>
63 explicit ThreadBound(Args
&&... aArgs
)
64 : mData(std::forward
<Args
>(aArgs
)...),
65 mThread(PR_GetCurrentThread()),
68 ~ThreadBound() { AssertIsNotCurrentlyAccessed(); }
70 void Transfer(const PRThread
* const aDest
) {
71 AssertIsCorrectThread();
72 AssertIsNotCurrentlyAccessed();
79 // This member is (potentially) accessed by multiple threads and is
80 // thus the first point of synchronization between them.
81 Atomic
<const PRThread
*, ReleaseAcquire
> mThread
;
83 // In order to support nested accesses (e.g. from different stack
84 // frames) it's necessary to maintain a counter of the existing
85 // accessor. Since it's possible to access a const ThreadBound, the
86 // counter is mutable. It's atomic because accessing it synchronizes
87 // access to mData (see comment in Accessor's constructor).
88 using AccessCountType
= Atomic
<int, ReleaseAcquire
>;
89 mutable AccessCountType mAccessCount
;
92 template <bool IsConst
>
93 class MOZ_STACK_CLASS Accessor final
{
94 using DataType
= typename
detail::AddConstIf
<IsConst
, T
>::type
;
98 typename
detail::AddConstIf
<IsConst
, ThreadBound
>::type
& aThreadBound
)
99 : mData(aThreadBound
.mData
), mAccessCount(aThreadBound
.mAccessCount
) {
100 aThreadBound
.AssertIsCorrectThread();
102 // This load/store serves as a memory fence that guards mData
103 // against accesses that would trip the thread assertion.
104 // (Otherwise one of the loads in the caller's instruction
105 // stream might be scheduled before the assertion.)
109 Accessor(const Accessor
&) = delete;
110 Accessor(Accessor
&&) = delete;
111 Accessor
& operator=(const Accessor
&) = delete;
112 Accessor
& operator=(Accessor
&&) = delete;
114 ~Accessor() { --mAccessCount
; }
116 DataType
* operator->() { return &mData
; }
120 AccessCountType
& mAccessCount
;
123 auto Access() { return Accessor
<false>{*this}; }
125 auto Access() const { return Accessor
<true>{*this}; }
128 bool IsCorrectThread() const { return mThread
== PR_GetCurrentThread(); }
130 bool IsNotCurrentlyAccessed() const { return mAccessCount
== 0; }
132 #define MOZ_DEFINE_THREAD_BOUND_ASSERT(predicate) \
133 void Assert##predicate() const { MOZ_DIAGNOSTIC_ASSERT(predicate()); }
135 MOZ_DEFINE_THREAD_BOUND_ASSERT(IsCorrectThread
)
136 MOZ_DEFINE_THREAD_BOUND_ASSERT(IsNotCurrentlyAccessed
)
138 #undef MOZ_DEFINE_THREAD_BOUND_ASSERT
141 } // namespace mozilla
143 #endif // mozilla_ThreadBound_h