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/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"
18 class BlockingTask
: public CancelationObserver
{
20 BlockingTask(CancelationSignal
* cancel_signal
);
21 ~BlockingTask() override
;
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
);
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
);
34 // Implementation of CancelationObserver.
35 // Wakes up the thread blocked in Run().
36 void OnSignalReceived() override
;
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
44 base::WaitableEvent event_
;
45 base::Thread exec_thread_
;
46 CancelationSignal
* cancel_signal_
;
50 BlockingTask::BlockingTask(CancelationSignal
* cancel_signal
)
51 : event_(true, false),
52 exec_thread_("BlockingTaskBackgroundThread"),
53 cancel_signal_(cancel_signal
),
54 was_started_(false) { }
56 BlockingTask::~BlockingTask() {
58 cancel_signal_
->UnregisterHandler(this);
62 void BlockingTask::RunAsync(base::WaitableEvent
* task_start_signal
,
63 base::WaitableEvent
* task_done_signal
) {
65 exec_thread_
.message_loop()->PostTask(
67 base::Bind(&BlockingTask::Run
,
68 base::Unretained(this),
69 base::Unretained(task_start_signal
),
70 base::Unretained(task_done_signal
)));
73 void BlockingTask::Run(
74 base::WaitableEvent
* task_start_signal
,
75 base::WaitableEvent
* task_done_signal
) {
76 if (cancel_signal_
->TryRegisterHandler(this)) {
77 DCHECK(!event_
.IsSignaled());
79 task_start_signal
->Signal();
82 task_done_signal
->Signal();
85 void BlockingTask::OnSignalReceived() {
89 bool BlockingTask::WasStarted() {
93 class CancelationSignalTest
: public ::testing::Test
{
95 CancelationSignalTest();
96 ~CancelationSignalTest() override
;
98 // Starts the blocking task on a background thread. Does not wait for the
100 void StartBlockingTaskAsync();
102 // Starts the blocking task on a background thread. Does not return until
103 // the task has been started.
104 void StartBlockingTaskAndWaitForItToStart();
106 // Cancels the blocking task.
107 void CancelBlocking();
109 // Verifies that the background task was canceled early.
111 // This method may block for a brief period of time while waiting for the
112 // background thread to make progress.
113 bool VerifyTaskNotStarted();
116 base::MessageLoop main_loop_
;
118 CancelationSignal signal_
;
119 base::WaitableEvent task_start_event_
;
120 base::WaitableEvent task_done_event_
;
121 BlockingTask blocking_task_
;
124 CancelationSignalTest::CancelationSignalTest()
125 : task_start_event_(false, false),
126 task_done_event_(false, false),
127 blocking_task_(&signal_
) {}
129 CancelationSignalTest::~CancelationSignalTest() {}
131 void CancelationSignalTest::StartBlockingTaskAsync() {
132 blocking_task_
.RunAsync(&task_start_event_
, &task_done_event_
);
135 void CancelationSignalTest::StartBlockingTaskAndWaitForItToStart() {
136 blocking_task_
.RunAsync(&task_start_event_
, &task_done_event_
);
137 task_start_event_
.Wait();
140 void CancelationSignalTest::CancelBlocking() {
144 bool CancelationSignalTest::VerifyTaskNotStarted() {
145 // Wait until BlockingTask::Run() has finished.
146 task_done_event_
.Wait();
148 // Verify the background thread never started blocking.
149 return !blocking_task_
.WasStarted();
152 class FakeCancelationObserver
: public CancelationObserver
{
153 void OnSignalReceived() override
{}
156 TEST(CancelationSignalTest_SingleThread
, CheckFlags
) {
157 FakeCancelationObserver observer
;
158 CancelationSignal signal
;
160 EXPECT_FALSE(signal
.IsSignalled());
162 EXPECT_TRUE(signal
.IsSignalled());
163 EXPECT_FALSE(signal
.TryRegisterHandler(&observer
));
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).
169 TEST_F(CancelationSignalTest
, CancelEarly
) {
171 StartBlockingTaskAsync();
172 EXPECT_TRUE(VerifyTaskNotStarted());
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
178 TEST_F(CancelationSignalTest
, Cancel
) {
179 StartBlockingTaskAndWaitForItToStart();
181 // Wait for the task to finish and let verify it has been started.
183 EXPECT_FALSE(VerifyTaskNotStarted());
186 } // namespace syncer