Bug 1885489 - Part 5: Add SnapshotIterator::readInt32(). r=iain
[gecko.git] / js / src / threading / Thread.h
blob221ba22b3a442fe1f109a27b74c82ca3c2bc077b
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"
13 #include <stdint.h>
14 #include <type_traits>
15 #include <utility>
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"
24 #ifdef XP_WIN
25 # define THREAD_RETURN_TYPE unsigned int
26 # define THREAD_CALL_API __stdcall
27 #else
28 # define THREAD_RETURN_TYPE void*
29 # define THREAD_CALL_API
30 #endif
32 namespace js {
33 namespace detail {
34 template <typename F, typename... Args>
35 class ThreadTrampoline;
36 } // namespace detail
38 // Execute the given functor concurrent with the currently executing instruction
39 // stream and within the current address space. Use with care.
40 class Thread {
41 public:
42 // Provides optional parameters to a Thread.
43 class Options {
44 size_t stackSize_;
46 public:
47 Options() : stackSize_(0) {}
49 Options& setStackSize(size_t sz) {
50 stackSize_ = sz;
51 return *this;
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 : 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...>;
80 auto trampoline =
81 js_new<Trampoline>(std::forward<F>(f), std::forward<Args>(args)...);
82 if (!trampoline) {
83 return false;
86 bool result;
88 // We hold this lock while create() sets the thread id.
89 LockGuard<Mutex> lock(trampoline->createMutex);
90 result = create(Trampoline::Start, trampoline);
92 if (!result) {
93 // Trampoline should be deleted outside of the above lock.
94 js_delete(trampoline);
95 return false;
97 return true;
100 // The thread must be joined or detached before destruction.
101 ~Thread();
103 // Move the thread into the detached state without blocking. In the detached
104 // state, the thread continues to run until it exits, but cannot be joined.
105 // After this method returns, this Thread no longer represents a thread of
106 // execution. When the thread exits, its resources will be cleaned up by the
107 // system. At process exit, if the thread is still running, the thread's TLS
108 // storage will be destructed, but the thread stack will *not* be unrolled.
109 void detach();
111 // Block the current thread until this Thread returns from the functor it was
112 // created with. The thread's resources will be cleaned up before this
113 // function returns. After this method returns, this Thread no longer
114 // represents a thread of execution.
115 void join();
117 // Return true if this thread has not yet been joined or detached. If this
118 // method returns false, this Thread does not have an associated thread of
119 // execution, for example, if it has been previously moved or joined.
120 bool joinable();
122 // Returns the id of this thread if this represents a thread of execution or
123 // the default constructed Id() if not. The thread ID is guaranteed to
124 // uniquely identify a thread and can be compared with the == operator.
125 ThreadId get_id();
127 // Allow threads to be moved so that they can be stored in containers.
128 Thread(Thread&& aOther);
129 Thread& operator=(Thread&& aOther);
131 private:
132 // Disallow copy as that's not sensible for unique resources.
133 Thread(const Thread&) = delete;
134 void operator=(const Thread&) = delete;
136 // Provide a process global ID to each thread.
137 ThreadId id_;
139 // Overridable thread creation options.
140 Options options_;
142 // Dispatch to per-platform implementation of thread creation.
143 [[nodiscard]] bool create(THREAD_RETURN_TYPE(THREAD_CALL_API* aMain)(void*),
144 void* aArg);
146 // An internal version of JS_IsInitialized() that returns whether SpiderMonkey
147 // is currently initialized or is in the process of being initialized.
148 static inline bool isInitialized() {
149 using namespace JS::detail;
150 return libraryInitState == InitState::Initializing ||
151 libraryInitState == InitState::Running;
155 namespace ThisThread {
157 // Set the current thread name. Note that setting the thread name may not be
158 // available on all platforms; on these platforms setName() will simply do
159 // nothing.
160 void SetName(const char* name);
162 // Get the current thread name. As with SetName, not available on all
163 // platforms. On these platforms getName() will give back an empty string (by
164 // storing NUL in nameBuffer[0]). 'len' is the bytes available to be written in
165 // 'nameBuffer', including the terminating NUL.
166 void GetName(char* nameBuffer, size_t len);
168 // Causes the current thread to sleep until the
169 // number of real-time milliseconds specified have elapsed.
170 void SleepMilliseconds(size_t ms);
172 } // namespace ThisThread
174 namespace detail {
176 // Platform thread APIs allow passing a single void* argument to the target
177 // thread. This class is responsible for safely ferrying the arg pack and
178 // functor across that void* membrane and running it in the other thread.
179 template <typename F, typename... Args>
180 class ThreadTrampoline {
181 // The functor to call.
182 F f;
184 // A std::decay copy of the arguments, as specified by std::thread. Using an
185 // rvalue reference for the arguments to Thread and ThreadTrampoline gives us
186 // move semantics for large structures, allowing us to quickly and easily pass
187 // enormous amounts of data to a new thread. Unfortunately, there is a
188 // downside: rvalue references becomes lvalue references when used with POD
189 // types. This becomes dangerous when attempting to pass POD stored on the
190 // stack to the new thread; the rvalue reference will implicitly become an
191 // lvalue reference to the stack location. Thus, the value may not exist if
192 // the parent thread leaves the frame before the read happens in the new
193 // thread. To avoid this dangerous and highly non-obvious footgun, the
194 // standard requires a "decay" copy of the arguments at the cost of making it
195 // impossible to pass references between threads.
196 std::tuple<std::decay_t<Args>...> args;
198 // Protect the thread id during creation.
199 Mutex createMutex MOZ_UNANNOTATED;
201 // Thread can access createMutex.
202 friend class js::Thread;
204 public:
205 // Note that this template instatiation duplicates and is identical to the
206 // class template instantiation. It is required for perfect forwarding of
207 // rvalue references, which is only enabled for calls to a function template,
208 // even if the class template arguments are correct.
209 template <typename G, typename... ArgsT>
210 explicit ThreadTrampoline(G&& aG, ArgsT&&... aArgsT)
211 : f(std::forward<F>(aG)),
212 args(std::forward<Args>(aArgsT)...),
213 createMutex(mutexid::ThreadId) {}
215 static THREAD_RETURN_TYPE THREAD_CALL_API Start(void* aPack) {
216 auto* pack = static_cast<ThreadTrampoline<F, Args...>*>(aPack);
217 pack->callMain(std::index_sequence_for<Args...>{});
218 js_delete(pack);
219 return 0;
222 template <size_t... Indices>
223 void callMain(std::index_sequence<Indices...>) {
224 // Pretend createMutex is a semaphore and wait for a notification that the
225 // thread that spawned us is ready.
226 createMutex.lock();
227 createMutex.unlock();
228 f(std::move(std::get<Indices>(args))...);
232 } // namespace detail
233 } // namespace js
235 #undef THREAD_RETURN_TYPE
237 #endif // threading_Thread_h