Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /refs/heads/main /. /base /run_loop_unittest.cc
blob: 856381e9a4fb570c8645c7b55f543c4445d5777c [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:06[diff] [blame]1// Copyright 2016 The Chromium Authors
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]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"base/run_loop.h"
6
jdoerrie9d7236f62019-03-05 13:00:23[diff] [blame]7#include<functional>
David Bienvenu5f4d4f02020-09-27 16:55:03[diff] [blame]8#include<memory>
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]9#include<utility>
10
Brett Wilsona62d9c02017-09-20 20:53:20[diff] [blame]11#include"base/containers/queue.h"
Avi Drissman63e1f992023-01-13 18:54:43[diff] [blame]12#include"base/functional/bind.h"
13#include"base/functional/callback_helpers.h"
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]14#include"base/location.h"
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]15#include"base/memory/ptr_util.h"
16#include"base/memory/ref_counted.h"
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]17#include"base/synchronization/lock.h"
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]18#include"base/synchronization/waitable_event.h"
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]19#include"base/task/sequenced_task_runner.h"
Patrick Monette643cdf62021-10-15 19:13:42[diff] [blame]20#include"base/task/single_thread_task_runner.h"
Guido Urdanetaef4e91942020-11-09 15:06:24[diff] [blame]21#include"base/test/bind.h"
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]22#include"base/test/gtest_util.h"
Wez9d5dd282020-02-10 17:21:22[diff] [blame]23#include"base/test/scoped_run_loop_timeout.h"
Gabriel Charettec7108742019-08-23 03:31:40[diff] [blame]24#include"base/test/task_environment.h"
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]25#include"base/test/test_timeouts.h"
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]26#include"base/threading/platform_thread.h"
27#include"base/threading/thread.h"
28#include"base/threading/thread_checker_impl.h"
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]29#include"build/build_config.h"
gab7af9dc02017-05-05 13:38:54[diff] [blame]30#include"testing/gmock/include/gmock/gmock.h"
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]31#include"testing/gtest/include/gtest/gtest.h"
32
33namespace base{
34
35namespace{
36
37voidQuitWhenIdleTask(RunLoop* run_loop,int* counter){
38 run_loop->QuitWhenIdle();
39++(*counter);
40}
41
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]42voidRunNestedLoopTask(int* counter){
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]43RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]44
45// This task should quit |nested_run_loop| but not the main RunLoop.
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]46SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
tzik92b7a422017-04-11 15:00:44[diff] [blame]47 FROM_HERE,BindOnce(&QuitWhenIdleTask,Unretained(&nested_run_loop),
48Unretained(counter)));
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]49
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]50SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Peter Kasting53fd6ee2021-10-05 20:40:48[diff] [blame]51 FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE),Days(1));
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]52
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]53 nested_run_loop.Run();
54
55++(*counter);
56}
57
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]58// A simple SingleThreadTaskRunner that just queues undelayed tasks (and ignores
59// delayed tasks). Tasks can then be processed one by one by ProcessTask() which
60// will return true if it processed a task and false otherwise.
61classSimpleSingleThreadTaskRunner:publicSingleThreadTaskRunner{
62public:
63SimpleSingleThreadTaskRunner()=default;
David Bienvenu5f4d4f02020-09-27 16:55:03[diff] [blame]64SimpleSingleThreadTaskRunner(constSimpleSingleThreadTaskRunner&)=delete;
65SimpleSingleThreadTaskRunner&operator=(constSimpleSingleThreadTaskRunner&)=
66delete;
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]67
Brett Wilson8e88b312017-09-12 05:22:16[diff] [blame]68boolPostDelayedTask(constLocation& from_here,
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]69OnceClosure task,
70 base::TimeDelta delay) override{
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]71if(delay.is_positive()){
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]72returnfalse;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]73}
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]74AutoLock auto_lock(tasks_lock_);
75 pending_tasks_.push(std::move(task));
76returntrue;
77}
78
Brett Wilson8e88b312017-09-12 05:22:16[diff] [blame]79boolPostNonNestableDelayedTask(constLocation& from_here,
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]80OnceClosure task,
81 base::TimeDelta delay) override{
82returnPostDelayedTask(from_here, std::move(task), delay);
83}
84
85boolRunsTasksInCurrentSequence()const override{
86return origin_thread_checker_.CalledOnValidThread();
87}
88
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]89boolProcessSingleTask(){
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]90OnceClosure task;
91{
92AutoLock auto_lock(tasks_lock_);
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]93if(pending_tasks_.empty()){
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]94returnfalse;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]95}
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]96 task= std::move(pending_tasks_.front());
97 pending_tasks_.pop();
98}
99// It's important to Run() after pop() and outside the lock as |task| may
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]100// run a nested loop which will re-enter ProcessSingleTask().
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]101 std::move(task).Run();
102returntrue;
103}
104
105private:
106~SimpleSingleThreadTaskRunner() override=default;
107
108Lock tasks_lock_;
Brett Wilsona62d9c02017-09-20 20:53:20[diff] [blame]109 base::queue<OnceClosure> pending_tasks_;
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]110
111// RunLoop relies on RunsTasksInCurrentSequence() signal. Use a
112// ThreadCheckerImpl to be able to reliably provide that signal even in
113// non-dcheck builds.
114ThreadCheckerImpl origin_thread_checker_;
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]115};
116
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]117// The basis of all TestDelegates, allows safely injecting a OnceClosure to be
118// run in the next idle phase of this delegate's Run() implementation. This can
119// be used to have code run on a thread that is otherwise livelocked in an idle
120// phase (sometimes a simple PostTask() won't do it -- e.g. when processing
121// application tasks is disallowed).
122classInjectableTestDelegate:publicRunLoop::Delegate{
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]123public:
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]124voidInjectClosureOnDelegate(OnceClosure closure){
125AutoLock auto_lock(closure_lock_);
126 closure_= std::move(closure);
127}
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]128
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]129boolRunInjectedClosure(){
130AutoLock auto_lock(closure_lock_);
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]131if(closure_.is_null()){
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]132returnfalse;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]133}
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]134 std::move(closure_).Run();
135returntrue;
136}
137
138private:
139Lock closure_lock_;
140OnceClosure closure_;
141};
142
143// A simple test RunLoop::Delegate to exercise Runloop logic independent of any
144// other base constructs. BindToCurrentThread() must be called before this
145// TestBoundDelegate is operational.
146classTestBoundDelegate final:publicInjectableTestDelegate{
147public:
148TestBoundDelegate()=default;
149
150// Makes this TestBoundDelegate become the RunLoop::Delegate and
Sean Maher70f2942932023-01-04 22:15:06[diff] [blame]151// SingleThreadTaskRunner::CurrentDefaultHandle for this thread.
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]152voidBindToCurrentThread(){
153 thread_task_runner_handle_=
Sean Maher9d43acc2022-12-01 17:42:01[diff] [blame]154 std::make_unique<SingleThreadTaskRunner::CurrentDefaultHandle>(
155 simple_task_runner_);
Gabriel Charettea3ec9612017-12-14 17:22:40[diff] [blame]156RunLoop::RegisterDelegateForCurrentThread(this);
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]157}
158
159private:
Alex Clarke9752bbf2019-04-01 10:41:20[diff] [blame]160voidRun(bool application_tasks_allowed,TimeDelta timeout) override{
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]161if(nested_run_allowing_tasks_incoming_){
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]162 EXPECT_TRUE(RunLoop::IsNestedOnCurrentThread());
Gabriel Charetteb030a4a2017-10-26 01:04:40[diff] [blame]163 EXPECT_TRUE(application_tasks_allowed);
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]164}elseif(RunLoop::IsNestedOnCurrentThread()){
Gabriel Charetteb030a4a2017-10-26 01:04:40[diff] [blame]165 EXPECT_FALSE(application_tasks_allowed);
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]166}
167 nested_run_allowing_tasks_incoming_=false;
168
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]169while(!should_quit_){
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]170if(application_tasks_allowed&&
171 simple_task_runner_->ProcessSingleTask()){
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]172continue;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]173}
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]174
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]175if(ShouldQuitWhenIdle()){
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]176break;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]177}
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]178
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]179if(RunInjectedClosure()){
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]180continue;
Peter Kasting134ef9af2024-12-28 02:30:09[diff] [blame]181}
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]182
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]183PlatformThread::YieldCurrentThread();
184}
185 should_quit_=false;
186}
187
188voidQuit() override{ should_quit_=true;}
189
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]190voidEnsureWorkScheduled() override{
191 nested_run_allowing_tasks_incoming_=true;
192}
193
194// True if the next invocation of Run() is expected to be from a
195// kNestableTasksAllowed RunLoop.
196bool nested_run_allowing_tasks_incoming_=false;
197
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]198 scoped_refptr<SimpleSingleThreadTaskRunner> simple_task_runner_=
199MakeRefCounted<SimpleSingleThreadTaskRunner>();
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]200
Sean Maher9d43acc2022-12-01 17:42:01[diff] [blame]201 std::unique_ptr<SingleThreadTaskRunner::CurrentDefaultHandle>
202 thread_task_runner_handle_;
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]203
204bool should_quit_=false;
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]205};
206
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]207enumclassRunLoopTestType{
Gabriel Charette694c3c332019-08-19 14:53:05[diff] [blame]208// Runs all RunLoopTests under a TaskEnvironment to make sure real world
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]209// scenarios work.
210 kRealEnvironment,
211
212// Runs all RunLoopTests under a test RunLoop::Delegate to make sure the
213// delegate interface fully works standalone.
214 kTestDelegate,
215};
216
217// The task environment for the RunLoopTest of a given type. A separate class
218// so it can be instantiated on the stack in the RunLoopTest fixture.
219classRunLoopTestEnvironment{
220public:
David Bienvenu5f4d4f02020-09-27 16:55:03[diff] [blame]221explicitRunLoopTestEnvironment(RunLoopTestType type){
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]222switch(type){
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]223caseRunLoopTestType::kRealEnvironment:{
Gabriel Charette694c3c332019-08-19 14:53:05[diff] [blame]224 task_environment_= std::make_unique<test::TaskEnvironment>();
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]225break;
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]226}
227caseRunLoopTestType::kTestDelegate:{
228auto test_delegate= std::make_unique<TestBoundDelegate>();
229 test_delegate->BindToCurrentThread();
230 test_delegate_= std::move(test_delegate);
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]231break;
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]232}
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]233}
234}
235
236private:
Gabriel Charette50518fc2018-05-22 17:53:22[diff] [blame]237// Instantiates one or the other based on the RunLoopTestType.
Gabriel Charette694c3c332019-08-19 14:53:05[diff] [blame]238 std::unique_ptr<test::TaskEnvironment> task_environment_;
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]239 std::unique_ptr<InjectableTestDelegate> test_delegate_;
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]240};
241
242classRunLoopTest:public testing::TestWithParam<RunLoopTestType>{
David Bienvenu5f4d4f02020-09-27 16:55:03[diff] [blame]243public:
244RunLoopTest(constRunLoopTest&)=delete;
245RunLoopTest&operator=(constRunLoopTest&)=delete;
246
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]247protected:
248RunLoopTest(): test_environment_(GetParam()){}
249
250RunLoopTestEnvironment test_environment_;
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]251RunLoop run_loop_;
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]252};
253
254}// namespace
255
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]256TEST_P(RunLoopTest,QuitWhenIdle){
Wezbbffcc52019-02-21 02:01:20[diff] [blame]257int counter=0;
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]258SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
tzik92b7a422017-04-11 15:00:44[diff] [blame]259 FROM_HERE,BindOnce(&QuitWhenIdleTask,Unretained(&run_loop_),
Wezbbffcc52019-02-21 02:01:20[diff] [blame]260Unretained(&counter)));
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]261SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
262 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
263SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Peter Kasting53fd6ee2021-10-05 20:40:48[diff] [blame]264 FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE),Days(1));
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]265
266 run_loop_.Run();
Wezbbffcc52019-02-21 02:01:20[diff] [blame]267 EXPECT_EQ(1, counter);
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]268}
269
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]270TEST_P(RunLoopTest,QuitWhenIdleNestedLoop){
Wezbbffcc52019-02-21 02:01:20[diff] [blame]271int counter=0;
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]272SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Wezbbffcc52019-02-21 02:01:20[diff] [blame]273 FROM_HERE,BindOnce(&RunNestedLoopTask,Unretained(&counter)));
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]274SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
tzik92b7a422017-04-11 15:00:44[diff] [blame]275 FROM_HERE,BindOnce(&QuitWhenIdleTask,Unretained(&run_loop_),
Wezbbffcc52019-02-21 02:01:20[diff] [blame]276Unretained(&counter)));
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]277SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
278 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
279SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Peter Kasting53fd6ee2021-10-05 20:40:48[diff] [blame]280 FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE),Days(1));
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]281
282 run_loop_.Run();
Wezbbffcc52019-02-21 02:01:20[diff] [blame]283 EXPECT_EQ(3, counter);
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]284}
285
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]286TEST_P(RunLoopTest,QuitWhenIdleClosure){
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]287SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
288 FROM_HERE, run_loop_.QuitWhenIdleClosure());
289SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
290 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
291SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Peter Kasting53fd6ee2021-10-05 20:40:48[diff] [blame]292 FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE),Days(1));
fdoraya3658602016-06-10 18:23:15[diff] [blame]293
294 run_loop_.Run();
fdoraya3658602016-06-10 18:23:15[diff] [blame]295}
296
297// Verify that the QuitWhenIdleClosure() can run after the RunLoop has been
298// deleted. It should have no effect.
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]299TEST_P(RunLoopTest,QuitWhenIdleClosureAfterRunLoopScope){
kylechar650caf02019-07-17 03:25:41[diff] [blame]300RepeatingClosure quit_when_idle_closure;
fdoraya3658602016-06-10 18:23:15[diff] [blame]301{
302RunLoop run_loop;
303 quit_when_idle_closure= run_loop.QuitWhenIdleClosure();
304 run_loop.RunUntilIdle();
305}
306 quit_when_idle_closure.Run();
307}
308
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]309// Verify that Quit can be executed from another sequence.
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]310TEST_P(RunLoopTest,QuitFromOtherSequence){
311Thread other_thread("test");
312 other_thread.Start();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]313 scoped_refptr<SequencedTaskRunner> other_sequence=
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]314 other_thread.task_runner();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]315
316// Always expected to run before asynchronous Quit() kicks in.
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]317SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
318 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]319
320WaitableEvent loop_was_quit(WaitableEvent::ResetPolicy::MANUAL,
321WaitableEvent::InitialState::NOT_SIGNALED);
322 other_sequence->PostTask(
323 FROM_HERE, base::BindOnce([](RunLoop* run_loop){ run_loop->Quit();},
324Unretained(&run_loop_)));
325 other_sequence->PostTask(
326 FROM_HERE,
327 base::BindOnce(&WaitableEvent::Signal, base::Unretained(&loop_was_quit)));
328
329// Anything that's posted after the Quit closure was posted back to this
330// sequence shouldn't get a chance to run.
331 loop_was_quit.Wait();
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]332SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
333 FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE));
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]334
335 run_loop_.Run();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]336}
337
338// Verify that QuitClosure can be executed from another sequence.
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]339TEST_P(RunLoopTest,QuitFromOtherSequenceWithClosure){
340Thread other_thread("test");
341 other_thread.Start();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]342 scoped_refptr<SequencedTaskRunner> other_sequence=
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]343 other_thread.task_runner();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]344
345// Always expected to run before asynchronous Quit() kicks in.
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]346SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
347 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]348
349WaitableEvent loop_was_quit(WaitableEvent::ResetPolicy::MANUAL,
350WaitableEvent::InitialState::NOT_SIGNALED);
351 other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure());
352 other_sequence->PostTask(
353 FROM_HERE,
354 base::BindOnce(&WaitableEvent::Signal, base::Unretained(&loop_was_quit)));
355
356// Anything that's posted after the Quit closure was posted back to this
357// sequence shouldn't get a chance to run.
358 loop_was_quit.Wait();
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]359SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
360 FROM_HERE,MakeExpectedNotRunClosure(FROM_HERE));
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]361
362 run_loop_.Run();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]363}
364
365// Verify that Quit can be executed from another sequence even when the
366// Quit is racing with Run() -- i.e. forgo the WaitableEvent used above.
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]367TEST_P(RunLoopTest,QuitFromOtherSequenceRacy){
368Thread other_thread("test");
369 other_thread.Start();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]370 scoped_refptr<SequencedTaskRunner> other_sequence=
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]371 other_thread.task_runner();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]372
373// Always expected to run before asynchronous Quit() kicks in.
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]374SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
375 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]376
Wezbbffcc52019-02-21 02:01:20[diff] [blame]377 other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure());
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]378
379 run_loop_.Run();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]380}
381
382// Verify that QuitClosure can be executed from another sequence even when the
383// Quit is racing with Run() -- i.e. forgo the WaitableEvent used above.
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]384TEST_P(RunLoopTest,QuitFromOtherSequenceRacyWithClosure){
385Thread other_thread("test");
386 other_thread.Start();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]387 scoped_refptr<SequencedTaskRunner> other_sequence=
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]388 other_thread.task_runner();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]389
390// Always expected to run before asynchronous Quit() kicks in.
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]391SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
392 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]393
394 other_sequence->PostTask(FROM_HERE, run_loop_.QuitClosure());
395
396 run_loop_.Run();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]397}
398
399// Verify that QuitWhenIdle can be executed from another sequence.
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]400TEST_P(RunLoopTest,QuitWhenIdleFromOtherSequence){
401Thread other_thread("test");
402 other_thread.Start();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]403 scoped_refptr<SequencedTaskRunner> other_sequence=
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]404 other_thread.task_runner();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]405
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]406SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
407 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]408
409 other_sequence->PostTask(
410 FROM_HERE,
411 base::BindOnce([](RunLoop* run_loop){ run_loop->QuitWhenIdle();},
412Unretained(&run_loop_)));
413
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]414SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
415 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]416
417 run_loop_.Run();
418
419// Regardless of the outcome of the race this thread shouldn't have been idle
Wezbbffcc52019-02-21 02:01:20[diff] [blame]420// until both tasks posted to this sequence have run.
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]421}
422
423// Verify that QuitWhenIdleClosure can be executed from another sequence.
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]424TEST_P(RunLoopTest,QuitWhenIdleFromOtherSequenceWithClosure){
425Thread other_thread("test");
426 other_thread.Start();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]427 scoped_refptr<SequencedTaskRunner> other_sequence=
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]428 other_thread.task_runner();
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]429
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]430SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
431 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]432
433 other_sequence->PostTask(FROM_HERE, run_loop_.QuitWhenIdleClosure());
434
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]435SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
436 FROM_HERE,MakeExpectedRunClosure(FROM_HERE));
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]437
438 run_loop_.Run();
439
440// Regardless of the outcome of the race this thread shouldn't have been idle
Wezbbffcc52019-02-21 02:01:20[diff] [blame]441// until the both tasks posted to this sequence have run.
gabcf5e4ce2017-05-19 22:56:57[diff] [blame]442}
443
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]444TEST_P(RunLoopTest,IsRunningOnCurrentThread){
gab7af9dc02017-05-05 13:38:54[diff] [blame]445 EXPECT_FALSE(RunLoop::IsRunningOnCurrentThread());
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]446SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
gab7af9dc02017-05-05 13:38:54[diff] [blame]447 FROM_HERE,
Sorin Jianuad4cc6232024-10-08 19:06:01[diff] [blame]448BindOnce([]{ EXPECT_TRUE(RunLoop::IsRunningOnCurrentThread());}));
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]449SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
450 FROM_HERE, run_loop_.QuitClosure());
gab7af9dc02017-05-05 13:38:54[diff] [blame]451 run_loop_.Run();
452}
453
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]454TEST_P(RunLoopTest,IsNestedOnCurrentThread){
gab7af9dc02017-05-05 13:38:54[diff] [blame]455 EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
456
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]457SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Sorin Jianuad4cc6232024-10-08 19:06:01[diff] [blame]458 FROM_HERE,BindOnce([]{
gab7af9dc02017-05-05 13:38:54[diff] [blame]459 EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
460
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]461RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
gab7af9dc02017-05-05 13:38:54[diff] [blame]462
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]463SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Sorin Jianuad4cc6232024-10-08 19:06:01[diff] [blame]464 FROM_HERE,
465BindOnce([]{ EXPECT_TRUE(RunLoop::IsNestedOnCurrentThread());}));
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]466SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
467 FROM_HERE, nested_run_loop.QuitClosure());
gab7af9dc02017-05-05 13:38:54[diff] [blame]468
469 EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
gab7af9dc02017-05-05 13:38:54[diff] [blame]470 nested_run_loop.Run();
471 EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
472}));
473
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]474SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
475 FROM_HERE, run_loop_.QuitClosure());
gab7af9dc02017-05-05 13:38:54[diff] [blame]476 run_loop_.Run();
477}
478
Gabriel Charette2a53350172021-05-06 20:22:55[diff] [blame]479TEST_P(RunLoopTest,CannotRunMoreThanOnce){
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]480SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
481 FROM_HERE, run_loop_.QuitClosure());
Gabriel Charette2a53350172021-05-06 20:22:55[diff] [blame]482 run_loop_.Run();
483 EXPECT_DCHECK_DEATH({ run_loop_.Run();});
484}
485
486TEST_P(RunLoopTest,CanRunUntilIdleMoreThanOnce){
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]487SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,DoNothing());
Gabriel Charette2a53350172021-05-06 20:22:55[diff] [blame]488 run_loop_.RunUntilIdle();
489
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]490SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,DoNothing());
Gabriel Charette2a53350172021-05-06 20:22:55[diff] [blame]491 run_loop_.RunUntilIdle();
492 run_loop_.RunUntilIdle();
493}
494
495TEST_P(RunLoopTest,CanRunUntilIdleThenRunIfNotQuit){
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]496SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,DoNothing());
Gabriel Charette2a53350172021-05-06 20:22:55[diff] [blame]497 run_loop_.RunUntilIdle();
498
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]499SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
500 FROM_HERE, run_loop_.QuitClosure());
Gabriel Charette2a53350172021-05-06 20:22:55[diff] [blame]501 run_loop_.Run();
502}
503
504TEST_P(RunLoopTest,CannotRunUntilIdleThenRunIfQuit){
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]505SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
506 FROM_HERE, run_loop_.QuitClosure());
Gabriel Charette2a53350172021-05-06 20:22:55[diff] [blame]507 run_loop_.RunUntilIdle();
508
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]509SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,DoNothing());
Gabriel Charette2a53350172021-05-06 20:22:55[diff] [blame]510 EXPECT_DCHECK_DEATH({ run_loop_.Run();});
511}
512
513TEST_P(RunLoopTest,CannotRunAgainIfQuitWhenIdle){
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]514SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
515 FROM_HERE, run_loop_.QuitWhenIdleClosure());
Gabriel Charette2a53350172021-05-06 20:22:55[diff] [blame]516 run_loop_.RunUntilIdle();
517
518 EXPECT_DCHECK_DEATH({ run_loop_.RunUntilIdle();});
519}
520
Francois Doray80bdddf2018-01-04 16:17:32[diff] [blame]521namespace{
522
gab7af9dc02017-05-05 13:38:54[diff] [blame]523classMockNestingObserver:publicRunLoop::NestingObserver{
524public:
525MockNestingObserver()=default;
David Bienvenu5f4d4f02020-09-27 16:55:03[diff] [blame]526MockNestingObserver(constMockNestingObserver&)=delete;
527MockNestingObserver&operator=(constMockNestingObserver&)=delete;
gab7af9dc02017-05-05 13:38:54[diff] [blame]528
529// RunLoop::NestingObserver:
Lei Zhange9f2f482025-03-04 22:40:26[diff] [blame]530 MOCK_METHOD(void,OnBeginNestedRunLoop,());
531 MOCK_METHOD(void,OnExitNestedRunLoop,());
gab7af9dc02017-05-05 13:38:54[diff] [blame]532};
533
Francois Doray80bdddf2018-01-04 16:17:32[diff] [blame]534classMockTask{
535public:
536MockTask()=default;
David Bienvenu5f4d4f02020-09-27 16:55:03[diff] [blame]537MockTask(constMockTask&)=delete;
538MockTask&operator=(constMockTask&)=delete;
Francois Doray80bdddf2018-01-04 16:17:32[diff] [blame]539
David Bienvenu5f4d4f02020-09-27 16:55:03[diff] [blame]540 MOCK_METHOD0(Task,void());
Francois Doray80bdddf2018-01-04 16:17:32[diff] [blame]541};
542
543}// namespace
544
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]545TEST_P(RunLoopTest,NestingObservers){
gab7af9dc02017-05-05 13:38:54[diff] [blame]546 testing::StrictMock<MockNestingObserver> nesting_observer;
Francois Doray80bdddf2018-01-04 16:17:32[diff] [blame]547 testing::StrictMock<MockTask> mock_task_a;
548 testing::StrictMock<MockTask> mock_task_b;
gab7af9dc02017-05-05 13:38:54[diff] [blame]549
550RunLoop::AddNestingObserverOnCurrentThread(&nesting_observer);
551
Sorin Jianuad4cc6232024-10-08 19:06:01[diff] [blame]552constRepeatingClosure run_nested_loop=BindRepeating([]{
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]553RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]554SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
555 FROM_HERE, nested_run_loop.QuitClosure());
gab7af9dc02017-05-05 13:38:54[diff] [blame]556 nested_run_loop.Run();
557});
558
Francois Doray80bdddf2018-01-04 16:17:32[diff] [blame]559// Generate a stack of nested RunLoops. OnBeginNestedRunLoop() is expected
560// when beginning each nesting depth and OnExitNestedRunLoop() is expected
Gabriel Charetted8839442018-03-15 18:56:22[diff] [blame]561// when exiting each nesting depth. Each one of these tasks is ahead of the
562// QuitClosures as those are only posted at the end of the queue when
563// |run_nested_loop| is executed.
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]564SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,
565 run_nested_loop);
566SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Francois Doray80bdddf2018-01-04 16:17:32[diff] [blame]567 FROM_HERE,
568 base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_a)));
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]569SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,
570 run_nested_loop);
571SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Francois Doray80bdddf2018-01-04 16:17:32[diff] [blame]572 FROM_HERE,
573 base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_b)));
gab7af9dc02017-05-05 13:38:54[diff] [blame]574
Francois Doray80bdddf2018-01-04 16:17:32[diff] [blame]575{
576 testing::InSequence in_sequence;
577 EXPECT_CALL(nesting_observer,OnBeginNestedRunLoop());
578 EXPECT_CALL(mock_task_a,Task());
579 EXPECT_CALL(nesting_observer,OnBeginNestedRunLoop());
580 EXPECT_CALL(mock_task_b,Task());
581 EXPECT_CALL(nesting_observer,OnExitNestedRunLoop()).Times(2);
582}
583 run_loop_.RunUntilIdle();
gab7af9dc02017-05-05 13:38:54[diff] [blame]584
585RunLoop::RemoveNestingObserverOnCurrentThread(&nesting_observer);
586}
587
Minoru Chikamune76ba6822021-01-12 10:39:51[diff] [blame]588TEST_P(RunLoopTest,DisallowRunning){
Hans Wennborg9b292ba2022-02-15 16:01:29[diff] [blame]589ScopedDisallowRunningRunLoop disallow_running;
Gabriel Charettef3fd8f02018-05-22 15:31:48[diff] [blame]590 EXPECT_DCHECK_DEATH({ run_loop_.RunUntilIdle();});
Gabriel Charettea44975052017-08-21 23:14:04[diff] [blame]591}
592
Minoru Chikamune76ba6822021-01-12 10:39:51[diff] [blame]593TEST_P(RunLoopTest,ExpiredDisallowRunning){
Hans Wennborg9b292ba2022-02-15 16:01:29[diff] [blame]594{ScopedDisallowRunningRunLoop disallow_running;}
Gabriel Charettea44975052017-08-21 23:14:04[diff] [blame]595// Running should be fine after |disallow_running| goes out of scope.
596 run_loop_.RunUntilIdle();
597}
598
Victor Costan033b9ac2019-01-29 00:52:16[diff] [blame]599INSTANTIATE_TEST_SUITE_P(Real,
600RunLoopTest,
601 testing::Values(RunLoopTestType::kRealEnvironment));
602INSTANTIATE_TEST_SUITE_P(Mock,
603RunLoopTest,
604 testing::Values(RunLoopTestType::kTestDelegate));
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]605
606TEST(RunLoopDeathTest,MustRegisterBeforeInstantiating){
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]607TestBoundDelegate unbound_test_delegate_;
Sean Maher70f2942932023-01-04 22:15:06[diff] [blame]608// RunLoop::RunLoop() should CHECK fetching the
609// SingleThreadTaskRunner::CurrentDefaultHandle.
Wez39ee6102018-04-14 21:33:59[diff] [blame]610 EXPECT_DEATH_IF_SUPPORTED({RunLoop();},"");
Gabriel Charettee2b632b2017-08-02 03:52:16[diff] [blame]611}
612
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]613TEST(RunLoopDelegateTest,NestableTasksDontRunInDefaultNestedLoops){
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]614TestBoundDelegate test_delegate;
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]615 test_delegate.BindToCurrentThread();
616
617 base::Thread other_thread("test");
618 other_thread.Start();
619
620RunLoop main_loop;
621// A nested run loop which isn't kNestableTasksAllowed.
622RunLoop nested_run_loop(RunLoop::Type::kDefault);
623
624bool nested_run_loop_ended=false;
625
626// The first task on the main loop will result in a nested run loop. Since
627// it's not kNestableTasksAllowed, no further task should be processed until
628// it's quit.
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]629SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]630 FROM_HERE,
631BindOnce([](RunLoop* nested_run_loop){ nested_run_loop->Run();},
632Unretained(&nested_run_loop)));
633
634// Post a task that will fail if it runs inside the nested run loop.
Sean Maher7d0e8052022-12-09 01:46:32[diff] [blame]635SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
jdoerrie9d7236f62019-03-05 13:00:23[diff] [blame]636 FROM_HERE,
637BindOnce(
638[](constbool& nested_run_loop_ended,
639OnceClosure continuation_callback){
640 EXPECT_TRUE(nested_run_loop_ended);
641 EXPECT_FALSE(RunLoop::IsNestedOnCurrentThread());
642 std::move(continuation_callback).Run();
643},
644 std::cref(nested_run_loop_ended), main_loop.QuitClosure()));
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]645
646// Post a task flipping the boolean bit for extra verification right before
647// quitting |nested_run_loop|.
648 other_thread.task_runner()->PostDelayedTask(
649 FROM_HERE,
650BindOnce(
651[](bool* nested_run_loop_ended){
652 EXPECT_FALSE(*nested_run_loop_ended);
653*nested_run_loop_ended=true;
654},
655Unretained(&nested_run_loop_ended)),
656TestTimeouts::tiny_timeout());
657// Post an async delayed task to exit the run loop when idle. This confirms
658// that (1) the test task only ran in the main loop after the nested loop
659// exited and (2) the nested run loop actually considers itself idle while
660// spinning. Note: The quit closure needs to be injected directly on the
661// delegate as invoking QuitWhenIdle() off-thread results in a thread bounce
662// which will not processed because of the very logic under test (nestable
663// tasks don't run in |nested_run_loop|).
664 other_thread.task_runner()->PostDelayedTask(
665 FROM_HERE,
666BindOnce(
Gabriel Charette1ef212b2017-12-03 12:47:21[diff] [blame]667[](TestBoundDelegate* test_delegate,OnceClosure injected_closure){
668 test_delegate->InjectClosureOnDelegate(std::move(injected_closure));
Gabriel Charette3ff403e2017-08-07 04:22:48[diff] [blame]669},
670Unretained(&test_delegate), nested_run_loop.QuitWhenIdleClosure()),
671TestTimeouts::tiny_timeout());
672
673 main_loop.Run();
674}
675
fdoraya4f28ec2016-06-10 00:08:58[diff] [blame]676}// namespace base

[8]ページ先頭

©2009-2025 Movatter.jp