diff options
author | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-28 09:06:09 +0000 |
---|---|---|
committer | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-28 09:06:09 +0000 |
commit | 6d64642d4e1000f2d21f290aebee952cc61c6375 (patch) | |
tree | b3323b629cfa9a646171118438dda49e789643d2 /remoting/base | |
parent | eeaa7c2ae03465818725e7509ec192c0a6b1c279 (diff) | |
download | chromium_src-6d64642d4e1000f2d21f290aebee952cc61c6375.zip chromium_src-6d64642d4e1000f2d21f290aebee952cc61c6375.tar.gz chromium_src-6d64642d4e1000f2d21f290aebee952cc61c6375.tar.bz2 |
Add COM initialization support to AutoThread.
This CL adds SetComInitType(), which can be used to configure an
AutoThread for a single- or multi-threaded COM apartment before
calling Start(), and a creator function providing equivalent
capability.
This is a re-land of crrev.com/11348087.
BUG=145856
TBR=alexeypa
Review URL: https://chromiumcodereview.appspot.com/11413193
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@169886 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/base')
-rw-r--r-- | remoting/base/auto_thread.cc | 69 | ||||
-rw-r--r-- | remoting/base/auto_thread.h | 25 | ||||
-rw-r--r-- | remoting/base/auto_thread_unittest.cc | 86 |
3 files changed, 169 insertions, 11 deletions
diff --git a/remoting/base/auto_thread.cc b/remoting/base/auto_thread.cc index 324ccc3..9f51378 100644 --- a/remoting/base/auto_thread.cc +++ b/remoting/base/auto_thread.cc @@ -12,11 +12,34 @@ #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. MessageLoop::Type loop_type; // Used to receive the AutoThreadTaskRunner for the thread. @@ -37,12 +60,9 @@ scoped_refptr<AutoThreadTaskRunner> AutoThread::CreateWithType( MessageLoop::Type type) { AutoThread* thread = new AutoThread(name, joiner); scoped_refptr<AutoThreadTaskRunner> task_runner = thread->StartWithType(type); - if (task_runner.get()) { - return task_runner; - } else { + if (!task_runner) delete thread; - return NULL; - } + return task_runner; } // static @@ -51,8 +71,28 @@ scoped_refptr<AutoThreadTaskRunner> AutoThread::Create( return CreateWithType(name, joiner, MessageLoop::TYPE_DEFAULT); } +#if defined(OS_WIN) +// static +scoped_refptr<AutoThreadTaskRunner> AutoThread::CreateWithLoopAndComInitTypes( + const char* name, + scoped_refptr<AutoThreadTaskRunner> joiner, + MessageLoop::Type loop_type, + ComInitType com_init_type) { + AutoThread* thread = new AutoThread(name, joiner); + thread->SetComInitType(com_init_type); + scoped_refptr<AutoThreadTaskRunner> task_runner = + thread->StartWithType(loop_type); + if (!task_runner) + 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_(0), name_(name), was_quit_properly_(false) { @@ -60,6 +100,9 @@ AutoThread::AutoThread(const char* name) AutoThread::AutoThread(const char* name, AutoThreadTaskRunner* joiner) : startup_data_(NULL), +#if defined(OS_WIN) + com_init_type_(COM_INIT_NONE), +#endif thread_(0), name_(name), was_quit_properly_(false), @@ -78,6 +121,9 @@ AutoThread::~AutoThread() { scoped_refptr<AutoThreadTaskRunner> AutoThread::StartWithType(MessageLoop::Type type) { DCHECK(!thread_); +#if defined(OS_WIN) + DCHECK(com_init_type_ != COM_INIT_STA || type == MessageLoop::TYPE_UI); +#endif StartupData startup_data(type); startup_data_ = &startup_data; @@ -103,9 +149,12 @@ AutoThread::StartWithType(MessageLoop::Type type) { return startup_data.task_runner; } -scoped_refptr<AutoThreadTaskRunner> AutoThread::Start() { - return StartWithType(MessageLoop::TYPE_DEFAULT); +#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) { @@ -150,6 +199,12 @@ void AutoThread::ThreadMain() { // 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. diff --git a/remoting/base/auto_thread.h b/remoting/base/auto_thread.h index 4a34976..6242839 100644 --- a/remoting/base/auto_thread.h +++ b/remoting/base/auto_thread.h @@ -40,13 +40,24 @@ class AutoThread : base::PlatformThread::Delegate { const char* name, scoped_refptr<AutoThreadTaskRunner> joiner); +#if defined(OS_WIN) + // Create an AutoThread initialized for COM. |com_init_type| specifies the + // type of COM apartment to initialize. + enum ComInitType { COM_INIT_NONE, COM_INIT_STA, COM_INIT_MTA }; + static scoped_refptr<AutoThreadTaskRunner> CreateWithLoopAndComInitTypes( + const char* name, + scoped_refptr<AutoThreadTaskRunner> joiner, + MessageLoop::Type loop_type, + ComInitType com_init_type); +#endif + // Construct the AutoThread. |name| identifies the thread for debugging. explicit AutoThread(const char* name); // Waits for the thread to exit, and then destroys it. virtual ~AutoThread(); - // Starts a the thread, running the specified type of MessageLoop. Returns + // Starts the thread, running the specified type of MessageLoop. Returns // an AutoThreadTaskRunner through which tasks may be posted to the thread // if successful, or NULL on failure. // @@ -58,8 +69,11 @@ class AutoThread : base::PlatformThread::Delegate { // thread will exit when no references to the TaskRunner remain. scoped_refptr<AutoThreadTaskRunner> StartWithType(MessageLoop::Type type); - // Shorthand for StartWithType(MessageLoop::TYPE_DEFAULT). - scoped_refptr<AutoThreadTaskRunner> Start(); +#if defined(OS_WIN) + // Configures the thread to initialize the specified COM apartment type. + // SetComInitType() must be called before Start(). + void SetComInitType(ComInitType com_init_type); +#endif private: AutoThread(const char* name, AutoThreadTaskRunner* joiner); @@ -74,6 +88,11 @@ class AutoThread : base::PlatformThread::Delegate { struct StartupData; StartupData* startup_data_; +#if defined(OS_WIN) + // Specifies which kind of COM apartment to initialize, if any. + ComInitType com_init_type_; +#endif + // The thread's handle. base::PlatformThreadHandle thread_; diff --git a/remoting/base/auto_thread_unittest.cc b/remoting/base/auto_thread_unittest.cc index 65f4aa5..25bdd03 100644 --- a/remoting/base/auto_thread_unittest.cc +++ b/remoting/base/auto_thread_unittest.cc @@ -3,10 +3,17 @@ // found in the LICENSE file. #include "base/bind.h" +#include "base/file_path.h" #include "base/memory/ref_counted.h" +#include "base/scoped_native_library.h" #include "remoting/base/auto_thread.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_WIN) +#include <objbase.h> +#include "base/win/windows_version.h" +#endif + namespace { const char kThreadName[] = "Test thread"; @@ -22,6 +29,30 @@ void PostSetFlagTask( task_runner->PostTask(FROM_HERE, base::Bind(&SetFlagTask, success)); } +#if defined(OS_WIN) +void CheckComAptTypeTask(APTTYPE* apt_type_out, HRESULT* hresult) { + typedef HRESULT (WINAPI * CoGetApartmentTypeFunc) + (APTTYPE*, APTTYPEQUALIFIER*); + + // CoGetApartmentType requires Windows 7 or above. + if (base::win::GetVersion() < base::win::VERSION_WIN7) { + *hresult = E_NOTIMPL; + return; + } + + // Dynamic link to the API so the same test binary can run on older systems. + base::ScopedNativeLibrary com_library(FilePath(L"ole32.dll")); + ASSERT_TRUE(com_library.is_valid()); + CoGetApartmentTypeFunc co_get_apartment_type = + static_cast<CoGetApartmentTypeFunc>( + com_library.GetFunctionPointer("CoGetApartmentType")); + ASSERT_TRUE(co_get_apartment_type != NULL); + + APTTYPEQUALIFIER apt_type_qualifier; + *hresult = (*co_get_apartment_type)(apt_type_out, &apt_type_qualifier); +} +#endif + } // namespace namespace remoting { @@ -59,7 +90,6 @@ class AutoThreadTest : public testing::Test { message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure()); } - MessageLoop message_loop_; bool message_loop_quit_correctly_; scoped_refptr<AutoThreadTaskRunner> main_task_runner_; @@ -112,4 +142,58 @@ TEST_F(AutoThreadTest, ThreadDependency) { EXPECT_TRUE(success); } +#if defined(OS_WIN) +TEST_F(AutoThreadTest, ThreadWithComMta) { + scoped_refptr<base::TaskRunner> task_runner = + AutoThread::CreateWithLoopAndComInitTypes( + kThreadName, main_task_runner_, MessageLoop::TYPE_DEFAULT, + AutoThread::COM_INIT_MTA); + EXPECT_TRUE(task_runner.get()); + + // Post a task to query the COM apartment type. + HRESULT hresult = E_FAIL; + APTTYPE apt_type = APTTYPE_NA; + task_runner->PostTask(FROM_HERE, + base::Bind(&CheckComAptTypeTask, &apt_type, &hresult)); + + task_runner = NULL; + RunMessageLoop(); + + // CoGetApartmentType requires Windows 7 or above. + if (base::win::GetVersion() >= base::win::VERSION_WIN7) { + EXPECT_EQ(S_OK, hresult); + EXPECT_EQ(APTTYPE_MTA, apt_type); + } else { + EXPECT_EQ(E_NOTIMPL, hresult); + } +} + +TEST_F(AutoThreadTest, ThreadWithComSta) { + scoped_refptr<base::TaskRunner> task_runner = + AutoThread::CreateWithLoopAndComInitTypes( + kThreadName, main_task_runner_, MessageLoop::TYPE_UI, + AutoThread::COM_INIT_STA); + EXPECT_TRUE(task_runner.get()); + + // Post a task to query the COM apartment type. + HRESULT hresult = E_FAIL; + APTTYPE apt_type = APTTYPE_NA; + task_runner->PostTask(FROM_HERE, + base::Bind(&CheckComAptTypeTask, &apt_type, &hresult)); + + task_runner = NULL; + RunMessageLoop(); + + // CoGetApartmentType requires Windows 7 or above. + if (base::win::GetVersion() >= base::win::VERSION_WIN7) { + EXPECT_EQ(S_OK, hresult); + // Whether the thread is the "main" STA apartment depends upon previous + // COM activity in this test process, so allow both types here. + EXPECT_TRUE(apt_type == APTTYPE_MAINSTA || apt_type == APTTYPE_STA); + } else { + EXPECT_EQ(E_NOTIMPL, hresult); + } +} +#endif // defined(OS_WIN) + } // namespace remoting |