summaryrefslogtreecommitdiffstats
path: root/js/src/threading/Thread.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/threading/Thread.h')
-rw-r--r--js/src/threading/Thread.h243
1 files changed, 243 insertions, 0 deletions
diff --git a/js/src/threading/Thread.h b/js/src/threading/Thread.h
new file mode 100644
index 000000000..2ea445e7d
--- /dev/null
+++ b/js/src/threading/Thread.h
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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>
+ MOZ_MUST_USE 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.
+ MOZ_MUST_USE 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