Bug 1893155 - Part 1: Make Calendar{WeekOfYear,YearOfWeek} return undefined. r=spider...
[gecko.git] / xpcom / threads / ThreadBound.h
blob4d5e0088b5628abb6c9fa856492dee4a3275e0ab
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"
11 #include "prthread.h"
13 #include <type_traits>
15 namespace mozilla {
17 template <typename T>
18 class ThreadBound;
20 namespace detail {
22 template <bool Condition, typename T>
23 struct AddConstIf {
24 using type = T;
27 template <typename T>
28 struct AddConstIf<true, T> {
29 using type = typename std::add_const<T>::type;
32 } // namespace detail
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
55 // assertion.)
57 // Note: A ThreadBound<T> may be destructed from any thread, not just
58 // its designated thread at the time the destructor is invoked.
59 template <typename T>
60 class ThreadBound final {
61 public:
62 template <typename... Args>
63 explicit ThreadBound(Args&&... aArgs)
64 : mData(std::forward<Args>(aArgs)...),
65 mThread(PR_GetCurrentThread()),
66 mAccessCount(0) {}
68 ~ThreadBound() { AssertIsNotCurrentlyAccessed(); }
70 void Transfer(const PRThread* const aDest) {
71 AssertIsCorrectThread();
72 AssertIsNotCurrentlyAccessed();
73 mThread = aDest;
76 private:
77 T mData;
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;
91 public:
92 template <bool IsConst>
93 class MOZ_STACK_CLASS Accessor final {
94 using DataType = typename detail::AddConstIf<IsConst, T>::type;
96 public:
97 explicit Accessor(
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.)
106 ++mAccessCount;
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; }
118 private:
119 DataType& mData;
120 AccessCountType& mAccessCount;
123 auto Access() { return Accessor<false>{*this}; }
125 auto Access() const { return Accessor<true>{*this}; }
127 private:
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