Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(554)

Unified Diff: webrtc/system_wrappers/source/event_timer_posix_unittest.cc

Issue 1812533002: Fix race condition in EventTimerPosix (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Fixed race in test code Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webrtc/system_wrappers/source/event_timer_posix_unittest.cc
diff --git a/webrtc/system_wrappers/source/event_timer_posix_unittest.cc b/webrtc/system_wrappers/source/event_timer_posix_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5674e539c9e63bbb0a73d659b359bc8406c07e2d
--- /dev/null
+++ b/webrtc/system_wrappers/source/event_timer_posix_unittest.cc
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+
+#include "webrtc/system_wrappers/source/event_timer_posix.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/criticalsection.h"
+
+namespace webrtc {
+
+enum class ThreadState {
+ kNotStarted,
+ kWaiting,
+ kRequestProcessCall,
+ kCallingProcess,
+ kProcessDone,
+ kContinue,
+ kExiting,
+ kDead
+};
+
+class EventTimerPosixTest : public testing::Test, public EventTimerPosix {
+ public:
+ EventTimerPosixTest()
+ : thread_state_(ThreadState::kNotStarted),
+ process_event_(false, true),
+ main_event_(false, true),
+ process_thread_id_(0),
+ process_thread_(nullptr) {}
+ virtual ~EventTimerPosixTest() {}
+
+ rtc::PlatformThread* CreateThread() override {
+ EXPECT_TRUE(process_thread_ == nullptr);
+ process_thread_ =
+ new rtc::PlatformThread(Run, this, "EventTimerPosixTestThread");
+ return process_thread_;
+ }
+
+ static bool Run(void* obj) {
+ return static_cast<EventTimerPosixTest*>(obj)->Process();
+ }
+
+ bool Process() {
+ {
+ rtc::CritScope cs(&lock_);
+ process_thread_id_ = rtc::CurrentThreadId();
+ }
+
+ if (!SetThreadState(ThreadState::kWaiting))
+ return false;
+
+ if (!AwaitThreadState(ThreadState::kRequestProcessCall,
+ rtc::Event::kForever)) {
+ SetThreadState(ThreadState::kDead);
+ return false;
+ }
+
+ if (!SetThreadState(ThreadState::kCallingProcess))
+ return false;
+
+ EventTimerPosix::Process();
+
+ if (!SetThreadState(ThreadState::kProcessDone))
+ return false;
+
+ if (!AwaitThreadState(ThreadState::kContinue, rtc::Event::kForever)) {
+ SetThreadState(ThreadState::kDead);
+ return false;
+ }
+
+ return true;
+ }
+
+ bool IsProcessThread() {
+ rtc::CritScope cs(&lock_);
+ return process_thread_id_ == rtc::CurrentThreadId();
+ }
+
+ bool SetThreadState(ThreadState state) {
+ rtc::CritScope cs(&lock_);
+ if (thread_state_ == ThreadState::kDead)
+ return false;
+ thread_state_ = state;
+ if (IsProcessThread()) {
+ main_event_.Set();
+ } else {
+ process_event_.Set();
+ }
+ return true;
+ }
+
+ bool AwaitThreadState(ThreadState state, int timeout) {
+ rtc::Event* event = IsProcessThread() ? &process_event_ : &main_event_;
+ do {
+ rtc::CritScope cs(&lock_);
+ if (state != ThreadState::kDead && thread_state_ == ThreadState::kExiting)
+ return false;
+ if (thread_state_ == state)
+ return true;
+ } while (event->Wait(timeout));
+ return false;
+ }
+
+ bool CallProcess(int timeout_ms) {
+ return AwaitThreadState(ThreadState::kWaiting, timeout_ms) &&
+ SetThreadState(ThreadState::kRequestProcessCall);
+ }
+
+ bool AwaitProcessDone(int timeout_ms) {
+ return AwaitThreadState(ThreadState::kProcessDone, timeout_ms) &&
+ SetThreadState(ThreadState::kContinue);
+ }
+
+ void TearDown() override {
+ if (process_thread_) {
+ SetThreadState(ThreadState::kExiting);
+ AwaitThreadState(ThreadState::kDead, 5000);
+ }
+ }
+
+ ThreadState thread_state_;
+ rtc::CriticalSection lock_;
+ rtc::Event process_event_;
+ rtc::Event main_event_;
+ rtc::PlatformThreadId process_thread_id_;
+ rtc::PlatformThread* process_thread_;
+};
+
+TEST_F(EventTimerPosixTest, WaiterBlocksUntilTimeout) {
+ const int kTimerIntervalMs = 100;
+ const int kTimeoutMs = 5000;
+ ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
+ ASSERT_TRUE(CallProcess(kTimeoutMs));
+ EventTypeWrapper res = Wait(kTimeoutMs);
+ EXPECT_EQ(kEventSignaled, res);
+ ASSERT_TRUE(AwaitProcessDone(kTimeoutMs));
+}
+
+TEST_F(EventTimerPosixTest, WaiterWakesImmediatelyAfterTimeout) {
+ const int kTimerIntervalMs = 100;
+ const int kTimeoutMs = 5000;
+ ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
+ ASSERT_TRUE(CallProcess(kTimeoutMs));
+ ASSERT_TRUE(AwaitProcessDone(kTimeoutMs));
+ EventTypeWrapper res = Wait(0);
+ EXPECT_EQ(kEventSignaled, res);
+}
+
+TEST_F(EventTimerPosixTest, WaiterBlocksUntilTimeoutProcessInactiveOnStart) {
+ const int kTimerIntervalMs = 100;
+ const int kTimeoutMs = 5000;
+ // First call to StartTimer initializes thread.
+ ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
+
+ // Process thread currently _not_ blocking on Process() call.
+ ASSERT_TRUE(AwaitThreadState(ThreadState::kWaiting, kTimeoutMs));
+
+ // Start new one-off timer, then call Process().
+ ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
+ ASSERT_TRUE(CallProcess(kTimeoutMs));
+
+ EventTypeWrapper res = Wait(kTimeoutMs);
+ EXPECT_EQ(kEventSignaled, res);
+
+ ASSERT_TRUE(AwaitProcessDone(kTimeoutMs));
+}
+
+} // namespace webrtc

Powered by Google App Engine
This is Rietveld 408576698