1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef threading_Thread_h
8 #define threading_Thread_h
10 #include "mozilla/Atomics.h"
11 #include "mozilla/TimeStamp.h"
14 #include <type_traits>
17 #include "js/Initialization.h"
18 #include "js/Utility.h"
19 #include "threading/LockGuard.h"
20 #include "threading/Mutex.h"
21 #include "threading/ThreadId.h"
22 #include "vm/MutexIDs.h"
25 # define THREAD_RETURN_TYPE unsigned int
26 # define THREAD_CALL_API __stdcall
28 # define THREAD_RETURN_TYPE void*
29 # define THREAD_CALL_API
34 template <typename F
, typename
... Args
>
35 class ThreadTrampoline
;
38 // Execute the given functor concurrent with the currently executing instruction
39 // stream and within the current address space. Use with care.
42 // Provides optional parameters to a Thread.
47 Options() : stackSize_(0) {}
49 Options
& setStackSize(size_t sz
) {
53 size_t stackSize() const { return stackSize_
; }
56 // Create a Thread in an initially unjoinable state. A thread of execution can
57 // be created for this Thread by calling |init|. Some of the thread's
58 // properties may be controlled by passing options to this constructor.
59 template <typename O
= Options
,
60 // SFINAE to make sure we don't try and treat functors for the other
61 // constructor as an Options and vice versa.
62 typename NonConstO
= std::remove_const_t
<O
>,
63 typename DerefO
= std::remove_reference_t
<NonConstO
>,
64 typename
= std::enable_if_t
<std::is_same_v
<DerefO
, Options
>>>
65 explicit Thread(O
&& options
= Options())
66 : id_(ThreadId()), options_(std::forward
<O
>(options
)) {
67 MOZ_ASSERT(isInitialized());
70 // Start a thread of execution at functor |f| with parameters |args|. This
71 // method will return false if thread creation fails. This Thread must not
72 // already have been created. Note that the arguments must be either POD or
73 // rvalue references (std::move). Attempting to pass a reference will
74 // result in the value being copied, which may not be the intended behavior.
75 // See the comment below on ThreadTrampoline::args for an explanation.
76 template <typename F
, typename
... Args
>
77 [[nodiscard
]] bool init(F
&& f
, Args
&&... args
) {
78 MOZ_RELEASE_ASSERT(id_
== ThreadId());
79 using Trampoline
= detail::ThreadTrampoline
<F
, Args
...>;
81 js_new
<Trampoline
>(std::forward
<F
>(f
), std::forward
<Args
>(args
)...);
86 // We hold this lock while create() sets the thread id.
87 LockGuard
<Mutex
> lock(trampoline
->createMutex
);
88 return create(Trampoline::Start
, trampoline
);
91 // The thread must be joined or detached before destruction.
94 // Move the thread into the detached state without blocking. In the detached
95 // state, the thread continues to run until it exits, but cannot be joined.
96 // After this method returns, this Thread no longer represents a thread of
97 // execution. When the thread exits, its resources will be cleaned up by the
98 // system. At process exit, if the thread is still running, the thread's TLS
99 // storage will be destructed, but the thread stack will *not* be unrolled.
102 // Block the current thread until this Thread returns from the functor it was
103 // created with. The thread's resources will be cleaned up before this
104 // function returns. After this method returns, this Thread no longer
105 // represents a thread of execution.
108 // Return true if this thread has not yet been joined or detached. If this
109 // method returns false, this Thread does not have an associated thread of
110 // execution, for example, if it has been previously moved or joined.
113 // Returns the id of this thread if this represents a thread of execution or
114 // the default constructed Id() if not. The thread ID is guaranteed to
115 // uniquely identify a thread and can be compared with the == operator.
118 // Allow threads to be moved so that they can be stored in containers.
119 Thread(Thread
&& aOther
);
120 Thread
& operator=(Thread
&& aOther
);
123 // Disallow copy as that's not sensible for unique resources.
124 Thread(const Thread
&) = delete;
125 void operator=(const Thread
&) = delete;
127 // Provide a process global ID to each thread.
130 // Overridable thread creation options.
133 // Dispatch to per-platform implementation of thread creation.
134 [[nodiscard
]] bool create(THREAD_RETURN_TYPE(THREAD_CALL_API
* aMain
)(void*),
137 // An internal version of JS_IsInitialized() that returns whether SpiderMonkey
138 // is currently initialized or is in the process of being initialized.
139 static inline bool isInitialized() {
140 using namespace JS::detail
;
141 return libraryInitState
== InitState::Initializing
||
142 libraryInitState
== InitState::Running
;
146 namespace ThisThread
{
148 // Set the current thread name. Note that setting the thread name may not be
149 // available on all platforms; on these platforms setName() will simply do
151 void SetName(const char* name
);
153 // Get the current thread name. As with SetName, not available on all
154 // platforms. On these platforms getName() will give back an empty string (by
155 // storing NUL in nameBuffer[0]). 'len' is the bytes available to be written in
156 // 'nameBuffer', including the terminating NUL.
157 void GetName(char* nameBuffer
, size_t len
);
159 // Causes the current thread to sleep until the
160 // number of real-time milliseconds specified have elapsed.
161 void SleepMilliseconds(size_t ms
);
163 } // namespace ThisThread
167 // Platform thread APIs allow passing a single void* argument to the target
168 // thread. This class is responsible for safely ferrying the arg pack and
169 // functor across that void* membrane and running it in the other thread.
170 template <typename F
, typename
... Args
>
171 class ThreadTrampoline
{
172 // The functor to call.
175 // A std::decay copy of the arguments, as specified by std::thread. Using an
176 // rvalue reference for the arguments to Thread and ThreadTrampoline gives us
177 // move semantics for large structures, allowing us to quickly and easily pass
178 // enormous amounts of data to a new thread. Unfortunately, there is a
179 // downside: rvalue references becomes lvalue references when used with POD
180 // types. This becomes dangerous when attempting to pass POD stored on the
181 // stack to the new thread; the rvalue reference will implicitly become an
182 // lvalue reference to the stack location. Thus, the value may not exist if
183 // the parent thread leaves the frame before the read happens in the new
184 // thread. To avoid this dangerous and highly non-obvious footgun, the
185 // standard requires a "decay" copy of the arguments at the cost of making it
186 // impossible to pass references between threads.
187 std::tuple
<std::decay_t
<Args
>...> args
;
189 // Protect the thread id during creation.
190 Mutex createMutex MOZ_UNANNOTATED
;
192 // Thread can access createMutex.
193 friend class js::Thread
;
196 // Note that this template instatiation duplicates and is identical to the
197 // class template instantiation. It is required for perfect forwarding of
198 // rvalue references, which is only enabled for calls to a function template,
199 // even if the class template arguments are correct.
200 template <typename G
, typename
... ArgsT
>
201 explicit ThreadTrampoline(G
&& aG
, ArgsT
&&... aArgsT
)
202 : f(std::forward
<F
>(aG
)),
203 args(std::forward
<Args
>(aArgsT
)...),
204 createMutex(mutexid::ThreadId
) {}
206 static THREAD_RETURN_TYPE THREAD_CALL_API
Start(void* aPack
) {
207 auto* pack
= static_cast<ThreadTrampoline
<F
, Args
...>*>(aPack
);
208 pack
->callMain(std::index_sequence_for
<Args
...>{});
213 template <size_t... Indices
>
214 void callMain(std::index_sequence
<Indices
...>) {
215 // Pretend createMutex is a semaphore and wait for a notification that the
216 // thread that spawned us is ready.
218 createMutex
.unlock();
219 f(std::move(std::get
<Indices
>(args
))...);
223 } // namespace detail
226 #undef THREAD_RETURN_TYPE
228 #endif // threading_Thread_h