cancelation_signal_unittest.cc 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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. #include "components/sync/engine/cancelation_signal.h"
  5. #include "base/bind.h"
  6. #include "base/memory/raw_ptr.h"
  7. #include "base/synchronization/waitable_event.h"
  8. #include "base/task/single_thread_task_runner.h"
  9. #include "base/threading/platform_thread.h"
  10. #include "base/threading/thread.h"
  11. #include "testing/gtest/include/gtest/gtest.h"
  12. namespace syncer {
  13. class BlockingTask : public CancelationSignal::Observer {
  14. public:
  15. explicit BlockingTask(CancelationSignal* cancel_signal);
  16. ~BlockingTask() override;
  17. // Starts the |exec_thread_| and uses it to execute DoRun().
  18. void RunAsync(base::WaitableEvent* task_start_signal,
  19. base::WaitableEvent* task_done_signal);
  20. // Blocks until canceled. Signals |task_done_signal| when finished (either
  21. // via early cancel or cancel after start). Signals |task_start_signal| if
  22. // and when the task starts successfully (which will not happen if the task
  23. // was cancelled early).
  24. void Run(base::WaitableEvent* task_start_signal,
  25. base::WaitableEvent* task_done_signal);
  26. // Implementation of CancelationSignal::Observer.
  27. // Wakes up the thread blocked in Run().
  28. void OnCancelationSignalReceived() override;
  29. // Checks if we ever did successfully start waiting for |event_|. Be careful
  30. // with this. The flag itself is thread-unsafe, and the event that flips it
  31. // is racy.
  32. bool WasStarted();
  33. private:
  34. base::WaitableEvent event_;
  35. base::Thread exec_thread_;
  36. raw_ptr<CancelationSignal> cancel_signal_;
  37. bool was_started_ = false;
  38. };
  39. BlockingTask::BlockingTask(CancelationSignal* cancel_signal)
  40. : event_(base::WaitableEvent::ResetPolicy::MANUAL,
  41. base::WaitableEvent::InitialState::NOT_SIGNALED),
  42. exec_thread_("BlockingTaskBackgroundThread"),
  43. cancel_signal_(cancel_signal) {}
  44. BlockingTask::~BlockingTask() {
  45. if (was_started_) {
  46. cancel_signal_->UnregisterHandler(this);
  47. }
  48. }
  49. void BlockingTask::RunAsync(base::WaitableEvent* task_start_signal,
  50. base::WaitableEvent* task_done_signal) {
  51. exec_thread_.Start();
  52. exec_thread_.task_runner()->PostTask(
  53. FROM_HERE, base::BindOnce(&BlockingTask::Run, base::Unretained(this),
  54. base::Unretained(task_start_signal),
  55. base::Unretained(task_done_signal)));
  56. }
  57. void BlockingTask::Run(base::WaitableEvent* task_start_signal,
  58. base::WaitableEvent* task_done_signal) {
  59. if (cancel_signal_->TryRegisterHandler(this)) {
  60. DCHECK(!event_.IsSignaled());
  61. was_started_ = true;
  62. task_start_signal->Signal();
  63. event_.Wait();
  64. }
  65. task_done_signal->Signal();
  66. }
  67. void BlockingTask::OnCancelationSignalReceived() {
  68. event_.Signal();
  69. }
  70. bool BlockingTask::WasStarted() {
  71. return was_started_;
  72. }
  73. class CancelationSignalTest : public ::testing::Test {
  74. public:
  75. CancelationSignalTest();
  76. ~CancelationSignalTest() override;
  77. // Starts the blocking task on a background thread. Does not wait for the
  78. // task to start.
  79. void StartBlockingTaskAsync();
  80. // Starts the blocking task on a background thread. Does not return until
  81. // the task has been started.
  82. void StartBlockingTaskAndWaitForItToStart();
  83. // Cancels the blocking task.
  84. void CancelBlocking();
  85. // Verifies that the background task was canceled early.
  86. //
  87. // This method may block for a brief period of time while waiting for the
  88. // background thread to make progress.
  89. bool VerifyTaskNotStarted();
  90. private:
  91. CancelationSignal signal_;
  92. base::WaitableEvent task_start_event_;
  93. base::WaitableEvent task_done_event_;
  94. BlockingTask blocking_task_;
  95. };
  96. CancelationSignalTest::CancelationSignalTest()
  97. : task_start_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
  98. base::WaitableEvent::InitialState::NOT_SIGNALED),
  99. task_done_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
  100. base::WaitableEvent::InitialState::NOT_SIGNALED),
  101. blocking_task_(&signal_) {}
  102. CancelationSignalTest::~CancelationSignalTest() = default;
  103. void CancelationSignalTest::StartBlockingTaskAsync() {
  104. blocking_task_.RunAsync(&task_start_event_, &task_done_event_);
  105. }
  106. void CancelationSignalTest::StartBlockingTaskAndWaitForItToStart() {
  107. blocking_task_.RunAsync(&task_start_event_, &task_done_event_);
  108. task_start_event_.Wait();
  109. }
  110. void CancelationSignalTest::CancelBlocking() {
  111. signal_.Signal();
  112. }
  113. bool CancelationSignalTest::VerifyTaskNotStarted() {
  114. // Wait until BlockingTask::Run() has finished.
  115. task_done_event_.Wait();
  116. // Verify the background thread never started blocking.
  117. return !blocking_task_.WasStarted();
  118. }
  119. class FakeObserver : public CancelationSignal::Observer {
  120. public:
  121. void OnCancelationSignalReceived() override {}
  122. };
  123. TEST(CancelationSignalTest_SingleThread, CheckFlags) {
  124. FakeObserver observer;
  125. CancelationSignal signal;
  126. EXPECT_FALSE(signal.IsSignalled());
  127. signal.Signal();
  128. EXPECT_TRUE(signal.IsSignalled());
  129. EXPECT_FALSE(signal.TryRegisterHandler(&observer));
  130. }
  131. // Send the cancelation signal before the task is started. This will ensure
  132. // that the task will never be "started" (ie. TryRegisterHandler() will fail,
  133. // so it will never start blocking on its main WaitableEvent).
  134. TEST_F(CancelationSignalTest, CancelEarly) {
  135. CancelBlocking();
  136. StartBlockingTaskAsync();
  137. EXPECT_TRUE(VerifyTaskNotStarted());
  138. }
  139. // Send the cancelation signal after the task has started running. This tests
  140. // the non-early exit code path, where the task is stopped while it is in
  141. // progress.
  142. TEST_F(CancelationSignalTest, Cancel) {
  143. StartBlockingTaskAndWaitForItToStart();
  144. // Wait for the task to finish and let verify it has been started.
  145. CancelBlocking();
  146. EXPECT_FALSE(VerifyTaskNotStarted());
  147. }
  148. } // namespace syncer