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