Line data Source code
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 108 : 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:
69 108 : Options() : stackSize_(0) {}
70 :
71 36 : Options& setStackSize(size_t sz) { stackSize_ = sz; return *this; }
72 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 :
82 : 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 36 : explicit Thread(O&& options = Options())
98 : : id_(Id())
99 36 : , options_(mozilla::Forward<O>(options))
100 36 : { }
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>
109 36 : MOZ_MUST_USE bool init(F&& f, Args&&... args) {
110 36 : MOZ_RELEASE_ASSERT(!joinable());
111 : using Trampoline = detail::ThreadTrampoline<F, Args...>;
112 72 : AutoEnterOOMUnsafeRegion oom;
113 36 : auto trampoline = js_new<Trampoline>(mozilla::Forward<F>(f),
114 36 : mozilla::Forward<Args>(args)...);
115 36 : if (!trampoline)
116 0 : oom.crash("js::Thread::init");
117 72 : return create(Trampoline::Start, trampoline);
118 : }
119 :
120 : // The thread must be joined or detached before destruction.
121 144 : ~Thread() {
122 72 : MOZ_RELEASE_ASSERT(!joinable());
123 72 : }
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.
142 108 : bool joinable() const {
143 108 : 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.
149 279 : 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>
219 36 : explicit ThreadTrampoline(G&& aG, ArgsT&&... aArgsT)
220 36 : : f(mozilla::Forward<F>(aG)),
221 36 : args(mozilla::Forward<Args>(aArgsT)...)
222 : {
223 36 : }
224 :
225 36 : static THREAD_RETURN_TYPE THREAD_CALL_API Start(void* aPack) {
226 36 : auto* pack = static_cast<ThreadTrampoline<F, Args...>*>(aPack);
227 36 : pack->callMain(typename mozilla::IndexSequenceFor<Args...>::Type());
228 0 : js_delete(pack);
229 0 : return 0;
230 : }
231 :
232 : template<size_t ...Indices>
233 35 : void callMain(mozilla::IndexSequence<Indices...>) {
234 35 : f(mozilla::Get<Indices>(args)...);
235 0 : }
236 : };
237 :
238 : } // namespace detail
239 : } // namespace js
240 :
241 : #undef THREAD_RETURN_TYPE
242 :
243 : #endif // threading_Thread_h
|