tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Thread.h (8837B)


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