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