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/. */
6 
7 #ifndef threading_Thread_h
8 #define threading_Thread_h
9 
10 #include "mozilla/Atomics.h"
11 #include "mozilla/TimeStamp.h"
12 #include "mozilla/Tuple.h"
13 
14 #include <stdint.h>
15 #include <type_traits>
16 #include <utility>
17 
18 #include "js/Initialization.h"
19 #include "js/Utility.h"
20 #include "threading/LockGuard.h"
21 #include "threading/Mutex.h"
22 #include "threading/ThreadId.h"
23 #include "vm/MutexIDs.h"
24 
25 #ifdef XP_WIN
26 #  define THREAD_RETURN_TYPE unsigned int
27 #  define THREAD_CALL_API __stdcall
28 #else
29 #  define THREAD_RETURN_TYPE void*
30 #  define THREAD_CALL_API
31 #endif
32 
33 namespace js {
34 namespace detail {
35 template <typename F, typename... Args>
36 class ThreadTrampoline;
37 }  // namespace detail
38 
39 // Execute the given functor concurrent with the currently executing instruction
40 // stream and within the current address space. Use with care.
41 class Thread {
42  public:
43   // Provides optional parameters to a Thread.
44   class Options {
45     size_t stackSize_;
46 
47    public:
Options()48     Options() : stackSize_(0) {}
49 
setStackSize(size_t sz)50     Options& setStackSize(size_t sz) {
51       stackSize_ = sz;
52       return *this;
53     }
stackSize()54     size_t stackSize() const { return stackSize_; }
55   };
56 
57   // Create a Thread in an initially unjoinable state. A thread of execution can
58   // be created for this Thread by calling |init|. Some of the thread's
59   // properties may be controlled by passing options to this constructor.
60   template <typename O = Options,
61             // SFINAE to make sure we don't try and treat functors for the other
62             // constructor as an Options and vice versa.
63             typename NonConstO = std::remove_const_t<O>,
64             typename DerefO = std::remove_reference_t<NonConstO>,
65             typename = std::enable_if_t<std::is_same_v<DerefO, Options>>>
66   explicit Thread(O&& options = Options())
id_(ThreadId ())67       : id_(ThreadId()), options_(std::forward<O>(options)) {
68     MOZ_ASSERT(isInitialized());
69   }
70 
71   // Start a thread of execution at functor |f| with parameters |args|. This
72   // method will return false if thread creation fails. This Thread must not
73   // already have been created. Note that the arguments must be either POD or
74   // rvalue references (std::move). Attempting to pass a reference will
75   // result in the value being copied, which may not be the intended behavior.
76   // See the comment below on ThreadTrampoline::args for an explanation.
77   template <typename F, typename... Args>
init(F && f,Args &&...args)78   [[nodiscard]] bool init(F&& f, Args&&... args) {
79     MOZ_RELEASE_ASSERT(id_ == ThreadId());
80     using Trampoline = detail::ThreadTrampoline<F, Args...>;
81     auto trampoline =
82         js_new<Trampoline>(std::forward<F>(f), std::forward<Args>(args)...);
83     if (!trampoline) {
84       return false;
85     }
86 
87     // We hold this lock while create() sets the thread id.
88     LockGuard<Mutex> lock(trampoline->createMutex);
89     return create(Trampoline::Start, trampoline);
90   }
91 
92   // The thread must be joined or detached before destruction.
93   ~Thread();
94 
95   // Move the thread into the detached state without blocking. In the detatched
96   // state, the thread continues to run until it exits, but cannot be joined.
97   // After this method returns, this Thread no longer represents a thread of
98   // execution. When the thread exits, its resources will be cleaned up by the
99   // system. At process exit, if the thread is still running, the thread's TLS
100   // storage will be destructed, but the thread stack will *not* be unrolled.
101   void detach();
102 
103   // Block the current thread until this Thread returns from the functor it was
104   // created with. The thread's resources will be cleaned up before this
105   // function returns. After this method returns, this Thread no longer
106   // represents a thread of execution.
107   void join();
108 
109   // Return true if this thread has not yet been joined or detached. If this
110   // method returns false, this Thread does not have an associated thread of
111   // execution, for example, if it has been previously moved or joined.
112   bool joinable();
113 
114   // Returns the id of this thread if this represents a thread of execution or
115   // the default constructed Id() if not. The thread ID is guaranteed to
116   // uniquely identify a thread and can be compared with the == operator.
117   ThreadId get_id();
118 
119   // Allow threads to be moved so that they can be stored in containers.
120   Thread(Thread&& aOther);
121   Thread& operator=(Thread&& aOther);
122 
123  private:
124   // Disallow copy as that's not sensible for unique resources.
125   Thread(const Thread&) = delete;
126   void operator=(const Thread&) = delete;
127 
128   // Provide a process global ID to each thread.
129   ThreadId id_;
130 
131   // Overridable thread creation options.
132   Options options_;
133 
134   // Dispatch to per-platform implementation of thread creation.
135   [[nodiscard]] bool create(THREAD_RETURN_TYPE(THREAD_CALL_API* aMain)(void*),
136                             void* aArg);
137 
138   // An internal version of JS_IsInitialized() that returns whether SpiderMonkey
139   // is currently initialized or is in the process of being initialized.
isInitialized()140   static inline bool isInitialized() {
141     using namespace JS::detail;
142     return libraryInitState == InitState::Initializing ||
143            libraryInitState == InitState::Running;
144   }
145 };
146 
147 namespace ThisThread {
148 
149 // Set the current thread name. Note that setting the thread name may not be
150 // available on all platforms; on these platforms setName() will simply do
151 // nothing.
152 void SetName(const char* name);
153 
154 // Get the current thread name. As with SetName, not available on all
155 // platforms. On these platforms getName() will give back an empty string (by
156 // storing NUL in nameBuffer[0]). 'len' is the bytes available to be written in
157 // 'nameBuffer', including the terminating NUL.
158 void GetName(char* nameBuffer, size_t len);
159 
160 // Causes the current thread to sleep until the
161 // number of real-time milliseconds specified have elapsed.
162 void SleepMilliseconds(size_t ms);
163 
164 }  // namespace ThisThread
165 
166 namespace detail {
167 
168 // Platform thread APIs allow passing a single void* argument to the target
169 // thread. This class is responsible for safely ferrying the arg pack and
170 // functor across that void* membrane and running it in the other thread.
171 template <typename F, typename... Args>
172 class ThreadTrampoline {
173   // The functor to call.
174   F f;
175 
176   // A std::decay copy of the arguments, as specified by std::thread. Using an
177   // rvalue reference for the arguments to Thread and ThreadTrampoline gives us
178   // move semantics for large structures, allowing us to quickly and easily pass
179   // enormous amounts of data to a new thread. Unfortunately, there is a
180   // downside: rvalue references becomes lvalue references when used with POD
181   // types. This becomes dangerous when attempting to pass POD stored on the
182   // stack to the new thread; the rvalue reference will implicitly become an
183   // lvalue reference to the stack location. Thus, the value may not exist if
184   // the parent thread leaves the frame before the read happens in the new
185   // thread. To avoid this dangerous and highly non-obvious footgun, the
186   // standard requires a "decay" copy of the arguments at the cost of making it
187   // impossible to pass references between threads.
188   mozilla::Tuple<std::decay_t<Args>...> args;
189 
190   // Protect the thread id during creation.
191   Mutex createMutex;
192 
193   // Thread can access createMutex.
194   friend class js::Thread;
195 
196  public:
197   // Note that this template instatiation duplicates and is identical to the
198   // class template instantiation. It is required for perfect forwarding of
199   // rvalue references, which is only enabled for calls to a function template,
200   // even if the class template arguments are correct.
201   template <typename G, typename... ArgsT>
ThreadTrampoline(G && aG,ArgsT &&...aArgsT)202   explicit ThreadTrampoline(G&& aG, ArgsT&&... aArgsT)
203       : f(std::forward<F>(aG)),
204         args(std::forward<Args>(aArgsT)...),
205         createMutex(mutexid::ThreadId) {}
206 
Start(void * aPack)207   static THREAD_RETURN_TYPE THREAD_CALL_API Start(void* aPack) {
208     auto* pack = static_cast<ThreadTrampoline<F, Args...>*>(aPack);
209     pack->callMain(std::index_sequence_for<Args...>{});
210     js_delete(pack);
211     return 0;
212   }
213 
214   template <size_t... Indices>
callMain(std::index_sequence<Indices...>)215   void callMain(std::index_sequence<Indices...>) {
216     // Pretend createMutex is a semaphore and wait for a notification that the
217     // thread that spawned us is ready.
218     createMutex.lock();
219     createMutex.unlock();
220     f(mozilla::Get<Indices>(args)...);
221   }
222 };
223 
224 }  // namespace detail
225 }  // namespace js
226 
227 #undef THREAD_RETURN_TYPE
228 
229 #endif  // threading_Thread_h
230