| /* | |
| * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. | |
| * | |
| * Use of this source code is governed by a BSD-style license | |
| * that can be found in the LICENSE file in the root of the source | |
| * tree. An additional intellectual property rights grant can be found | |
| * in the file PATENTS. All contributing project authors may | |
| * be found in the AUTHORS file in the root of the source tree. | |
| */ | |
| // Borrowed from Chromium's src/base/threading/thread_checker_unittest.cc. | |
| #include "rtc_base/thread_checker.h" | |
| #include <memory> | |
| #include <utility> | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/constructor_magic.h" | |
| #include "rtc_base/null_socket_server.h" | |
| #include "rtc_base/socket_server.h" | |
| #include "rtc_base/task_queue.h" | |
| #include "rtc_base/thread.h" | |
| #include "test/gtest.h" | |
| // Duplicated from base/threading/thread_checker.h so that we can be | |
| // good citizens there and undef the macro. | |
| #define ENABLE_THREAD_CHECKER RTC_DCHECK_IS_ON | |
| namespace rtc { | |
| namespace { | |
| // Simple class to exercise the basics of ThreadChecker. | |
| // Both the destructor and DoStuff should verify that they were | |
| // called on the same thread as the constructor. | |
| class ThreadCheckerClass : public ThreadChecker { | |
| public: | |
| ThreadCheckerClass() {} | |
| // Verifies that it was called on the same thread as the constructor. | |
| void DoStuff() { RTC_DCHECK(IsCurrent()); } | |
| void Detach() { ThreadChecker::Detach(); } | |
| static void MethodOnDifferentThreadImpl(); | |
| static void DetachThenCallFromDifferentThreadImpl(); | |
| private: | |
| RTC_DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass); | |
| }; | |
| // Calls ThreadCheckerClass::DoStuff on another thread. | |
| class CallDoStuffOnThread : public Thread { | |
| public: | |
| explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class) | |
| : Thread(std::unique_ptr<SocketServer>(new rtc::NullSocketServer())), | |
| thread_checker_class_(thread_checker_class) { | |
| SetName("call_do_stuff_on_thread", nullptr); | |
| } | |
| void Run() override { thread_checker_class_->DoStuff(); } | |
| // New method. Needed since Thread::Join is protected, and it is called by | |
| // the TEST. | |
| void Join() { Thread::Join(); } | |
| private: | |
| ThreadCheckerClass* thread_checker_class_; | |
| RTC_DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread); | |
| }; | |
| // Deletes ThreadCheckerClass on a different thread. | |
| class DeleteThreadCheckerClassOnThread : public Thread { | |
| public: | |
| explicit DeleteThreadCheckerClassOnThread( | |
| std::unique_ptr<ThreadCheckerClass> thread_checker_class) | |
| : Thread(std::unique_ptr<SocketServer>(new rtc::NullSocketServer())), | |
| thread_checker_class_(std::move(thread_checker_class)) { | |
| SetName("delete_thread_checker_class_on_thread", nullptr); | |
| } | |
| void Run() override { thread_checker_class_.reset(); } | |
| // New method. Needed since Thread::Join is protected, and it is called by | |
| // the TEST. | |
| void Join() { Thread::Join(); } | |
| bool has_been_deleted() const { return !thread_checker_class_; } | |
| private: | |
| std::unique_ptr<ThreadCheckerClass> thread_checker_class_; | |
| RTC_DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread); | |
| }; | |
| } // namespace | |
| TEST(ThreadCheckerTest, CallsAllowedOnSameThread) { | |
| std::unique_ptr<ThreadCheckerClass> thread_checker_class( | |
| new ThreadCheckerClass); | |
| // Verify that DoStuff doesn't assert. | |
| thread_checker_class->DoStuff(); | |
| // Verify that the destructor doesn't assert. | |
| thread_checker_class.reset(); | |
| } | |
| TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) { | |
| std::unique_ptr<ThreadCheckerClass> thread_checker_class( | |
| new ThreadCheckerClass); | |
| // Verify that the destructor doesn't assert | |
| // when called on a different thread. | |
| DeleteThreadCheckerClassOnThread delete_on_thread( | |
| std::move(thread_checker_class)); | |
| EXPECT_FALSE(delete_on_thread.has_been_deleted()); | |
| delete_on_thread.Start(); | |
| delete_on_thread.Join(); | |
| EXPECT_TRUE(delete_on_thread.has_been_deleted()); | |
| } | |
| TEST(ThreadCheckerTest, Detach) { | |
| std::unique_ptr<ThreadCheckerClass> thread_checker_class( | |
| new ThreadCheckerClass); | |
| // Verify that DoStuff doesn't assert when called on a different thread after | |
| // a call to Detach. | |
| thread_checker_class->Detach(); | |
| CallDoStuffOnThread call_on_thread(thread_checker_class.get()); | |
| call_on_thread.Start(); | |
| call_on_thread.Join(); | |
| } | |
| #if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER | |
| void ThreadCheckerClass::MethodOnDifferentThreadImpl() { | |
| std::unique_ptr<ThreadCheckerClass> thread_checker_class( | |
| new ThreadCheckerClass); | |
| // DoStuff should assert in debug builds only when called on a | |
| // different thread. | |
| CallDoStuffOnThread call_on_thread(thread_checker_class.get()); | |
| call_on_thread.Start(); | |
| call_on_thread.Join(); | |
| } | |
| #if ENABLE_THREAD_CHECKER | |
| TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) { | |
| ASSERT_DEATH({ ThreadCheckerClass::MethodOnDifferentThreadImpl(); }, ""); | |
| } | |
| #else | |
| TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) { | |
| ThreadCheckerClass::MethodOnDifferentThreadImpl(); | |
| } | |
| #endif // ENABLE_THREAD_CHECKER | |
| void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() { | |
| std::unique_ptr<ThreadCheckerClass> thread_checker_class( | |
| new ThreadCheckerClass); | |
| // DoStuff doesn't assert when called on a different thread | |
| // after a call to Detach. | |
| thread_checker_class->Detach(); | |
| CallDoStuffOnThread call_on_thread(thread_checker_class.get()); | |
| call_on_thread.Start(); | |
| call_on_thread.Join(); | |
| // DoStuff should assert in debug builds only after moving to | |
| // another thread. | |
| thread_checker_class->DoStuff(); | |
| } | |
| #if ENABLE_THREAD_CHECKER | |
| TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) { | |
| ASSERT_DEATH({ ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); }, | |
| ""); | |
| } | |
| #else | |
| TEST(ThreadCheckerTest, DetachFromThreadInRelease) { | |
| ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); | |
| } | |
| #endif // ENABLE_THREAD_CHECKER | |
| #endif // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER | |
| class ThreadAnnotateTest { | |
| public: | |
| // Next two function should create warnings when compile (e.g. if used with | |
| // specific T). | |
| // TODO(danilchap): Find a way to test they do not compile when thread | |
| // annotation checks enabled. | |
| template <typename T> | |
| void access_var_no_annotate() { | |
| var_thread_ = 42; | |
| } | |
| template <typename T> | |
| void access_fun_no_annotate() { | |
| function(); | |
| } | |
| // Functions below should be able to compile. | |
| void access_var_annotate_thread() { | |
| RTC_DCHECK_RUN_ON(thread_); | |
| var_thread_ = 42; | |
| } | |
| void access_var_annotate_checker() { | |
| RTC_DCHECK_RUN_ON(&checker_); | |
| var_checker_ = 44; | |
| } | |
| void access_var_annotate_queue() { | |
| RTC_DCHECK_RUN_ON(queue_); | |
| var_queue_ = 46; | |
| } | |
| void access_fun_annotate() { | |
| RTC_DCHECK_RUN_ON(thread_); | |
| function(); | |
| } | |
| void access_fun_and_var() { | |
| RTC_DCHECK_RUN_ON(thread_); | |
| fun_acccess_var(); | |
| } | |
| private: | |
| void function() RTC_RUN_ON(thread_) {} | |
| void fun_acccess_var() RTC_RUN_ON(thread_) { var_thread_ = 13; } | |
| rtc::Thread* thread_; | |
| rtc::ThreadChecker checker_; | |
| rtc::TaskQueue* queue_; | |
| int var_thread_ RTC_GUARDED_BY(thread_); | |
| int var_checker_ RTC_GUARDED_BY(checker_); | |
| int var_queue_ RTC_GUARDED_BY(queue_); | |
| }; | |
| // Just in case we ever get lumped together with other compilation units. | |
| #undef ENABLE_THREAD_CHECKER | |
| } // namespace rtc |