sequence_checker_unittest.cc (6868B)
1 /* 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "api/sequence_checker.h" 12 13 #include <functional> 14 #include <memory> 15 16 #include "absl/functional/any_invocable.h" 17 #include "api/function_view.h" 18 #include "api/units/time_delta.h" 19 #include "rtc_base/checks.h" 20 #include "rtc_base/event.h" 21 #include "rtc_base/platform_thread.h" 22 #include "rtc_base/synchronization/sequence_checker_internal.h" 23 #include "rtc_base/task_queue_for_test.h" 24 #include "rtc_base/thread_annotations.h" 25 #include "test/gmock.h" 26 #include "test/gtest.h" 27 28 using testing::HasSubstr; 29 30 namespace webrtc { 31 namespace { 32 33 // This class is dead code, but its purpose is to make sure that 34 // SequenceChecker is compatible with the RTC_GUARDED_BY and RTC_RUN_ON 35 // attributes that are checked at compile-time. 36 class CompileTimeTestForGuardedBy { 37 public: 38 int CalledOnSequence() RTC_RUN_ON(sequence_checker_) { return guarded_; } 39 40 void CallMeFromSequence() { 41 RTC_DCHECK_RUN_ON(&sequence_checker_); 42 guarded_ = 41; 43 } 44 45 private: 46 int guarded_ RTC_GUARDED_BY(sequence_checker_); 47 SequenceChecker sequence_checker_; 48 }; 49 50 void RunOnDifferentThread(FunctionView<void()> run) { 51 Event thread_has_run_event; 52 PlatformThread::SpawnJoinable( 53 [&] { 54 run(); 55 thread_has_run_event.Set(); 56 }, 57 "thread"); 58 EXPECT_TRUE(thread_has_run_event.Wait(TimeDelta::Seconds(1))); 59 } 60 61 } // namespace 62 63 TEST(SequenceCheckerTest, CallsAllowedOnSameThread) { 64 SequenceChecker sequence_checker; 65 EXPECT_TRUE(sequence_checker.IsCurrent()); 66 } 67 68 TEST(SequenceCheckerTest, DestructorAllowedOnDifferentThread) { 69 auto sequence_checker = std::make_unique<SequenceChecker>(); 70 RunOnDifferentThread([&] { 71 // Verify that the destructor doesn't assert when called on a different 72 // thread. 73 sequence_checker.reset(); 74 }); 75 } 76 77 TEST(SequenceCheckerTest, Detach) { 78 SequenceChecker sequence_checker; 79 sequence_checker.Detach(); 80 RunOnDifferentThread([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }); 81 } 82 83 TEST(SequenceCheckerTest, DetachFromThreadAndUseOnTaskQueue) { 84 SequenceChecker sequence_checker; 85 sequence_checker.Detach(); 86 TaskQueueForTest queue; 87 queue.SendTask([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }); 88 } 89 90 TEST(SequenceCheckerTest, InitializeForDifferentTaskQueue) { 91 TaskQueueForTest queue; 92 SequenceChecker sequence_checker(queue.Get()); 93 EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); 94 queue.SendTask([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }); 95 } 96 97 TEST(SequenceCheckerTest, DetachFromTaskQueueAndUseOnThread) { 98 TaskQueueForTest queue; 99 queue.SendTask([] { 100 SequenceChecker sequence_checker; 101 sequence_checker.Detach(); 102 RunOnDifferentThread([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }); 103 }); 104 } 105 106 TEST(SequenceCheckerTest, MethodNotAllowedOnDifferentThreadInDebug) { 107 SequenceChecker sequence_checker; 108 RunOnDifferentThread( 109 [&] { EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); }); 110 } 111 112 #if RTC_DCHECK_IS_ON 113 TEST(SequenceCheckerTest, OnlyCurrentOnOneThread) { 114 SequenceChecker sequence_checker(SequenceChecker::kDetached); 115 RunOnDifferentThread([&] { 116 EXPECT_TRUE(sequence_checker.IsCurrent()); 117 // Spawn a new thread from within the first one to guarantee that we have 118 // two concurrently active threads (and that there's no chance of the 119 // thread ref being reused). 120 RunOnDifferentThread([&] { EXPECT_FALSE(sequence_checker.IsCurrent()); }); 121 }); 122 } 123 #endif 124 125 TEST(SequenceCheckerTest, MethodNotAllowedOnDifferentTaskQueueInDebug) { 126 SequenceChecker sequence_checker; 127 TaskQueueForTest queue; 128 queue.SendTask( 129 [&] { EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); }); 130 } 131 132 TEST(SequenceCheckerTest, DetachFromTaskQueueInDebug) { 133 SequenceChecker sequence_checker; 134 sequence_checker.Detach(); 135 136 TaskQueueForTest queue1; 137 queue1.SendTask([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }); 138 139 // IsCurrent should return false in debug builds after moving to 140 // another task queue. 141 TaskQueueForTest queue2; 142 queue2.SendTask( 143 [&] { EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); }); 144 } 145 146 TEST(SequenceCheckerTest, ExpectationToString) { 147 TaskQueueForTest queue1; 148 149 SequenceChecker sequence_checker(SequenceChecker::kDetached); 150 151 Event blocker; 152 queue1.PostTask([&blocker, &sequence_checker]() { 153 (void)sequence_checker.IsCurrent(); 154 blocker.Set(); 155 }); 156 157 blocker.Wait(Event::kForever); 158 159 #if RTC_DCHECK_IS_ON 160 161 EXPECT_THAT(ExpectationToString(&sequence_checker), 162 HasSubstr("# Expected: TQ:")); 163 164 // Test for the base class 165 webrtc_sequence_checker_internal::SequenceCheckerImpl* sequence_checker_base = 166 &sequence_checker; 167 EXPECT_THAT(ExpectationToString(sequence_checker_base), 168 HasSubstr("# Expected: TQ:")); 169 170 #else 171 GTEST_ASSERT_EQ(ExpectationToString(&sequence_checker), ""); 172 #endif 173 } 174 175 TEST(SequenceCheckerTest, InitiallyDetached) { 176 TaskQueueForTest queue1; 177 178 SequenceChecker sequence_checker(SequenceChecker::kDetached); 179 180 Event blocker; 181 queue1.PostTask([&blocker, &sequence_checker]() { 182 EXPECT_TRUE(sequence_checker.IsCurrent()); 183 blocker.Set(); 184 }); 185 186 blocker.Wait(Event::kForever); 187 188 #if RTC_DCHECK_IS_ON 189 EXPECT_FALSE(sequence_checker.IsCurrent()); 190 #endif 191 } 192 193 class TestAnnotations { 194 public: 195 TestAnnotations() : test_var_(false) {} 196 197 void ModifyTestVar() { 198 RTC_DCHECK_RUN_ON(&checker_); 199 test_var_ = true; 200 } 201 202 private: 203 bool test_var_ RTC_GUARDED_BY(&checker_); 204 SequenceChecker checker_; 205 }; 206 207 TEST(SequenceCheckerTest, TestAnnotations) { 208 TestAnnotations annotations; 209 annotations.ModifyTestVar(); 210 } 211 212 #if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) 213 214 void TestAnnotationsOnWrongQueue() { 215 TestAnnotations annotations; 216 TaskQueueForTest queue; 217 queue.SendTask([&] { annotations.ModifyTestVar(); }); 218 } 219 220 #if RTC_DCHECK_IS_ON 221 // Note: Ending the test suite name with 'DeathTest' is important as it causes 222 // gtest to order this test before any other non-death-tests, to avoid potential 223 // global process state pollution such as shared worker threads being started 224 // (e.g. a side effect of calling InitCocoaMultiThreading() on Mac causes one or 225 // two additional threads to be created). 226 TEST(SequenceCheckerDeathTest, TestAnnotationsOnWrongQueueDebug) { 227 ASSERT_DEATH({ TestAnnotationsOnWrongQueue(); }, ""); 228 } 229 #else 230 TEST(SequenceCheckerTest, TestAnnotationsOnWrongQueueRelease) { 231 TestAnnotationsOnWrongQueue(); 232 } 233 #endif 234 #endif // GTEST_HAS_DEATH_TEST 235 } // namespace webrtc