tor-browser

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

thread.cc (5919B)


      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 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
      4 // Use of this source code is governed by a BSD-style license that can be
      5 // found in the LICENSE file.
      6 
      7 #include "base/thread.h"
      8 
      9 #include "base/string_util.h"
     10 #include "base/thread_local.h"
     11 #include "base/waitable_event.h"
     12 #include "GeckoProfiler.h"
     13 #include "mozilla/EventQueue.h"
     14 #include "mozilla/IOInterposer.h"
     15 #include "mozilla/ThreadEventQueue.h"
     16 #include "nsThreadUtils.h"
     17 #include "nsThreadManager.h"
     18 
     19 namespace base {
     20 
     21 // This task is used to trigger the message loop to exit.
     22 class ThreadQuitTask : public mozilla::Runnable {
     23 public:
     24  ThreadQuitTask() : mozilla::Runnable("ThreadQuitTask") {}
     25  NS_IMETHOD Run() override {
     26    MessageLoop::current()->Quit();
     27    Thread::SetThreadWasQuitProperly(true);
     28    return NS_OK;
     29  }
     30 };
     31 
     32 // Used to pass data to ThreadMain.  This structure is allocated on the stack
     33 // from within StartWithOptions.
     34 struct Thread::StartupData {
     35  // We get away with a const reference here because of how we are allocated.
     36  const Thread::Options& options;
     37 
     38  // Used to synchronize thread startup.
     39  WaitableEvent event;
     40 
     41  explicit StartupData(const Options& opt)
     42      : options(opt), event(false, false) {}
     43 };
     44 
     45 Thread::Thread(const char* name)
     46    : startup_data_(NULL),
     47      thread_(0),
     48      message_loop_(NULL),
     49      thread_id_(0),
     50      name_(name) {
     51  MOZ_COUNT_CTOR(base::Thread);
     52 }
     53 
     54 Thread::~Thread() {
     55  MOZ_COUNT_DTOR(base::Thread);
     56  Stop();
     57 }
     58 
     59 namespace {
     60 
     61 // We use this thread-local variable to record whether or not a thread exited
     62 // because its Stop method was called.  This allows us to catch cases where
     63 // MessageLoop::Quit() is called directly, which is unexpected when using a
     64 // Thread to setup and run a MessageLoop.
     65 
     66 static base::ThreadLocalBoolean& get_tls_bool() {
     67  static base::ThreadLocalBoolean tls_ptr;
     68  return tls_ptr;
     69 }
     70 
     71 }  // namespace
     72 
     73 void Thread::SetThreadWasQuitProperly(bool flag) { get_tls_bool().Set(flag); }
     74 
     75 bool Thread::GetThreadWasQuitProperly() {
     76  bool quit_properly = true;
     77 #ifndef NDEBUG
     78  quit_properly = get_tls_bool().Get();
     79 #endif
     80  return quit_properly;
     81 }
     82 
     83 bool Thread::Start() { return StartWithOptions(Options()); }
     84 
     85 bool Thread::StartWithOptions(const Options& options) {
     86  DCHECK(!message_loop_);
     87 
     88  SetThreadWasQuitProperly(false);
     89 
     90  StartupData startup_data(options);
     91  startup_data_ = &startup_data;
     92 
     93  if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
     94    DLOG(ERROR) << "failed to create thread";
     95    startup_data_ = NULL;  // Record that we failed to start.
     96    return false;
     97  }
     98 
     99  // Wait for the thread to start and initialize message_loop_
    100  startup_data.event.Wait();
    101 
    102  DCHECK(message_loop_);
    103  return true;
    104 }
    105 
    106 void Thread::Stop() {
    107  if (!thread_was_started()) return;
    108 
    109  // We should only be called on the same thread that started us.
    110  DCHECK_NE(thread_id_, PlatformThread::CurrentId());
    111 
    112  // StopSoon may have already been called.
    113  if (message_loop_) {
    114    RefPtr<ThreadQuitTask> task = new ThreadQuitTask();
    115    message_loop_->PostTask(task.forget());
    116  }
    117 
    118  // Wait for the thread to exit.  It should already have terminated but make
    119  // sure this assumption is valid.
    120  //
    121  // TODO(darin): Unfortunately, we need to keep message_loop_ around until
    122  // the thread exits.  Some consumers are abusing the API.  Make them stop.
    123  //
    124  PlatformThread::Join(thread_);
    125 
    126  // The thread can't receive messages anymore.
    127  message_loop_ = NULL;
    128 
    129  // The thread no longer needs to be joined.
    130  startup_data_ = NULL;
    131 }
    132 
    133 void Thread::StopSoon() {
    134  if (!message_loop_) return;
    135 
    136  // We should only be called on the same thread that started us.
    137  DCHECK_NE(thread_id_, PlatformThread::CurrentId());
    138 
    139  // We had better have a message loop at this point!  If we do not, then it
    140  // most likely means that the thread terminated unexpectedly, probably due
    141  // to someone calling Quit() on our message loop directly.
    142  DCHECK(message_loop_);
    143 
    144  RefPtr<ThreadQuitTask> task = new ThreadQuitTask();
    145  message_loop_->PostTask(task.forget());
    146 }
    147 
    148 void Thread::ThreadMain() {
    149  nsCOMPtr<nsIThread> xpcomThread;
    150  auto loopType = startup_data_->options.message_loop_type;
    151  if (loopType == MessageLoop::TYPE_MOZILLA_NONMAINTHREAD ||
    152      loopType == MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD) {
    153    auto queue = mozilla::MakeRefPtr<mozilla::ThreadEventQueue>(
    154        mozilla::MakeUnique<mozilla::EventQueue>());
    155    xpcomThread = nsThreadManager::get().CreateCurrentThread(queue);
    156  } else {
    157    xpcomThread = NS_GetCurrentThread();
    158  }
    159 
    160  AUTO_PROFILER_REGISTER_THREAD(name_.c_str());
    161  mozilla::IOInterposer::RegisterCurrentThread();
    162 
    163  // The message loop for this thread.
    164  MessageLoop message_loop(startup_data_->options.message_loop_type,
    165                           xpcomThread);
    166 
    167  xpcomThread = nullptr;
    168 
    169  // Complete the initialization of our Thread object.
    170  thread_id_ = PlatformThread::CurrentId();
    171  PlatformThread::SetName(name_.c_str());
    172  NS_SetCurrentThreadName(name_.c_str());
    173  message_loop.set_thread_name(name_);
    174  message_loop.set_hang_timeouts(startup_data_->options.transient_hang_timeout,
    175                                 startup_data_->options.permanent_hang_timeout);
    176  message_loop_ = &message_loop;
    177 
    178  // Let the thread do extra initialization.
    179  // Let's do this before signaling we are started.
    180  Init();
    181 
    182  startup_data_->event.Signal();
    183  // startup_data_ can't be touched anymore since the starting thread is now
    184  // unlocked.
    185 
    186  message_loop.Run();
    187 
    188  // Let the thread do extra cleanup.
    189  CleanUp();
    190 
    191  // Assert that MessageLoop::Quit was called by ThreadQuitTask.
    192  DCHECK(GetThreadWasQuitProperly());
    193 
    194  mozilla::IOInterposer::UnregisterCurrentThread();
    195 
    196  // We can't receive messages anymore.
    197  message_loop_ = NULL;
    198  thread_id_ = 0;
    199 }
    200 
    201 }  // namespace base