diff options
Diffstat (limited to 'ipc/chromium/src/base/thread.cc')
-rw-r--r-- | ipc/chromium/src/base/thread.cc | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/ipc/chromium/src/base/thread.cc b/ipc/chromium/src/base/thread.cc new file mode 100644 index 0000000000..3fc258af2d --- /dev/null +++ b/ipc/chromium/src/base/thread.cc @@ -0,0 +1,202 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/thread.h" + +#include "base/string_util.h" +#include "base/thread_local.h" +#include "base/waitable_event.h" +#include "GeckoProfiler.h" +#include "mozilla/EventQueue.h" +#include "mozilla/IOInterposer.h" +#include "mozilla/ThreadEventQueue.h" +#include "nsThreadUtils.h" +#include "nsThreadManager.h" + +namespace base { + +// This task is used to trigger the message loop to exit. +class ThreadQuitTask : public mozilla::Runnable { + public: + ThreadQuitTask() : mozilla::Runnable("ThreadQuitTask") {} + NS_IMETHOD Run() override { + MessageLoop::current()->Quit(); + Thread::SetThreadWasQuitProperly(true); + return NS_OK; + } +}; + +// Used to pass data to ThreadMain. This structure is allocated on the stack +// from within StartWithOptions. +struct Thread::StartupData { + // We get away with a const reference here because of how we are allocated. + const Thread::Options& options; + + // Used to synchronize thread startup. + WaitableEvent event; + + explicit StartupData(const Options& opt) + : options(opt), event(false, false) {} +}; + +Thread::Thread(const char* name) + : startup_data_(NULL), + thread_(0), + message_loop_(NULL), + thread_id_(0), + name_(name) { + MOZ_COUNT_CTOR(base::Thread); +} + +Thread::~Thread() { + MOZ_COUNT_DTOR(base::Thread); + Stop(); +} + +namespace { + +// We use this thread-local variable to record whether or not a thread exited +// because its Stop method was called. This allows us to catch cases where +// MessageLoop::Quit() is called directly, which is unexpected when using a +// Thread to setup and run a MessageLoop. + +static base::ThreadLocalBoolean& get_tls_bool() { + static base::ThreadLocalBoolean tls_ptr; + return tls_ptr; +} + +} // namespace + +void Thread::SetThreadWasQuitProperly(bool flag) { get_tls_bool().Set(flag); } + +bool Thread::GetThreadWasQuitProperly() { + bool quit_properly = true; +#ifndef NDEBUG + quit_properly = get_tls_bool().Get(); +#endif + return quit_properly; +} + +bool Thread::Start() { return StartWithOptions(Options()); } + +bool Thread::StartWithOptions(const Options& options) { + DCHECK(!message_loop_); + + SetThreadWasQuitProperly(false); + + StartupData startup_data(options); + startup_data_ = &startup_data; + + if (!PlatformThread::Create(options.stack_size, this, &thread_)) { + DLOG(ERROR) << "failed to create thread"; + startup_data_ = NULL; // Record that we failed to start. + return false; + } + + // Wait for the thread to start and initialize message_loop_ + startup_data.event.Wait(); + + DCHECK(message_loop_); + return true; +} + +void Thread::Stop() { + if (!thread_was_started()) return; + + // We should only be called on the same thread that started us. + DCHECK_NE(thread_id_, PlatformThread::CurrentId()); + + // StopSoon may have already been called. + if (message_loop_) { + RefPtr<ThreadQuitTask> task = new ThreadQuitTask(); + message_loop_->PostTask(task.forget()); + } + + // Wait for the thread to exit. It should already have terminated but make + // sure this assumption is valid. + // + // TODO(darin): Unfortunately, we need to keep message_loop_ around until + // the thread exits. Some consumers are abusing the API. Make them stop. + // + PlatformThread::Join(thread_); + + // The thread can't receive messages anymore. + message_loop_ = NULL; + + // The thread no longer needs to be joined. + startup_data_ = NULL; +} + +void Thread::StopSoon() { + if (!message_loop_) return; + + // We should only be called on the same thread that started us. + DCHECK_NE(thread_id_, PlatformThread::CurrentId()); + + // We had better have a message loop at this point! If we do not, then it + // most likely means that the thread terminated unexpectedly, probably due + // to someone calling Quit() on our message loop directly. + DCHECK(message_loop_); + + RefPtr<ThreadQuitTask> task = new ThreadQuitTask(); + message_loop_->PostTask(task.forget()); +} + +void Thread::ThreadMain() { + nsCOMPtr<nsIThread> xpcomThread; + auto loopType = startup_data_->options.message_loop_type; + if (loopType == MessageLoop::TYPE_MOZILLA_NONMAINTHREAD || + loopType == MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD) { + auto queue = mozilla::MakeRefPtr<mozilla::ThreadEventQueue>( + mozilla::MakeUnique<mozilla::EventQueue>()); + xpcomThread = nsThreadManager::get().CreateCurrentThread( + queue, nsThread::NOT_MAIN_THREAD); + } else { + xpcomThread = NS_GetCurrentThread(); + } + + AUTO_PROFILER_REGISTER_THREAD(name_.c_str()); + mozilla::IOInterposer::RegisterCurrentThread(); + + // The message loop for this thread. + MessageLoop message_loop(startup_data_->options.message_loop_type, + xpcomThread); + + xpcomThread = nullptr; + + // Complete the initialization of our Thread object. + thread_id_ = PlatformThread::CurrentId(); + PlatformThread::SetName(name_.c_str()); + NS_SetCurrentThreadName(name_.c_str()); + message_loop.set_thread_name(name_); + message_loop.set_hang_timeouts(startup_data_->options.transient_hang_timeout, + startup_data_->options.permanent_hang_timeout); + message_loop_ = &message_loop; + + // Let the thread do extra initialization. + // Let's do this before signaling we are started. + Init(); + + startup_data_->event.Signal(); + // startup_data_ can't be touched anymore since the starting thread is now + // unlocked. + + message_loop.Run(); + + // Let the thread do extra cleanup. + CleanUp(); + + // Assert that MessageLoop::Quit was called by ThreadQuitTask. + DCHECK(GetThreadWasQuitProperly()); + + mozilla::IOInterposer::UnregisterCurrentThread(); + + // We can't receive messages anymore. + message_loop_ = NULL; + thread_id_ = 0; +} + +} // namespace base |