• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
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/message_loop/message_pump.h"
6 
7 #include <type_traits>
8 
9 #include "base/functional/bind.h"
10 #include "base/logging.h"
11 #include "base/memory/raw_ptr.h"
12 #include "base/message_loop/message_pump_for_io.h"
13 #include "base/message_loop/message_pump_for_ui.h"
14 #include "base/message_loop/message_pump_type.h"
15 #include "base/run_loop.h"
16 #include "base/task/single_thread_task_executor.h"
17 #include "base/test/bind.h"
18 #include "base/test/test_timeouts.h"
19 #include "base/threading/thread.h"
20 #include "build/build_config.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 
24 #if BUILDFLAG(IS_WIN)
25 #include <windows.h>
26 #endif
27 
28 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)
29 #include "base/message_loop/message_pump_libevent.h"
30 #endif
31 
32 using ::testing::_;
33 using ::testing::AnyNumber;
34 using ::testing::AtMost;
35 using ::testing::Invoke;
36 using ::testing::Return;
37 
38 namespace base {
39 
40 namespace {
41 
42 // On most platforms, the MessagePump impl controls when native work (e.g.
43 // handling input messages) gets its turn. Tests below verify that by expecting
44 // OnBeginWorkItem() calls that cover native work. In some configurations
45 // however, the platform owns the message loop and is the one yielding to
46 // Chrome's MessagePump to DoWork(). Under those configurations, it is not
47 // possible to precisely account for OnBeginWorkItem() calls as they can occur
48 // nondeterministically. For example, on some versions of iOS, the native loop
49 // can surprisingly go through multiple cycles of
50 // kCFRunLoopAfterWaiting=>kCFRunLoopBeforeWaiting before invoking Chrome's
51 // RunWork() for the first time, triggering multiple  ScopedDoWorkItem 's for
52 // potential native work before the first DoWork().
ChromeControlsNativeEventProcessing(MessagePumpType pump_type)53 constexpr bool ChromeControlsNativeEventProcessing(MessagePumpType pump_type) {
54 #if BUILDFLAG(IS_MAC)
55   return pump_type != MessagePumpType::UI;
56 #elif BUILDFLAG(IS_IOS)
57   return false;
58 #else
59   return true;
60 #endif
61 }
62 
63 class MockMessagePumpDelegate : public MessagePump::Delegate {
64  public:
MockMessagePumpDelegate(MessagePumpType pump_type)65   explicit MockMessagePumpDelegate(MessagePumpType pump_type)
66       : check_work_items_(ChromeControlsNativeEventProcessing(pump_type)),
67         native_work_item_accounting_is_on_(
68             !ChromeControlsNativeEventProcessing(pump_type)) {}
69 
~MockMessagePumpDelegate()70   ~MockMessagePumpDelegate() override { ValidateNoOpenWorkItems(); }
71 
72   MockMessagePumpDelegate(const MockMessagePumpDelegate&) = delete;
73   MockMessagePumpDelegate& operator=(const MockMessagePumpDelegate&) = delete;
74 
BeforeWait()75   void BeforeWait() override {}
76   MOCK_METHOD0(DoWork, MessagePump::Delegate::NextWorkInfo());
77   MOCK_METHOD0(DoIdleWork, bool());
78 
79   // Functions invoked directly by the message pump.
OnBeginWorkItem()80   void OnBeginWorkItem() override {
81     any_work_begun_ = true;
82 
83     if (check_work_items_) {
84       MockOnBeginWorkItem();
85     }
86 
87     ++work_item_count_;
88   }
89 
OnEndWorkItem(int run_level_depth)90   void OnEndWorkItem(int run_level_depth) override {
91     if (check_work_items_) {
92       MockOnEndWorkItem(run_level_depth);
93     }
94 
95     EXPECT_EQ(run_level_depth, work_item_count_);
96 
97     --work_item_count_;
98 
99     // It's not possible to close more scopes than there are open ones.
100     EXPECT_GE(work_item_count_, 0);
101   }
102 
RunDepth()103   int RunDepth() override { return work_item_count_; }
104 
ValidateNoOpenWorkItems()105   void ValidateNoOpenWorkItems() {
106     // Upon exiting there cannot be any open scopes.
107     EXPECT_EQ(work_item_count_, 0);
108 
109     if (native_work_item_accounting_is_on_) {
110 // Tests should trigger work beginning at least once except on iOS where
111 // they need a call to MessagePumpUIApplication::Attach() to do so when on
112 // the UI thread.
113 #if !BUILDFLAG(IS_IOS)
114       EXPECT_TRUE(any_work_begun_);
115 #endif
116     }
117   }
118 
119   // Mock functions for asserting.
120   MOCK_METHOD0(MockOnBeginWorkItem, void(void));
121   MOCK_METHOD1(MockOnEndWorkItem, void(int));
122 
123   // If native events are covered in the current configuration it's not
124   // possible to precisely test all assertions related to work items. This is
125   // because a number of speculative WorkItems are created during execution of
126   // such loops and it's not possible to determine their number before the
127   // execution of the test. In such configurations the functioning of the
128   // message pump is still verified by looking at the counts of opened and
129   // closed WorkItems.
130   const bool check_work_items_;
131   const bool native_work_item_accounting_is_on_;
132 
133   int work_item_count_ = 0;
134   bool any_work_begun_ = false;
135 };
136 
137 class MessagePumpTest : public ::testing::TestWithParam<MessagePumpType> {
138  public:
MessagePumpTest()139   MessagePumpTest() : message_pump_(MessagePump::Create(GetParam())) {}
140 
141  protected:
142 #if defined(USE_GLIB)
143   // Because of a GLIB implementation quirk, the pump doesn't do the same things
144   // between each DoWork. In this case, it won't set/clear a ScopedDoWorkItem
145   // because we run a chrome work item in the runloop outside of GLIB's control,
146   // so we oscillate between setting and not setting PreDoWorkExpectations.
147   std::map<MessagePump::Delegate*, int> do_work_counts;
148 #endif
AddPreDoWorkExpectations(testing::StrictMock<MockMessagePumpDelegate> & delegate)149   void AddPreDoWorkExpectations(
150       testing::StrictMock<MockMessagePumpDelegate>& delegate) {
151 #if BUILDFLAG(IS_WIN)
152     if (GetParam() == MessagePumpType::UI) {
153       // The Windows MessagePumpForUI may do native work from ::PeekMessage()
154       // and labels itself as such.
155       EXPECT_CALL(delegate, MockOnBeginWorkItem);
156       EXPECT_CALL(delegate, MockOnEndWorkItem);
157 
158       // If the above event was MessagePumpForUI's own kMsgHaveWork internal
159       // event, it will process another event to replace it (ref.
160       // ProcessPumpReplacementMessage).
161       EXPECT_CALL(delegate, MockOnBeginWorkItem).Times(AtMost(1));
162       EXPECT_CALL(delegate, MockOnEndWorkItem).Times(AtMost(1));
163     }
164 #endif  // BUILDFLAG(IS_WIN)
165 #if defined(USE_GLIB)
166     do_work_counts.try_emplace(&delegate, 0);
167     if (GetParam() == MessagePumpType::UI) {
168       if (++do_work_counts[&delegate] % 2) {
169         // The GLib MessagePump will do native work before chrome work on
170         // startup.
171         EXPECT_CALL(delegate, MockOnBeginWorkItem);
172         EXPECT_CALL(delegate, MockOnEndWorkItem);
173       }
174     }
175 #endif  // defined(USE_GLIB)
176   }
177 
AddPostDoWorkExpectations(testing::StrictMock<MockMessagePumpDelegate> & delegate)178   void AddPostDoWorkExpectations(
179       testing::StrictMock<MockMessagePumpDelegate>& delegate) {
180 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)
181     // MessagePumpLibEvent checks for native notifications once after processing
182     // a DoWork() but only instantiates a ScopedDoWorkItem that triggers
183     // MessagePumpLibevent::OnLibeventNotification() which this test does not
184     // so there are no post-work expectations at the moment.
185 #endif
186 #if defined(USE_GLIB)
187     if (GetParam() == MessagePumpType::UI) {
188       // The GLib MessagePump can create and destroy work items between DoWorks
189       // depending on internal state.
190       EXPECT_CALL(delegate, MockOnBeginWorkItem).Times(AtMost(1));
191       EXPECT_CALL(delegate, MockOnEndWorkItem).Times(AtMost(1));
192     }
193 #endif  // defined(USE_GLIB)
194   }
195 
196   std::unique_ptr<MessagePump> message_pump_;
197 };
198 
199 }  // namespace
200 
TEST_P(MessagePumpTest,QuitStopsWork)201 TEST_P(MessagePumpTest, QuitStopsWork) {
202   testing::InSequence sequence;
203   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
204 
205   AddPreDoWorkExpectations(delegate);
206 
207   // Not expecting any calls to DoIdleWork after quitting, nor any of the
208   // PostDoWorkExpectations, quitting should be instantaneous.
209   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
210     message_pump_->Quit();
211     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
212   }));
213 
214   // MessagePumpGlib uses a work item between a HandleDispatch() call and
215   // passing control back to the chrome loop, which handles the Quit() despite
216   // us not necessarily doing any native work during that time.
217 #if defined(USE_GLIB)
218   if (GetParam() == MessagePumpType::UI) {
219     AddPostDoWorkExpectations(delegate);
220   }
221 #endif
222 
223   EXPECT_CALL(delegate, DoIdleWork()).Times(0);
224 
225   message_pump_->ScheduleWork();
226   message_pump_->Run(&delegate);
227 }
228 
TEST_P(MessagePumpTest,QuitStopsWorkWithNestedRunLoop)229 TEST_P(MessagePumpTest, QuitStopsWorkWithNestedRunLoop) {
230   testing::InSequence sequence;
231   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
232   testing::StrictMock<MockMessagePumpDelegate> nested_delegate(GetParam());
233 
234   AddPreDoWorkExpectations(delegate);
235 
236   // We first schedule a call to DoWork, which runs a nested run loop. After
237   // the nested loop exits, we schedule another DoWork which quits the outer
238   // (original) run loop. The test verifies that there are no extra calls to
239   // DoWork after the outer loop quits.
240   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([&] {
241     message_pump_->Run(&nested_delegate);
242     // A null NextWorkInfo indicates immediate follow-up work.
243     return MessagePump::Delegate::NextWorkInfo();
244   }));
245 
246   AddPreDoWorkExpectations(nested_delegate);
247   EXPECT_CALL(nested_delegate, DoWork).WillOnce(Invoke([&] {
248     // Quit the nested run loop.
249     message_pump_->Quit();
250     // The underlying pump should process the next task in the first run-level
251     // regardless of whether the nested run-level indicates there's no more work
252     // (e.g. can happen when the only remaining tasks are non-nestable).
253     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
254   }));
255 
256   // The `nested_delegate` will quit first.
257   AddPostDoWorkExpectations(nested_delegate);
258 
259   // Return a delayed task with |yield_to_native| set, and exit.
260   AddPostDoWorkExpectations(delegate);
261 
262   AddPreDoWorkExpectations(delegate);
263 
264   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
265     message_pump_->Quit();
266     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
267   }));
268 
269   message_pump_->ScheduleWork();
270   message_pump_->Run(&delegate);
271 }
272 
TEST_P(MessagePumpTest,YieldToNativeRequestedSmokeTest)273 TEST_P(MessagePumpTest, YieldToNativeRequestedSmokeTest) {
274   // The handling of the "yield_to_native" boolean in the NextWorkInfo is only
275   // implemented on the MessagePumpForUI on android. However since we inject a
276   // fake one for testing this is hard to test. This test ensures that setting
277   // this boolean doesn't cause any MessagePump to explode.
278   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
279 
280   testing::InSequence sequence;
281 
282   // Return an immediate task with |yield_to_native| set.
283   AddPreDoWorkExpectations(delegate);
284   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([] {
285     return MessagePump::Delegate::NextWorkInfo{TimeTicks(), TimeTicks(),
286                                                /* yield_to_native = */ true};
287   }));
288   AddPostDoWorkExpectations(delegate);
289 
290   AddPreDoWorkExpectations(delegate);
291   // Return a delayed task with |yield_to_native| set, and exit.
292   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
293     message_pump_->Quit();
294     auto now = TimeTicks::Now();
295     return MessagePump::Delegate::NextWorkInfo{now + Milliseconds(1), now,
296                                                true};
297   }));
298   EXPECT_CALL(delegate, DoIdleWork()).Times(AnyNumber());
299 
300   message_pump_->ScheduleWork();
301   message_pump_->Run(&delegate);
302 }
303 
304 namespace {
305 
306 class TimerSlackTestDelegate : public MessagePump::Delegate {
307  public:
TimerSlackTestDelegate(MessagePump * message_pump)308   TimerSlackTestDelegate(MessagePump* message_pump)
309       : message_pump_(message_pump) {
310     // We first schedule a delayed task far in the future with maximum timer
311     // slack.
312     message_pump_->SetTimerSlack(TIMER_SLACK_MAXIMUM);
313     const TimeTicks now = TimeTicks::Now();
314     message_pump_->ScheduleDelayedWork({now + Hours(1), now});
315 
316     // Since we have no other work pending, the pump will initially be idle.
317     action_.store(NONE);
318   }
319 
OnBeginWorkItem()320   void OnBeginWorkItem() override {}
OnEndWorkItem(int run_level_depth)321   void OnEndWorkItem(int run_level_depth) override {}
RunDepth()322   int RunDepth() override { return 0; }
BeforeWait()323   void BeforeWait() override {}
324 
DoWork()325   MessagePump::Delegate::NextWorkInfo DoWork() override {
326     switch (action_.load()) {
327       case NONE:
328         break;
329       case SCHEDULE_DELAYED_WORK: {
330         // After being woken up by the other thread, we let the pump know that
331         // the next delayed task is in fact much sooner than the 1 hour delay it
332         // was aware of. If the pump refreshes its timer correctly, it will wake
333         // up shortly, finishing the test.
334         action_.store(QUIT);
335         TimeTicks now = TimeTicks::Now();
336         return {now + Milliseconds(50), now};
337       }
338       case QUIT:
339         message_pump_->Quit();
340         break;
341     }
342     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
343   }
344 
DoIdleWork()345   bool DoIdleWork() override { return false; }
346 
WakeUpFromOtherThread()347   void WakeUpFromOtherThread() {
348     action_.store(SCHEDULE_DELAYED_WORK);
349     message_pump_->ScheduleWork();
350   }
351 
352  private:
353   enum Action {
354     NONE,
355     SCHEDULE_DELAYED_WORK,
356     QUIT,
357   };
358 
359   const raw_ptr<MessagePump> message_pump_;
360   std::atomic<Action> action_;
361 };
362 
363 }  // namespace
364 
TEST_P(MessagePumpTest,TimerSlackWithLongDelays)365 TEST_P(MessagePumpTest, TimerSlackWithLongDelays) {
366   // This is a regression test for an issue where the iOS message pump fails to
367   // run delayed work when timer slack is enabled. The steps needed to trigger
368   // this are:
369   //
370   //  1. The message pump timer slack is set to maximum.
371   //  2. A delayed task is posted for far in the future (e.g., 1h).
372   //  3. The system goes idle at least for a few seconds.
373   //  4. Another delayed task is posted with a much smaller delay.
374   //
375   // The following message pump test delegate automatically runs through this
376   // sequence.
377   TimerSlackTestDelegate delegate(message_pump_.get());
378 
379   // We use another thread to wake up the pump after 2 seconds to allow the
380   // system to enter an idle state. This delay was determined experimentally on
381   // the iPhone 6S simulator.
382   Thread thread("Waking thread");
383   thread.StartAndWaitForTesting();
384   thread.task_runner()->PostDelayedTask(
385       FROM_HERE,
386       BindLambdaForTesting([&delegate] { delegate.WakeUpFromOtherThread(); }),
387       Seconds(2));
388 
389   message_pump_->Run(&delegate);
390 }
391 
TEST_P(MessagePumpTest,RunWithoutScheduleWorkInvokesDoWork)392 TEST_P(MessagePumpTest, RunWithoutScheduleWorkInvokesDoWork) {
393   testing::InSequence sequence;
394   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
395 
396   AddPreDoWorkExpectations(delegate);
397 
398   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
399     message_pump_->Quit();
400     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
401   }));
402 
403   AddPostDoWorkExpectations(delegate);
404 
405 #if BUILDFLAG(IS_IOS)
406   EXPECT_CALL(delegate, DoIdleWork).Times(AnyNumber());
407 #endif
408 
409   message_pump_->Run(&delegate);
410 }
411 
TEST_P(MessagePumpTest,NestedRunWithoutScheduleWorkInvokesDoWork)412 TEST_P(MessagePumpTest, NestedRunWithoutScheduleWorkInvokesDoWork) {
413   testing::InSequence sequence;
414   testing::StrictMock<MockMessagePumpDelegate> delegate(GetParam());
415   testing::StrictMock<MockMessagePumpDelegate> nested_delegate(GetParam());
416 
417   AddPreDoWorkExpectations(delegate);
418 
419   EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this, &nested_delegate] {
420     message_pump_->Run(&nested_delegate);
421     message_pump_->Quit();
422     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
423   }));
424 
425   AddPreDoWorkExpectations(nested_delegate);
426 
427   EXPECT_CALL(nested_delegate, DoWork).WillOnce(Invoke([this] {
428     message_pump_->Quit();
429     return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
430   }));
431 
432   // We quit `nested_delegate` before `delegate`
433   AddPostDoWorkExpectations(nested_delegate);
434 
435   AddPostDoWorkExpectations(delegate);
436 
437 #if BUILDFLAG(IS_IOS)
438   EXPECT_CALL(nested_delegate, DoIdleWork).Times(AnyNumber());
439   EXPECT_CALL(delegate, DoIdleWork).Times(AnyNumber());
440 #endif
441 
442   message_pump_->Run(&delegate);
443 }
444 
445 INSTANTIATE_TEST_SUITE_P(All,
446                          MessagePumpTest,
447                          ::testing::Values(MessagePumpType::DEFAULT,
448                                            MessagePumpType::UI,
449                                            MessagePumpType::IO));
450 
451 }  // namespace base
452