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.
5 #include "sync/internal_api/public/base/cancelation_signal.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/threading/thread.h"
11 #include "sync/internal_api/public/base/cancelation_observer.h"
12 #include "testing/gtest/include/gtest/gtest.h"
16 class BlockingTask : public CancelationObserver {
18 BlockingTask(CancelationSignal* cancel_signal);
19 virtual ~BlockingTask();
21 // Starts the |exec_thread_| and uses it to execute DoRun().
22 void RunAsync(base::WaitableEvent* task_done_signal);
24 // Blocks until canceled. Signals |task_done_signal| when finished.
25 void Run(base::WaitableEvent* task_done_signal);
27 // Implementation of CancelationObserver.
28 // Wakes up the thread blocked in Run().
29 virtual void OnSignalReceived() OVERRIDE;
31 // Checks if we ever did successfully start waiting for |event_|. Be careful
32 // with this. The flag itself is thread-unsafe, and the event that flips it
37 base::WaitableEvent event_;
38 base::Thread exec_thread_;
39 CancelationSignal* cancel_signal_;
43 BlockingTask::BlockingTask(CancelationSignal* cancel_signal)
44 : event_(true, false),
45 exec_thread_("BlockingTaskBackgroundThread"),
46 cancel_signal_(cancel_signal),
47 was_started_(false) { }
49 BlockingTask::~BlockingTask() {}
51 void BlockingTask::RunAsync(base::WaitableEvent* task_done_signal) {
53 exec_thread_.message_loop()->PostTask(
55 base::Bind(&BlockingTask::Run,
56 base::Unretained(this),
57 base::Unretained(task_done_signal)));
60 void BlockingTask::Run(base::WaitableEvent* task_done_signal) {
61 if (cancel_signal_->TryRegisterHandler(this)) {
62 DCHECK(!event_.IsSignaled());
66 task_done_signal->Signal();
69 void BlockingTask::OnSignalReceived() {
73 bool BlockingTask::WasStarted() {
77 class CancelationSignalTest : public ::testing::Test {
79 CancelationSignalTest();
80 virtual ~CancelationSignalTest();
82 // Starts the blocking task on a background thread.
83 void StartBlockingTask();
85 // Cancels the blocking task.
86 void CancelBlocking();
88 // Verifies that the background task is not running. This could be beacause
89 // it was canceled early or because it was canceled after it was started.
91 // This method may block for a brief period of time while waiting for the
92 // background thread to make progress.
93 bool VerifyTaskDone();
95 // Verifies that the background task was canceled early.
97 // This method may block for a brief period of time while waiting for the
98 // background thread to make progress.
99 bool VerifyTaskNotStarted();
102 base::MessageLoop main_loop_;
104 CancelationSignal signal_;
105 base::WaitableEvent task_done_event_;
106 BlockingTask blocking_task_;
109 CancelationSignalTest::CancelationSignalTest()
110 : task_done_event_(false, false), blocking_task_(&signal_) {}
112 CancelationSignalTest::~CancelationSignalTest() {}
114 void CancelationSignalTest::StartBlockingTask() {
115 blocking_task_.RunAsync(&task_done_event_);
118 void CancelationSignalTest::CancelBlocking() {
122 bool CancelationSignalTest::VerifyTaskDone() {
123 // Wait until BlockingTask::Run() has finished.
124 task_done_event_.Wait();
128 bool CancelationSignalTest::VerifyTaskNotStarted() {
129 // Wait until BlockingTask::Run() has finished.
130 task_done_event_.Wait();
132 // Verify the background thread never started blocking.
133 return !blocking_task_.WasStarted();
136 class FakeCancelationObserver : public CancelationObserver {
137 virtual void OnSignalReceived() OVERRIDE { }
140 TEST(CancelationSignalTest_SingleThread, CheckFlags) {
141 FakeCancelationObserver observer;
142 CancelationSignal signal;
144 EXPECT_FALSE(signal.IsSignalled());
146 EXPECT_TRUE(signal.IsSignalled());
147 EXPECT_FALSE(signal.TryRegisterHandler(&observer));
150 // Send the cancelation signal before the task is started. This will ensure
151 // that the task will never be attempted.
152 TEST_F(CancelationSignalTest, CancelEarly) {
155 EXPECT_TRUE(VerifyTaskNotStarted());
158 // Send the cancelation signal after the request to start the task has been
159 // posted. This is racy. The signal to stop may arrive before the signal to
160 // run the task. If that happens, we end up with another instance of the
161 // CancelEarly test defined earlier. If the signal requesting a stop arrives
162 // after the task has been started, it should end up stopping the task.
163 TEST_F(CancelationSignalTest, Cancel) {
166 EXPECT_TRUE(VerifyTaskDone());
169 } // namespace syncer