| // 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 <memory> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/run_loop.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/task/single_thread_task_executor.h" |
| #include "base/third_party/dynamic_annotations/dynamic_annotations.h" |
| #include "base/threading/thread_local.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "build/build_config.h" |
| #include "remoting/base/auto_thread_task_runner.h" |
| |
| #if defined(OS_POSIX) |
| #include "base/files/file_descriptor_watcher_posix.h" |
| #endif |
| |
| #if defined(OS_WIN) |
| #include "base/win/scoped_com_initializer.h" |
| #endif |
| |
| namespace remoting { |
| |
| namespace { |
| |
| #if defined(OS_WIN) |
| std::unique_ptr<base::win::ScopedCOMInitializer> CreateComInitializer( |
| AutoThread::ComInitType type) { |
| std::unique_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; |
| } |
| #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::MessagePumpType pump_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::MessagePumpType type) |
| : pump_type(type), |
| event(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED) {} |
| }; |
| |
| // static |
| scoped_refptr<AutoThreadTaskRunner> AutoThread::CreateWithType( |
| const char* name, |
| scoped_refptr<AutoThreadTaskRunner> joiner, |
| base::MessagePumpType 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::MessagePumpType::DEFAULT); |
| } |
| |
| #if defined(OS_WIN) |
| // static |
| scoped_refptr<AutoThreadTaskRunner> AutoThread::CreateWithLoopAndComInitTypes( |
| const char* name, |
| scoped_refptr<AutoThreadTaskRunner> joiner, |
| base::MessagePumpType pump_type, |
| ComInitType com_init_type) { |
| AutoThread* thread = new AutoThread(name, joiner.get()); |
| thread->SetComInitType(com_init_type); |
| scoped_refptr<AutoThreadTaskRunner> task_runner = |
| thread->StartWithType(pump_type); |
| if (!task_runner.get()) |
| delete thread; |
| return task_runner; |
| } |
| #endif |
| |
| AutoThread::AutoThread(const char* name) |
| : startup_data_(nullptr), |
| #if defined(OS_WIN) |
| com_init_type_(COM_INIT_NONE), |
| #endif |
| thread_(), |
| name_(name), |
| was_quit_properly_(false) { |
| thread_checker_.DetachFromThread(); |
| } |
| |
| AutoThread::AutoThread(const char* name, AutoThreadTaskRunner* joiner) |
| : startup_data_(nullptr), |
| #if defined(OS_WIN) |
| com_init_type_(COM_INIT_NONE), |
| #endif |
| thread_(), |
| name_(name), |
| was_quit_properly_(false), |
| joiner_(joiner) { |
| thread_checker_.DetachFromThread(); |
| } |
| |
| AutoThread::~AutoThread() { |
| DCHECK(!startup_data_); |
| |
| // Wait for the thread to exit. |
| if (!thread_.is_null()) { |
| // TODO(https://crbug.com/id=944316). |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait; |
| base::PlatformThread::Join(thread_); |
| } |
| } |
| |
| scoped_refptr<AutoThreadTaskRunner> AutoThread::StartWithType( |
| base::MessagePumpType type) { |
| DCHECK(thread_.is_null()); |
| #if defined(OS_WIN) |
| DCHECK(com_init_type_ != COM_INIT_STA || type == base::MessagePumpType::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_ = nullptr; |
| return nullptr; |
| } |
| |
| // Wait for the thread to start and initialize single_thread_task_executor |
| // 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. |
| // TODO(https://crbug.com/id=944316). |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope 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_ = nullptr; |
| |
| 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(base::OnceClosure quit_when_idle_closure) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| std::move(quit_when_idle_closure).Run(); |
| was_quit_properly_ = true; |
| |
| if (joiner_.get()) { |
| joiner_->PostTask(FROM_HERE, |
| base::BindOnce(&AutoThread::JoinAndDeleteThread, |
| base::Unretained(this))); |
| } |
| } |
| |
| void AutoThread::JoinAndDeleteThread() { |
| delete this; |
| } |
| |
| void AutoThread::ThreadMain() { |
| // Bind |thread_checker_| to the current thread. |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| base::SingleThreadTaskExecutor single_thread_task_executor( |
| startup_data_->pump_type); |
| base::RunLoop run_loop; |
| |
| // Complete the initialization of our AutoThread object. |
| base::PlatformThread::SetName(name_); |
| ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. |
| |
| // Return an AutoThreadTaskRunner that will cleanly quit this thread when |
| // no more references to it remain. |
| startup_data_->task_runner = new AutoThreadTaskRunner( |
| single_thread_task_executor.task_runner(), |
| base::BindOnce(&AutoThread::QuitThread, base::Unretained(this), |
| run_loop.QuitWhenIdleClosure())); |
| |
| startup_data_->event.Signal(); |
| // startup_data_ can't be touched anymore since the starting thread is now |
| // unlocked. |
| |
| #if defined(OS_POSIX) |
| // Allow threads running a MessageLoopForIO to use FileDescriptorWatcher. |
| std::unique_ptr<base::FileDescriptorWatcher> file_descriptor_watcher; |
| if (single_thread_task_executor.type() == base::MessagePumpType::IO) { |
| file_descriptor_watcher.reset(new base::FileDescriptorWatcher( |
| single_thread_task_executor.task_runner())); |
| } |
| #elif defined(OS_WIN) |
| // Initialize COM on the thread, if requested. |
| std::unique_ptr<base::win::ScopedCOMInitializer> com_initializer( |
| CreateComInitializer(com_init_type_)); |
| #endif |
| |
| run_loop.Run(); |
| |
| // Assert that MessageLoop::QuitWhenIdle was called by AutoThread::QuitThread. |
| DCHECK(was_quit_properly_); |
| } |
| |
| } // namespace base |