blob: 88fd90aef89cf2e6bfe5c36ad73db3d266a06db5 [file] [log] [blame]
sprang53cf3462016-03-22 01:51:39 -07001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "system_wrappers/source/event_timer_posix.h"
sprang53cf3462016-03-22 01:51:39 -070012
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "rtc_base/criticalsection.h"
14#include "rtc_base/event.h"
15#include "test/gtest.h"
sprang53cf3462016-03-22 01:51:39 -070016
17namespace webrtc {
18
19enum class ThreadState {
20 kNotStarted,
21 kWaiting,
22 kRequestProcessCall,
23 kCallingProcess,
24 kProcessDone,
25 kContinue,
26 kExiting,
27 kDead
28};
29
30class EventTimerPosixTest : public testing::Test, public EventTimerPosix {
31 public:
32 EventTimerPosixTest()
33 : thread_state_(ThreadState::kNotStarted),
34 process_event_(false, true),
35 main_event_(false, true),
36 process_thread_id_(0),
37 process_thread_(nullptr) {}
Mirko Bonadeic14d9bb2018-07-16 15:44:28 +020038 ~EventTimerPosixTest() override {}
sprang53cf3462016-03-22 01:51:39 -070039
40 rtc::PlatformThread* CreateThread() override {
41 EXPECT_TRUE(process_thread_ == nullptr);
42 process_thread_ =
43 new rtc::PlatformThread(Run, this, "EventTimerPosixTestThread");
44 return process_thread_;
45 }
46
47 static bool Run(void* obj) {
48 return static_cast<EventTimerPosixTest*>(obj)->Process();
49 }
50
51 bool Process() {
52 bool res = ProcessInternal();
53 if (!res) {
54 rtc::CritScope cs(&lock_);
55 thread_state_ = ThreadState::kDead;
56 main_event_.Set();
57 }
58 return res;
59 }
60
61 bool ProcessInternal() {
62 {
63 rtc::CritScope cs(&lock_);
64 if (thread_state_ == ThreadState::kNotStarted) {
65 if (!ChangeThreadState(ThreadState::kNotStarted,
66 ThreadState::kContinue)) {
67 ADD_FAILURE() << "Unable to start process thread";
68 return false;
69 }
70 process_thread_id_ = rtc::CurrentThreadId();
71 }
72 }
73
74 if (!ChangeThreadState(ThreadState::kContinue, ThreadState::kWaiting))
75 return false;
76
77 if (!AwaitThreadState(ThreadState::kRequestProcessCall,
78 rtc::Event::kForever))
79 return false;
80
81 if (!ChangeThreadState(ThreadState::kRequestProcessCall,
82 ThreadState::kCallingProcess))
83 return false;
84
85 EventTimerPosix::Process();
86
87 if (!ChangeThreadState(ThreadState::kCallingProcess,
88 ThreadState::kProcessDone))
89 return false;
90
91 if (!AwaitThreadState(ThreadState::kContinue, rtc::Event::kForever))
92 return false;
93
94 return true;
95 }
96
97 bool IsProcessThread() {
98 rtc::CritScope cs(&lock_);
99 return process_thread_id_ == rtc::CurrentThreadId();
100 }
101
102 bool ChangeThreadState(ThreadState prev_state, ThreadState new_state) {
103 rtc::CritScope cs(&lock_);
104 if (thread_state_ != prev_state)
105 return false;
106 thread_state_ = new_state;
107 if (IsProcessThread()) {
108 main_event_.Set();
109 } else {
110 process_event_.Set();
111 }
112 return true;
113 }
114
115 bool AwaitThreadState(ThreadState state, int timeout) {
116 rtc::Event* event = IsProcessThread() ? &process_event_ : &main_event_;
117 do {
118 rtc::CritScope cs(&lock_);
119 if (state != ThreadState::kDead && thread_state_ == ThreadState::kExiting)
120 return false;
121 if (thread_state_ == state)
122 return true;
123 } while (event->Wait(timeout));
124 return false;
125 }
126
127 bool CallProcess(int timeout_ms) {
128 return AwaitThreadState(ThreadState::kWaiting, timeout_ms) &&
129 ChangeThreadState(ThreadState::kWaiting,
130 ThreadState::kRequestProcessCall);
131 }
132
133 bool AwaitProcessDone(int timeout_ms) {
134 return AwaitThreadState(ThreadState::kProcessDone, timeout_ms) &&
135 ChangeThreadState(ThreadState::kProcessDone, ThreadState::kContinue);
136 }
137
138 void TearDown() override {
139 if (process_thread_) {
140 {
141 rtc::CritScope cs(&lock_);
142 if (thread_state_ != ThreadState::kDead) {
143 thread_state_ = ThreadState::kExiting;
144 process_event_.Set();
145 }
146 }
147 ASSERT_TRUE(AwaitThreadState(ThreadState::kDead, 5000));
148 }
149 }
150
151 ThreadState thread_state_;
152 rtc::CriticalSection lock_;
153 rtc::Event process_event_;
154 rtc::Event main_event_;
155 rtc::PlatformThreadId process_thread_id_;
156 rtc::PlatformThread* process_thread_;
157};
158
159TEST_F(EventTimerPosixTest, WaiterBlocksUntilTimeout) {
160 const int kTimerIntervalMs = 100;
161 const int kTimeoutMs = 5000;
162 ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
163 ASSERT_TRUE(CallProcess(kTimeoutMs));
164 EventTypeWrapper res = Wait(kTimeoutMs);
165 EXPECT_EQ(kEventSignaled, res);
166 ASSERT_TRUE(AwaitProcessDone(kTimeoutMs));
167}
168
169TEST_F(EventTimerPosixTest, WaiterWakesImmediatelyAfterTimeout) {
170 const int kTimerIntervalMs = 100;
171 const int kTimeoutMs = 5000;
172 ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
173 ASSERT_TRUE(CallProcess(kTimeoutMs));
174 ASSERT_TRUE(AwaitProcessDone(kTimeoutMs));
175 EventTypeWrapper res = Wait(0);
176 EXPECT_EQ(kEventSignaled, res);
177}
178
179TEST_F(EventTimerPosixTest, WaiterBlocksUntilTimeoutProcessInactiveOnStart) {
180 const int kTimerIntervalMs = 100;
181 const int kTimeoutMs = 5000;
182 // First call to StartTimer initializes thread.
183 ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
184
185 // Process thread currently _not_ blocking on Process() call.
186 ASSERT_TRUE(AwaitThreadState(ThreadState::kWaiting, kTimeoutMs));
187
188 // Start new one-off timer, then call Process().
189 ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
190 ASSERT_TRUE(CallProcess(kTimeoutMs));
191
192 EventTypeWrapper res = Wait(kTimeoutMs);
193 EXPECT_EQ(kEventSignaled, res);
194
195 ASSERT_TRUE(AwaitProcessDone(kTimeoutMs));
196}
197
198} // namespace webrtc