blob: 2e5006be3143a291091bde897736bb150686dde7 [file] [log] [blame]
// Copyright (c) 2012 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 "remoting/base/auto_thread.h"
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/thread_local.h"
#include "base/threading/thread_restrictions.h"
#include "base/synchronization/waitable_event.h"
#include "remoting/base/auto_thread_task_runner.h"
#if defined(OS_WIN)
#include "base/win/scoped_com_initializer.h"
#endif
namespace remoting {
namespace {
#if defined(OS_WIN)
scoped_ptr<base::win::ScopedCOMInitializer> CreateComInitializer(
AutoThread::ComInitType type) {
scoped_ptr<base::win::ScopedCOMInitializer> initializer;
if (type == AutoThread::COM_INIT_MTA) {
initializer.reset(new base::win::ScopedCOMInitializer(
base::win::ScopedCOMInitializer::kMTA));
} else if (type == AutoThread::COM_INIT_STA) {
initializer.reset(new base::win::ScopedCOMInitializer());
}
return initializer.Pass();
}
#endif
}
// Used to pass data to ThreadMain. This structure is allocated on the stack
// from within StartWithType.
struct AutoThread::StartupData {
// Fields describing the desired thread behaviour.
base::MessageLoop::Type loop_type;
// Used to receive the AutoThreadTaskRunner for the thread.
scoped_refptr<AutoThreadTaskRunner> task_runner;
// Used to synchronize thread startup.
base::WaitableEvent event;
explicit StartupData(base::MessageLoop::Type type)
: loop_type(type), event(false, false) {}
};
// static
scoped_refptr<AutoThreadTaskRunner> AutoThread::CreateWithType(
const char* name,
scoped_refptr<AutoThreadTaskRunner> joiner,
base::MessageLoop::Type type) {
AutoThread* thread = new AutoThread(name, joiner.get());
scoped_refptr<AutoThreadTaskRunner> task_runner = thread->StartWithType(type);
if (!task_runner.get())
delete thread;
return task_runner;
}
// static
scoped_refptr<AutoThreadTaskRunner> AutoThread::Create(
const char* name, scoped_refptr<AutoThreadTaskRunner> joiner) {
return CreateWithType(name, joiner, base::MessageLoop::TYPE_DEFAULT);
}
#if defined(OS_WIN)
// static
scoped_refptr<AutoThreadTaskRunner> AutoThread::CreateWithLoopAndComInitTypes(
const char* name,
scoped_refptr<AutoThreadTaskRunner> joiner,
base::MessageLoop::Type loop_type,
ComInitType com_init_type) {
AutoThread* thread = new AutoThread(name, joiner.get());
thread->SetComInitType(com_init_type);
scoped_refptr<AutoThreadTaskRunner> task_runner =
thread->StartWithType(loop_type);
if (!task_runner.get())
delete thread;
return task_runner;
}
#endif
AutoThread::AutoThread(const char* name)
: startup_data_(NULL),
#if defined(OS_WIN)
com_init_type_(COM_INIT_NONE),
#endif
thread_(),
name_(name),
was_quit_properly_(false) {
}
AutoThread::AutoThread(const char* name, AutoThreadTaskRunner* joiner)
: startup_data_(NULL),
#if defined(OS_WIN)
com_init_type_(COM_INIT_NONE),
#endif
thread_(),
name_(name),
was_quit_properly_(false),
joiner_(joiner) {
}
AutoThread::~AutoThread() {
DCHECK(!startup_data_);
// Wait for the thread to exit.
if (!thread_.is_null()) {
base::PlatformThread::Join(thread_);
}
}
scoped_refptr<AutoThreadTaskRunner> AutoThread::StartWithType(
base::MessageLoop::Type type) {
DCHECK(thread_.is_null());
#if defined(OS_WIN)
DCHECK(com_init_type_ != COM_INIT_STA || type == base::MessageLoop::TYPE_UI);
#endif
StartupData startup_data(type);
startup_data_ = &startup_data;
if (!base::PlatformThread::Create(0, this, &thread_)) {
DLOG(ERROR) << "failed to create thread";
startup_data_ = NULL;
return NULL;
}
// Wait for the thread to start and initialize message_loop_
// TODO(wez): Since at this point we know the MessageLoop _will_ run, and
// the thread lifetime is controlled by the AutoThreadTaskRunner, we would
// ideally return the AutoThreadTaskRunner to the caller without waiting for
// the thread to signal us.
base::ThreadRestrictions::ScopedAllowWait allow_wait;
startup_data.event.Wait();
// set it to NULL so we don't keep a pointer to some object on the stack.
startup_data_ = NULL;
DCHECK(startup_data.task_runner.get());
return startup_data.task_runner;
}
#if defined(OS_WIN)
void AutoThread::SetComInitType(ComInitType com_init_type) {
DCHECK_EQ(com_init_type_, COM_INIT_NONE);
com_init_type_ = com_init_type;
}
#endif
void AutoThread::QuitThread(
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
if (!task_runner->BelongsToCurrentThread()) {
task_runner->PostTask(FROM_HERE, base::Bind(&AutoThread::QuitThread,
base::Unretained(this),
task_runner));
return;
}
base::MessageLoop::current()->Quit();
was_quit_properly_ = true;
if (joiner_.get()) {
joiner_->PostTask(
FROM_HERE,
base::Bind(&AutoThread::JoinAndDeleteThread, base::Unretained(this)));
}
}
void AutoThread::JoinAndDeleteThread() {
delete this;
}
void AutoThread::ThreadMain() {
// The message loop for this thread.
base::MessageLoop message_loop(startup_data_->loop_type);
// Complete the initialization of our AutoThread object.
base::PlatformThread::SetName(name_.c_str());
ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector.
message_loop.set_thread_name(name_);
// Return an AutoThreadTaskRunner that will cleanly quit this thread when
// no more references to it remain.
startup_data_->task_runner =
new AutoThreadTaskRunner(message_loop.message_loop_proxy(),
base::Bind(&AutoThread::QuitThread,
base::Unretained(this),
message_loop.message_loop_proxy()));
startup_data_->event.Signal();
// startup_data_ can't be touched anymore since the starting thread is now
// unlocked.
#if defined(OS_WIN)
// Initialize COM on the thread, if requested.
scoped_ptr<base::win::ScopedCOMInitializer> com_initializer(
CreateComInitializer(com_init_type_));
#endif
message_loop.Run();
// Assert that MessageLoop::Quit was called by AutoThread::QuitThread.
DCHECK(was_quit_properly_);
}
} // namespace base