1d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// found in the LICENSE file.
4d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
5d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "sync/internal_api/public/base/cancelation_signal.h"
6d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
7d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/bind.h"
8d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/message_loop/message_loop.h"
9d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/synchronization/waitable_event.h"
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/threading/platform_thread.h"
11d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/threading/thread.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/time/time.h"
13d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "sync/internal_api/public/base/cancelation_observer.h"
14d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
15d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
16d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace syncer {
17d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)class BlockingTask : public CancelationObserver {
19d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) public:
20d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  BlockingTask(CancelationSignal* cancel_signal);
21d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  virtual ~BlockingTask();
22d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
23d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Starts the |exec_thread_| and uses it to execute DoRun().
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void RunAsync(base::WaitableEvent* task_start_signal,
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                base::WaitableEvent* task_done_signal);
26d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Blocks until canceled.  Signals |task_done_signal| when finished (either
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // via early cancel or cancel after start).  Signals |task_start_signal| if
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // and when the task starts successfully (which will not happen if the task
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // was cancelled early).
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void Run(base::WaitableEvent* task_start_signal,
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)           base::WaitableEvent* task_done_signal);
33d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
34d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Implementation of CancelationObserver.
35d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Wakes up the thread blocked in Run().
36d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  virtual void OnSignalReceived() OVERRIDE;
37d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
38d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Checks if we ever did successfully start waiting for |event_|.  Be careful
39d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // with this.  The flag itself is thread-unsafe, and the event that flips it
40d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // is racy.
41d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  bool WasStarted();
42d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
43d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) private:
44d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::WaitableEvent event_;
45d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::Thread exec_thread_;
46d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  CancelationSignal* cancel_signal_;
47d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  bool was_started_;
48d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)};
49d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
50d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)BlockingTask::BlockingTask(CancelationSignal* cancel_signal)
51d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  : event_(true, false),
52d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    exec_thread_("BlockingTaskBackgroundThread"),
53d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    cancel_signal_(cancel_signal),
54d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    was_started_(false) { }
55d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)BlockingTask::~BlockingTask() {
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (was_started_) {
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cancel_signal_->UnregisterHandler(this);
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
61d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void BlockingTask::RunAsync(base::WaitableEvent* task_start_signal,
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            base::WaitableEvent* task_done_signal) {
64d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  exec_thread_.Start();
65d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  exec_thread_.message_loop()->PostTask(
66d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      FROM_HERE,
67d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      base::Bind(&BlockingTask::Run,
68d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 base::Unretained(this),
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 base::Unretained(task_start_signal),
70d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 base::Unretained(task_done_signal)));
71d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
72d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void BlockingTask::Run(
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::WaitableEvent* task_start_signal,
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::WaitableEvent* task_done_signal) {
76d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (cancel_signal_->TryRegisterHandler(this)) {
77d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    DCHECK(!event_.IsSignaled());
78d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    was_started_ = true;
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    task_start_signal->Signal();
80d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    event_.Wait();
81d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
82d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  task_done_signal->Signal();
83d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
84d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
85d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void BlockingTask::OnSignalReceived() {
86d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  event_.Signal();
87d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
88d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
89d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)bool BlockingTask::WasStarted() {
90d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  return was_started_;
91d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
92d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
93d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)class CancelationSignalTest : public ::testing::Test {
94d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) public:
95d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  CancelationSignalTest();
96d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  virtual ~CancelationSignalTest();
97d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Starts the blocking task on a background thread.  Does not wait for the
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // task to start.
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void StartBlockingTaskAsync();
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Starts the blocking task on a background thread.  Does not return until
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // the task has been started.
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void StartBlockingTaskAndWaitForItToStart();
105d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
106d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Cancels the blocking task.
107d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  void CancelBlocking();
108d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
109d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Verifies that the background task was canceled early.
110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  //
111d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // This method may block for a brief period of time while waiting for the
112d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // background thread to make progress.
113d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  bool VerifyTaskNotStarted();
114d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
115d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) private:
116d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::MessageLoop main_loop_;
117d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
118d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  CancelationSignal signal_;
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::WaitableEvent task_start_event_;
120d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  base::WaitableEvent task_done_event_;
121d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  BlockingTask blocking_task_;
122d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)};
123d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
124d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)CancelationSignalTest::CancelationSignalTest()
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  : task_start_event_(false, false),
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    task_done_event_(false, false),
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    blocking_task_(&signal_) {}
128d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
129d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)CancelationSignalTest::~CancelationSignalTest() {}
130d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void CancelationSignalTest::StartBlockingTaskAsync() {
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  blocking_task_.RunAsync(&task_start_event_, &task_done_event_);
133d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
134d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void CancelationSignalTest::StartBlockingTaskAndWaitForItToStart() {
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  blocking_task_.RunAsync(&task_start_event_, &task_done_event_);
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  task_start_event_.Wait();
138d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
139d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void CancelationSignalTest::CancelBlocking() {
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  signal_.Signal();
142d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
143d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
144d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)bool CancelationSignalTest::VerifyTaskNotStarted() {
145d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Wait until BlockingTask::Run() has finished.
146d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  task_done_event_.Wait();
147d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
148d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Verify the background thread never started blocking.
149d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  return !blocking_task_.WasStarted();
150d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
151d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
152d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)class FakeCancelationObserver : public CancelationObserver {
153d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  virtual void OnSignalReceived() OVERRIDE { }
154d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)};
155d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
156d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)TEST(CancelationSignalTest_SingleThread, CheckFlags) {
157d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  FakeCancelationObserver observer;
158d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  CancelationSignal signal;
159d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
160d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  EXPECT_FALSE(signal.IsSignalled());
161d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  signal.Signal();
162d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  EXPECT_TRUE(signal.IsSignalled());
163d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  EXPECT_FALSE(signal.TryRegisterHandler(&observer));
164d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
165d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
166d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Send the cancelation signal before the task is started.  This will ensure
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// that the task will never be "started" (ie. TryRegisterHandler() will fail,
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// so it will never start blocking on its main WaitableEvent).
169d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)TEST_F(CancelationSignalTest, CancelEarly) {
170d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  CancelBlocking();
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  StartBlockingTaskAsync();
172d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  EXPECT_TRUE(VerifyTaskNotStarted());
173d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
174d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Send the cancelation signal after the task has started running.  This tests
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the non-early exit code path, where the task is stopped while it is in
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// progress.
178d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)TEST_F(CancelationSignalTest, Cancel) {
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  StartBlockingTaskAndWaitForItToStart();
1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Wait for the task to finish and let verify it has been started.
182d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  CancelBlocking();
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  EXPECT_FALSE(VerifyTaskNotStarted());
184d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
185d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
186d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}  // namespace syncer
187