summaryrefslogtreecommitdiff
path: root/js/src/threading/Thread.h
blob: 7d9f56040758a57c108d4a4b8c96a387e5fb534c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef threading_Thread_h
#define threading_Thread_h

#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/IndexSequence.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Tuple.h"

#include <stdint.h>

#include "js/Utility.h"

#ifdef XP_WIN
# define THREAD_RETURN_TYPE unsigned int
# define THREAD_CALL_API __stdcall
#else
# define THREAD_RETURN_TYPE void*
# define THREAD_CALL_API
#endif

namespace js {
namespace detail {
template <typename F, typename... Args>
class ThreadTrampoline;
} // namespace detail

// Execute the given functor concurrent with the currently executing instruction
// stream and within the current address space. Use with care.
class Thread
{
public:
  struct Hasher;

  class Id
  {
    friend struct Hasher;
    class PlatformData;
    void* platformData_[2];

  public:
    Id();

    Id(const Id&) = default;
    Id(Id&&) = default;
    Id& operator=(const Id&) = default;
    Id& operator=(Id&&) = default;

    bool operator==(const Id& aOther) const;
    bool operator!=(const Id& aOther) const { return !operator==(aOther); }

    inline PlatformData* platformData();
    inline const PlatformData* platformData() const;
  };

  // Provides optional parameters to a Thread.
  class Options
  {
    size_t stackSize_;

  public:
    Options() : stackSize_(0) {}

    Options& setStackSize(size_t sz) { stackSize_ = sz; return *this; }
    size_t stackSize() const { return stackSize_; }
  };

  // A js::HashTable hash policy for keying hash tables by js::Thread::Id.
  struct Hasher
  {
    typedef Id Lookup;

    static HashNumber hash(const Lookup& l);

    static bool match(const Id& key, const Lookup& lookup) {
      return key == lookup;
    }
  };

  // Create a Thread in an initially unjoinable state. A thread of execution can
  // be created for this Thread by calling |init|. Some of the thread's
  // properties may be controlled by passing options to this constructor.
  template <typename O = Options,
            // SFINAE to make sure we don't try and treat functors for the other
            // constructor as an Options and vice versa.
            typename NonConstO = typename mozilla::RemoveConst<O>::Type,
            typename DerefO = typename mozilla::RemoveReference<NonConstO>::Type,
            typename = typename mozilla::EnableIf<mozilla::IsSame<DerefO, Options>::value,
                                                  void*>::Type>
  explicit Thread(O&& options = Options())
    : id_(Id())
    , options_(mozilla::Forward<O>(options))
  { }

  // Start a thread of execution at functor |f| with parameters |args|. This
  // method will return false if thread creation fails. This Thread must not
  // already have been created. Note that the arguments must be either POD or
  // rvalue references (mozilla::Move). Attempting to pass a reference will
  // result in the value being copied, which may not be the intended behavior.
  // See the comment below on ThreadTrampoline::args for an explanation.
  template <typename F, typename... Args>
  [[nodiscard]] bool init(F&& f, Args&&... args) {
    MOZ_RELEASE_ASSERT(!joinable());
    using Trampoline = detail::ThreadTrampoline<F, Args...>;
    AutoEnterOOMUnsafeRegion oom;
    auto trampoline = js_new<Trampoline>(mozilla::Forward<F>(f),
                                         mozilla::Forward<Args>(args)...);
    if (!trampoline)
      oom.crash("js::Thread::init");
    return create(Trampoline::Start, trampoline);
  }

  // The thread must be joined or detached before destruction.
  ~Thread() {
    MOZ_RELEASE_ASSERT(!joinable());
  }

  // Move the thread into the detached state without blocking. In the detatched
  // state, the thread continues to run until it exits, but cannot be joined.
  // After this method returns, this Thread no longer represents a thread of
  // execution. When the thread exits, its resources will be cleaned up by the
  // system. At process exit, if the thread is still running, the thread's TLS
  // storage will be destructed, but the thread stack will *not* be unrolled.
  void detach();

