1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "sync/internal_api/public/base/cancelation_signal.h" 6 7#include "base/bind.h" 8#include "base/message_loop/message_loop.h" 9#include "base/synchronization/waitable_event.h" 10#include "base/threading/platform_thread.h" 11#include "base/threading/thread.h" 12#include "base/time/time.h" 13#include "sync/internal_api/public/base/cancelation_observer.h" 14#include "testing/gtest/include/gtest/gtest.h" 15 16namespace syncer { 17 18class BlockingTask : public CancelationObserver { 19 public: 20 BlockingTask(CancelationSignal* cancel_signal); 21 virtual ~BlockingTask(); 22 23 // Starts the |exec_thread_| and uses it to execute DoRun(). 24 void RunAsync(base::WaitableEvent* task_start_signal, 25 base::WaitableEvent* task_done_signal); 26 27 // Blocks until canceled. Signals |task_done_signal| when finished (either 28 // via early cancel or cancel after start). Signals |task_start_signal| if 29 // and when the task starts successfully (which will not happen if the task 30 // was cancelled early). 31 void Run(base::WaitableEvent* task_start_signal, 32 base::WaitableEvent* task_done_signal); 33 34 // Implementation of CancelationObserver. 35 // Wakes up the thread blocked in Run(). 36 virtual void OnSignalReceived() OVERRIDE; 37 38 // Checks if we ever did successfully start waiting for |event_|. Be careful 39 // with this. The flag itself is thread-unsafe, and the event that flips it 40 // is racy. 41 bool WasStarted(); 42 43 private: 44 base::WaitableEvent event_; 45 base::Thread exec_thread_; 46 CancelationSignal* cancel_signal_; 47 bool was_started_; 48}; 49 50BlockingTask::BlockingTask(CancelationSignal* cancel_signal) 51 : event_(true, false), 52 exec_thread_("BlockingTaskBackgroundThread"), 53 cancel_signal_(cancel_signal), 54 was_started_(false) { } 55 56BlockingTask::~BlockingTask() { 57 if (was_started_) { 58 cancel_signal_->UnregisterHandler(this); 59 } 60} 61 62void BlockingTask::RunAsync(base::WaitableEvent* task_start_signal, 63 base::WaitableEvent* task_done_signal) { 64 exec_thread_.Start(); 65 exec_thread_.message_loop()->PostTask( 66 FROM_HERE, 67 base::Bind(&BlockingTask::Run, 68 base::Unretained(this), 69 base::Unretained(task_start_signal), 70 base::Unretained(task_done_signal))); 71} 72 73void BlockingTask::Run( 74 base::WaitableEvent* task_start_signal, 75 base::WaitableEvent* task_done_signal) { 76 if (cancel_signal_->TryRegisterHandler(this)) { 77 DCHECK(!event_.IsSignaled()); 78 was_started_ = true; 79 task_start_signal->Signal(); 80 event_.Wait(); 81 } 82 task_done_signal->Signal(); 83} 84 85void BlockingTask::OnSignalReceived() { 86 event_.Signal(); 87} 88 89bool BlockingTask::WasStarted() { 90 return was_started_; 91} 92 93class CancelationSignalTest : public ::testing::Test { 94 public: 95 CancelationSignalTest(); 96 virtual ~CancelationSignalTest(); 97 98 // Starts the blocking task on a background thread. Does not wait for the 99 // task to start. 100 void StartBlockingTaskAsync(); 101 102 // Starts the blocking task on a background thread. Does not return until 103 // the task has been started. 104 void StartBlockingTaskAndWaitForItToStart(); 105 106 // Cancels the blocking task. 107 void CancelBlocking(); 108 109 // Verifies that the background task was canceled early. 110 // 111 // This method may block for a brief period of time while waiting for the 112 // background thread to make progress. 113 bool VerifyTaskNotStarted(); 114 115 private: 116 base::MessageLoop main_loop_; 117 118 CancelationSignal signal_; 119 base::WaitableEvent task_start_event_; 120 base::WaitableEvent task_done_event_; 121 BlockingTask blocking_task_; 122}; 123 124CancelationSignalTest::CancelationSignalTest() 125 : task_start_event_(false, false), 126 task_done_event_(false, false), 127 blocking_task_(&signal_) {} 128 129CancelationSignalTest::~CancelationSignalTest() {} 130 131void CancelationSignalTest::StartBlockingTaskAsync() { 132 blocking_task_.RunAsync(&task_start_event_, &task_done_event_); 133} 134 135void CancelationSignalTest::StartBlockingTaskAndWaitForItToStart() { 136 blocking_task_.RunAsync(&task_start_event_, &task_done_event_); 137 task_start_event_.Wait(); 138} 139 140void CancelationSignalTest::CancelBlocking() { 141 signal_.Signal(); 142} 143 144bool CancelationSignalTest::VerifyTaskNotStarted() { 145 // Wait until BlockingTask::Run() has finished. 146 task_done_event_.Wait(); 147 148 // Verify the background thread never started blocking. 149 return !blocking_task_.WasStarted(); 150} 151 152class FakeCancelationObserver : public CancelationObserver { 153 virtual void OnSignalReceived() OVERRIDE { } 154}; 155 156TEST(CancelationSignalTest_SingleThread, CheckFlags) { 157 FakeCancelationObserver observer; 158 CancelationSignal signal; 159 160 EXPECT_FALSE(signal.IsSignalled()); 161 signal.Signal(); 162 EXPECT_TRUE(signal.IsSignalled()); 163 EXPECT_FALSE(signal.TryRegisterHandler(&observer)); 164} 165 166// Send the cancelation signal before the task is started. This will ensure 167// that the task will never be "started" (ie. TryRegisterHandler() will fail, 168// so it will never start blocking on its main WaitableEvent). 169TEST_F(CancelationSignalTest, CancelEarly) { 170 CancelBlocking(); 171 StartBlockingTaskAsync(); 172 EXPECT_TRUE(VerifyTaskNotStarted()); 173} 174 175// Send the cancelation signal after the task has started running. This tests 176// the non-early exit code path, where the task is stopped while it is in 177// progress. 178TEST_F(CancelationSignalTest, Cancel) { 179 StartBlockingTaskAndWaitForItToStart(); 180 181 // Wait for the task to finish and let verify it has been started. 182 CancelBlocking(); 183 EXPECT_FALSE(VerifyTaskNotStarted()); 184} 185 186} // namespace syncer 187