diff --git a/webrtc/base/platform_thread.cc b/webrtc/base/platform_thread.cc index b6fd8732aa..286bee95f1 100644 --- a/webrtc/base/platform_thread.cc +++ b/webrtc/base/platform_thread.cc @@ -99,7 +99,8 @@ PlatformThread::PlatformThread(ThreadRunFunction func, name_(thread_name ? thread_name : "webrtc"), #if defined(WEBRTC_WIN) stop_(false), - thread_(NULL) { + thread_(NULL), + thread_id_(0) { #else stop_event_(false, false), thread_(0) { @@ -112,6 +113,7 @@ PlatformThread::~PlatformThread() { RTC_DCHECK(thread_checker_.CalledOnValidThread()); #if defined(WEBRTC_WIN) RTC_DCHECK(!thread_); + RTC_DCHECK(!thread_id_); #endif // defined(WEBRTC_WIN) } @@ -136,10 +138,10 @@ void PlatformThread::Start() { // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION. // Set the reserved stack stack size to 1M, which is the default on Windows // and Linux. - DWORD thread_id; thread_ = ::CreateThread(NULL, 1024 * 1024, &StartThread, this, - STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id); + STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id_); RTC_CHECK(thread_) << "CreateThread failed"; + RTC_DCHECK(thread_id_); #else ThreadAttributes attr; // Set the stack stack size to 1M. @@ -157,6 +159,14 @@ bool PlatformThread::IsRunning() const { #endif // defined(WEBRTC_WIN) } +PlatformThreadRef PlatformThread::GetThreadRef() const { +#if defined(WEBRTC_WIN) + return thread_id_; +#else + return thread_; +#endif // defined(WEBRTC_WIN) +} + void PlatformThread::Stop() { RTC_DCHECK(thread_checker_.CalledOnValidThread()); if (!IsRunning()) @@ -164,10 +174,13 @@ void PlatformThread::Stop() { #if defined(WEBRTC_WIN) // Set stop_ to |true| on the worker thread. - QueueUserAPC(&RaiseFlag, thread_, reinterpret_cast(&stop_)); + bool queued = QueueAPC(&RaiseFlag, reinterpret_cast(&stop_)); + // Queuing the APC can fail if the thread is being terminated. + RTC_CHECK(queued || GetLastError() == ERROR_GEN_FAILURE); WaitForSingleObject(thread_, INFINITE); CloseHandle(thread_); thread_ = nullptr; + thread_id_ = 0; #else stop_event_.Set(); RTC_CHECK_EQ(0, pthread_join(thread_, nullptr)); @@ -247,4 +260,13 @@ bool PlatformThread::SetPriority(ThreadPriority priority) { #endif // defined(WEBRTC_WIN) } +#if defined(WEBRTC_WIN) +bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) { + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + RTC_DCHECK(IsRunning()); + + return QueueUserAPC(function, thread_, data) != FALSE; +} +#endif + } // namespace rtc diff --git a/webrtc/base/platform_thread.h b/webrtc/base/platform_thread.h index 53465e4b17..8c2161baa8 100644 --- a/webrtc/base/platform_thread.h +++ b/webrtc/base/platform_thread.h @@ -59,18 +59,30 @@ class PlatformThread { PlatformThread(ThreadRunFunction func, void* obj, const char* thread_name); virtual ~PlatformThread(); + const std::string& name() const { return name_; } + // Spawns a thread and tries to set thread priority according to the priority // from when CreateThread was called. void Start(); bool IsRunning() const; + // Returns an identifier for the worker thread that can be used to do + // thread checks. + PlatformThreadRef GetThreadRef() const; + // Stops (joins) the spawned thread. void Stop(); // Set the priority of the thread. Must be called when thread is running. bool SetPriority(ThreadPriority priority); + protected: +#if defined(WEBRTC_WIN) + // Exposed to derived classes to allow for special cases specific to Windows. + bool QueueAPC(PAPCFUNC apc_function, ULONG_PTR data); +#endif + private: void Run(); @@ -85,6 +97,7 @@ class PlatformThread { bool stop_; HANDLE thread_; + DWORD thread_id_; #else static void* StartThread(void* param); diff --git a/webrtc/base/platform_thread_unittest.cc b/webrtc/base/platform_thread_unittest.cc index f9db8e34a3..d06a738a33 100644 --- a/webrtc/base/platform_thread_unittest.cc +++ b/webrtc/base/platform_thread_unittest.cc @@ -14,31 +14,47 @@ #include "webrtc/base/scoped_ptr.h" #include "webrtc/system_wrappers/include/sleep.h" -namespace webrtc { - +namespace rtc { +namespace { // Function that does nothing, and reports success. bool NullRunFunction(void* obj) { - SleepMs(0); // Hand over timeslice, prevents busy looping. + webrtc::SleepMs(0); // Hand over timeslice, prevents busy looping. return true; } -TEST(PlatformThreadTest, StartStop) { - rtc::PlatformThread thread(&NullRunFunction, nullptr, "PlatformThreadTest"); - thread.Start(); - thread.Stop(); -} - // Function that sets a boolean. bool SetFlagRunFunction(void* obj) { bool* obj_as_bool = static_cast(obj); *obj_as_bool = true; - SleepMs(0); // Hand over timeslice, prevents busy looping. + webrtc::SleepMs(0); // Hand over timeslice, prevents busy looping. return true; } +} // namespace + +TEST(PlatformThreadTest, StartStop) { + PlatformThread thread(&NullRunFunction, nullptr, "PlatformThreadTest"); + EXPECT_TRUE(thread.name() == "PlatformThreadTest"); + EXPECT_TRUE(thread.GetThreadRef() == 0); + thread.Start(); + EXPECT_TRUE(thread.GetThreadRef() != 0); + thread.Stop(); + EXPECT_TRUE(thread.GetThreadRef() == 0); +} + +TEST(PlatformThreadTest, StartStop2) { + PlatformThread thread1(&NullRunFunction, nullptr, "PlatformThreadTest1"); + PlatformThread thread2(&NullRunFunction, nullptr, "PlatformThreadTest2"); + EXPECT_TRUE(thread1.GetThreadRef() == thread2.GetThreadRef()); + thread1.Start(); + thread2.Start(); + EXPECT_TRUE(thread1.GetThreadRef() != thread2.GetThreadRef()); + thread2.Stop(); + thread1.Stop(); +} TEST(PlatformThreadTest, RunFunctionIsCalled) { bool flag = false; - rtc::PlatformThread thread(&SetFlagRunFunction, &flag, "RunFunctionIsCalled"); + PlatformThread thread(&SetFlagRunFunction, &flag, "RunFunctionIsCalled"); thread.Start(); // At this point, the flag may be either true or false. @@ -47,5 +63,4 @@ TEST(PlatformThreadTest, RunFunctionIsCalled) { // We expect the thread to have run at least once. EXPECT_TRUE(flag); } - -} // namespace webrtc +} // rtc