  // Block the current thread until this Thread returns from the functor it was
  // created with. The thread's resources will be cleaned up before this
  // function returns. After this method returns, this Thread no longer
  // represents a thread of execution.
  void join();

  // Return true if this thread has not yet been joined or detached. If this
  // method returns false, this Thread does not have an associated thread of
  // execution, for example, if it has been previously moved or joined.
  bool joinable() const {
    return get_id() != Id();
  }

  // Returns the id of this thread if this represents a thread of execution or
  // the default constructed Id() if not. The thread ID is guaranteed to
  // uniquely identify a thread and can be compared with the == operator.
  Id get_id() const { return id_; }

  // Allow threads to be moved so that they can be stored in containers.
  Thread(Thread&& aOther);
  Thread& operator=(Thread&& aOther);

private:
  // Disallow copy as that's not sensible for unique resources.
  Thread(const Thread&) = delete;
  void operator=(const Thread&) = delete;

  // Provide a process global ID to each thread.
  Id id_;

  // Overridable thread creation options.
  Options options_;

  // Dispatch to per-platform implementation of thread creation.
  [[nodiscard]] bool create(THREAD_RETURN_TYPE (THREAD_CALL_API *aMain)(void*), void* aArg);
};

namespace ThisThread {

// Return the thread id of the calling thread.
Thread::Id GetId();

// Set the current thread name. Note that setting the thread name may not be
// available on all platforms; on these platforms setName() will simply do
// nothing.
void SetName(const char* name);

// Get the current thread name. As with SetName, not available on all
// platforms. On these platforms getName() will give back an empty string (by
// storing NUL in nameBuffer[0]). 'len' is the bytes available to be written in
// 'nameBuffer', including the terminating NUL.
void GetName(char* nameBuffer, size_t len);

} // namespace ThisThread

namespace detail {

// Platform thread APIs allow passing a single void* argument to the target
// thread. This class is responsible for safely ferrying the arg pack and
// functor across that void* membrane and running it in the other thread.
template <typename F, typename... Args>
class ThreadTrampoline
{
  // The functor to call.
  F f;

  // A std::decay copy of the arguments, as specified by std::thread. Using an
  // rvalue reference for the arguments to Thread and ThreadTrampoline gives us
  // move semantics for large structures, allowing us to quickly and easily pass
  // enormous amounts of data to a new thread. Unfortunately, there is a
  // downside: rvalue references becomes lvalue references when used with POD
  // types. This becomes dangerous when attempting to pass POD stored on the
  // stack to the new thread; the rvalue reference will implicitly become an
  // lvalue reference to the stack location. Thus, the value may not exist if
  // the parent thread leaves the frame before the read happens in the new
  // thread. To avoid this dangerous and highly non-obvious footgun, the
  // standard requires a "decay" copy of the arguments at the cost of making it
  // impossible to pass references between threads.
  mozilla::Tuple<typename mozilla::Decay<Args>::Type...> args;

public:
  // Note that this template instatiation duplicates and is identical to the
  // class template instantiation. It is required for perfect forwarding of
  // rvalue references, which is only enabled for calls to a function template,
  // even if the class template arguments are correct.
  template <typename G, typename... ArgsT>
  explicit ThreadTrampoline(G&& aG, ArgsT&&... aArgsT)
    : f(mozilla::Forward<F>(aG)),
      args(mozilla::Forward<Args>(aArgsT)...)
  {
  }

  static THREAD_RETURN_TYPE THREAD_CALL_API Start(void* aPack) {
    auto* pack = static_cast<ThreadTrampoline<F, Args...>*>(aPack);
    pack->callMain(typename mozilla::IndexSequenceFor<Args...>::Type());
    js_delete(pack);
    return 0;
  }

  template<size_t ...Indices>
  void callMain(mozilla::IndexSequence<Indices...>) {
    f(mozilla::Get<Indices>(args)...);
  }
};

} // namespace detail
} // namespace js

#undef THREAD_RETURN_TYPE

#endif // threading_Thread_h