Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(440)

Unified Diff: base/run_loop_unittest.cc

Issue 2892993002: Make RunLoop::Quit() thread-safe. (Closed)
Patch Set: add tests that call Quit(WhenIdle) directly, without the Quit(WhenIdle)Closure Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/run_loop.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/run_loop_unittest.cc
diff --git a/base/run_loop_unittest.cc b/base/run_loop_unittest.cc
index ea596c60fb76f89946967e985fb690627937c2ef..947e3b920f2e9a30f1e8a23f26f6570605d0dfac 100644
--- a/base/run_loop_unittest.cc
+++ b/base/run_loop_unittest.cc
@@ -8,8 +8,10 @@
#include "base/bind_helpers.h"
#include "base/location.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -52,7 +54,7 @@ class RunLoopTest : public testing::Test {
protected:
RunLoopTest() = default;
- MessageLoop message_loop_;
+ test::ScopedTaskEnvironment task_environment_;
RunLoop run_loop_;
int counter_ = 0;
@@ -114,6 +116,141 @@ TEST_F(RunLoopTest, QuitWhenIdleClosureAfterRunLoopScope) {
quit_when_idle_closure.Run();
}
+// Verify that Quit can be executed from another sequence.
+TEST_F(RunLoopTest, QuitFromOtherSequence) {
+ scoped_refptr<SequencedTaskRunner> other_sequence =
+ CreateSequencedTaskRunnerWithTraits({});
+
+ // Always expected to run before asynchronous Quit() kicks in.
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+ WaitableEvent loop_was_quit(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED);
+ other_sequence->PostTask(
+ FROM_HERE, base::BindOnce([](RunLoop* run_loop) { run_loop->Quit(); },
+ Unretained(&run_loop_)));
+ other_sequence->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WaitableEvent::Signal, base::Unretained(&loop_was_quit)));
+
+ // Anything that's posted after the Quit closure was posted back to this
+ // sequence shouldn't get a chance to run.
+ loop_was_quit.Wait();
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+ run_loop_.Run();
+
+ EXPECT_EQ(1, counter_);
+}
+
+// Verify that QuitClosure can be executed from another sequence.
+TEST_F(RunLoopTest, QuitFromOtherSequenceWithClosure) {
+ scoped_refptr<SequencedTaskRunner> other_sequence =
+ CreateSequencedTaskRunnerWithTraits({});
+
+ // Always expected to run before asynchronous Quit() kicks in.
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+ WaitableEvent loop_was_quit(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED);
+ other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure());
+ other_sequence->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WaitableEvent::Signal, base::Unretained(&loop_was_quit)));
+
+ // Anything that's posted after the Quit closure was posted back to this
+ // sequence shouldn't get a chance to run.
+ loop_was_quit.Wait();
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+ run_loop_.Run();
+
+ EXPECT_EQ(1, counter_);
+}
+
+// Verify that Quit can be executed from another sequence even when the
+// Quit is racing with Run() -- i.e. forgo the WaitableEvent used above.
+TEST_F(RunLoopTest, QuitFromOtherSequenceRacy) {
+ scoped_refptr<SequencedTaskRunner> other_sequence =
+ CreateSequencedTaskRunnerWithTraits({});
+
+ // Always expected to run before asynchronous Quit() kicks in.
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+ other_sequence->PostTask(
+ FROM_HERE, base::BindOnce([](RunLoop* run_loop) { run_loop->Quit(); },
+ Unretained(&run_loop_)));
+
+ run_loop_.Run();
+
+ EXPECT_EQ(1, counter_);
+}
+
+// Verify that QuitClosure can be executed from another sequence even when the
+// Quit is racing with Run() -- i.e. forgo the WaitableEvent used above.
+TEST_F(RunLoopTest, QuitFromOtherSequenceRacyWithClosure) {
+ scoped_refptr<SequencedTaskRunner> other_sequence =
+ CreateSequencedTaskRunnerWithTraits({});
+
+ // Always expected to run before asynchronous Quit() kicks in.
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+ other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure());
+
+ run_loop_.Run();
+
+ EXPECT_EQ(1, counter_);
+}
+
+// Verify that QuitWhenIdle can be executed from another sequence.
+TEST_F(RunLoopTest, QuitWhenIdleFromOtherSequence) {
+ scoped_refptr<SequencedTaskRunner> other_sequence =
+ CreateSequencedTaskRunnerWithTraits({});
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+ other_sequence->PostTask(
+ FROM_HERE,
+ base::BindOnce([](RunLoop* run_loop) { run_loop->QuitWhenIdle(); },
+ Unretained(&run_loop_)));
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+ run_loop_.Run();
+
+ // Regardless of the outcome of the race this thread shouldn't have been idle
+ // until the counter was ticked twice.
+ EXPECT_EQ(2, counter_);
+}
+
+// Verify that QuitWhenIdleClosure can be executed from another sequence.
+TEST_F(RunLoopTest, QuitWhenIdleFromOtherSequenceWithClosure) {
+ scoped_refptr<SequencedTaskRunner> other_sequence =
+ CreateSequencedTaskRunnerWithTraits({});
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+ other_sequence->PostTask(FROM_HERE, run_loop_.QuitWhenIdleClosure());
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ShouldRunTask, Unretained(&counter_)));
+
+ run_loop_.Run();
+
+ // Regardless of the outcome of the race this thread shouldn't have been idle
+ // until the counter was ticked twice.
+ EXPECT_EQ(2, counter_);
+}
+
TEST_F(RunLoopTest, IsRunningOnCurrentThread) {
EXPECT_FALSE(RunLoop::IsRunningOnCurrentThread());
ThreadTaskRunnerHandle::Get()->PostTask(
« no previous file with comments | « base/run_loop.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698