OLD | NEW |
| (Empty) |
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/single_thread_task_runner.h" | |
10 #include "base/synchronization/waitable_event.h" | |
11 #include "base/threading/platform_thread.h" | |
12 #include "base/threading/thread.h" | |
13 #include "base/time/time.h" | |
14 #include "sync/internal_api/public/base/cancelation_observer.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 | |
17 namespace syncer { | |
18 | |
19 class BlockingTask : public CancelationObserver { | |
20 public: | |
21 explicit BlockingTask(CancelationSignal* cancel_signal); | |
22 ~BlockingTask() override; | |
23 | |
24 // Starts the |exec_thread_| and uses it to execute DoRun(). | |
25 void RunAsync(base::WaitableEvent* task_start_signal, | |
26 base::WaitableEvent* task_done_signal); | |
27 | |
28 // Blocks until canceled. Signals |task_done_signal| when finished (either | |
29 // via early cancel or cancel after start). Signals |task_start_signal| if | |
30 // and when the task starts successfully (which will not happen if the task | |
31 // was cancelled early). | |
32 void Run(base::WaitableEvent* task_start_signal, | |
33 base::WaitableEvent* task_done_signal); | |
34 | |
35 // Implementation of CancelationObserver. | |
36 // Wakes up the thread blocked in Run(). | |
37 void OnSignalReceived() override; | |
38 | |
39 // Checks if we ever did successfully start waiting for |event_|. Be careful | |
40 // with this. The flag itself is thread-unsafe, and the event that flips it | |
41 // is racy. | |
42 bool WasStarted(); | |
43 | |
44 private: | |
45 base::WaitableEvent event_; | |
46 base::Thread exec_thread_; | |
47 CancelationSignal* cancel_signal_; | |
48 bool was_started_; | |
49 }; | |
50 | |
51 BlockingTask::BlockingTask(CancelationSignal* cancel_signal) | |
52 : event_(base::WaitableEvent::ResetPolicy::MANUAL, | |
53 base::WaitableEvent::InitialState::NOT_SIGNALED), | |
54 exec_thread_("BlockingTaskBackgroundThread"), | |
55 cancel_signal_(cancel_signal), | |
56 was_started_(false) {} | |
57 | |
58 BlockingTask::~BlockingTask() { | |
59 if (was_started_) { | |
60 cancel_signal_->UnregisterHandler(this); | |
61 } | |
62 } | |
63 | |
64 void BlockingTask::RunAsync(base::WaitableEvent* task_start_signal, | |
65 base::WaitableEvent* task_done_signal) { | |
66 exec_thread_.Start(); | |
67 exec_thread_.task_runner()->PostTask( | |
68 FROM_HERE, base::Bind(&BlockingTask::Run, base::Unretained(this), | |
69 base::Unretained(task_start_signal), | |
70 base::Unretained(task_done_signal))); | |
71 } | |
72 | |
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()); | |
78 was_started_ = true; | |
79 task_start_signal->Signal(); | |
80 event_.Wait(); | |
81 } | |
82 task_done_signal->Signal(); | |
83 } | |
84 | |
85 void BlockingTask::OnSignalReceived() { | |
86 event_.Signal(); | |
87 } | |
88 | |
89 bool BlockingTask::WasStarted() { | |
90 return was_started_; | |
91 } | |
92 | |
93 class CancelationSignalTest : public ::testing::Test { | |
94 public: | |
95 CancelationSignalTest(); | |
96 ~CancelationSignalTest() override; | |
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 | |
124 CancelationSignalTest::CancelationSignalTest() | |
125 : task_start_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
126 base::WaitableEvent::InitialState::NOT_SIGNALED), | |
127 task_done_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
128 base::WaitableEvent::InitialState::NOT_SIGNALED), | |
129 blocking_task_(&signal_) {} | |
130 | |
131 CancelationSignalTest::~CancelationSignalTest() {} | |
132 | |
133 void CancelationSignalTest::StartBlockingTaskAsync() { | |
134 blocking_task_.RunAsync(&task_start_event_, &task_done_event_); | |
135 } | |
136 | |
137 void CancelationSignalTest::StartBlockingTaskAndWaitForItToStart() { | |
138 blocking_task_.RunAsync(&task_start_event_, &task_done_event_); | |
139 task_start_event_.Wait(); | |
140 } | |
141 | |
142 void CancelationSignalTest::CancelBlocking() { | |
143 signal_.Signal(); | |
144 } | |
145 | |
146 bool CancelationSignalTest::VerifyTaskNotStarted() { | |
147 // Wait until BlockingTask::Run() has finished. | |
148 task_done_event_.Wait(); | |
149 | |
150 // Verify the background thread never started blocking. | |
151 return !blocking_task_.WasStarted(); | |
152 } | |
153 | |
154 class FakeCancelationObserver : public CancelationObserver { | |
155 void OnSignalReceived() override {} | |
156 }; | |
157 | |
158 TEST(CancelationSignalTest_SingleThread, CheckFlags) { | |
159 FakeCancelationObserver observer; | |
160 CancelationSignal signal; | |
161 | |
162 EXPECT_FALSE(signal.IsSignalled()); | |
163 signal.Signal(); | |
164 EXPECT_TRUE(signal.IsSignalled()); | |
165 EXPECT_FALSE(signal.TryRegisterHandler(&observer)); | |
166 } | |
167 | |
168 // Send the cancelation signal before the task is started. This will ensure | |
169 // that the task will never be "started" (ie. TryRegisterHandler() will fail, | |
170 // so it will never start blocking on its main WaitableEvent). | |
171 TEST_F(CancelationSignalTest, CancelEarly) { | |
172 CancelBlocking(); | |
173 StartBlockingTaskAsync(); | |
174 EXPECT_TRUE(VerifyTaskNotStarted()); | |
175 } | |
176 | |
177 // Send the cancelation signal after the task has started running. This tests | |
178 // the non-early exit code path, where the task is stopped while it is in | |
179 // progress. | |
180 TEST_F(CancelationSignalTest, Cancel) { | |
181 StartBlockingTaskAndWaitForItToStart(); | |
182 | |
183 // Wait for the task to finish and let verify it has been started. | |
184 CancelBlocking(); | |
185 EXPECT_FALSE(VerifyTaskNotStarted()); | |
186 } | |
187 | |
188 } // namespace syncer | |
OLD | NEW |