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