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