1 // Copyright (c) 2012 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 5 #include "base/message_loop/message_pump_glib.h" 6 7 #include <glib.h> 8 #include <math.h> 9 10 #include <algorithm> 11 #include <vector> 12 13 #include "base/bind.h" 14 #include "base/bind_helpers.h" 15 #include "base/callback.h" 16 #include "base/memory/ref_counted.h" 17 #include "base/message_loop/message_loop.h" 18 #include "base/run_loop.h" 19 #include "base/threading/thread.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 22 namespace base { 23 namespace { 24 25 // This class injects dummy "events" into the GLib loop. When "handled" these 26 // events can run tasks. This is intended to mock gtk events (the corresponding 27 // GLib source runs at the same priority). 28 class EventInjector { 29 public: 30 EventInjector() : processed_events_(0) { 31 source_ = static_cast<Source*>(g_source_new(&SourceFuncs, sizeof(Source))); 32 source_->injector = this; 33 g_source_attach(source_, NULL); 34 g_source_set_can_recurse(source_, TRUE); 35 } 36 37 ~EventInjector() { 38 g_source_destroy(source_); 39 g_source_unref(source_); 40 } 41 42 int HandlePrepare() { 43 // If the queue is empty, block. 44 if (events_.empty()) 45 return -1; 46 TimeDelta delta = events_[0].time - Time::NowFromSystemTime(); 47 return std::max(0, static_cast<int>(ceil(delta.InMillisecondsF()))); 48 } 49 50 bool HandleCheck() { 51 if (events_.empty()) 52 return false; 53 return events_[0].time <= Time::NowFromSystemTime(); 54 } 55 56 void HandleDispatch() { 57 if (events_.empty()) 58 return; 59 Event event = events_[0]; 60 events_.erase(events_.begin()); 61 ++processed_events_; 62 if (!event.callback.is_null()) 63 event.callback.Run(); 64 else if (!event.task.is_null()) 65 event.task.Run(); 66 } 67 68 // Adds an event to the queue. When "handled", executes |callback|. 69 // delay_ms is relative to the last event if any, or to Now() otherwise. 70 void AddEvent(int delay_ms, const Closure& callback) { 71 AddEventHelper(delay_ms, callback, Closure()); 72 } 73 74 void AddDummyEvent(int delay_ms) { 75 AddEventHelper(delay_ms, Closure(), Closure()); 76 } 77 78 void AddEventAsTask(int delay_ms, const Closure& task) { 79 AddEventHelper(delay_ms, Closure(), task); 80 } 81 82 void Reset() { 83 processed_events_ = 0; 84 events_.clear(); 85 } 86 87 int processed_events() const { return processed_events_; } 88 89 private: 90 struct Event { 91 Time time; 92 Closure callback; 93 Closure task; 94 }; 95 96 struct Source : public GSource { 97 EventInjector* injector; 98 }; 99 100 void AddEventHelper( 101 int delay_ms, const Closure& callback, const Closure& task) { 102 Time last_time; 103 if (!events_.empty()) 104 last_time = (events_.end()-1)->time; 105 else 106 last_time = Time::NowFromSystemTime(); 107 108 Time future = last_time + TimeDelta::FromMilliseconds(delay_ms); 109 EventInjector::Event event = {future, callback, task}; 110 events_.push_back(event); 111 } 112 113 static gboolean Prepare(GSource* source, gint* timeout_ms) { 114 *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare(); 115 return FALSE; 116 } 117 118 static gboolean Check(GSource* source) { 119 return static_cast<Source*>(source)->injector->HandleCheck(); 120 } 121 122 static gboolean Dispatch(GSource* source, 123 GSourceFunc unused_func, 124 gpointer unused_data) { 125 static_cast<Source*>(source)->injector->HandleDispatch(); 126 return TRUE; 127 } 128 129 Source* source_; 130 std::vector<Event> events_; 131 int processed_events_; 132 static GSourceFuncs SourceFuncs; 133 DISALLOW_COPY_AND_ASSIGN(EventInjector); 134 }; 135 136 GSourceFuncs EventInjector::SourceFuncs = { 137 EventInjector::Prepare, 138 EventInjector::Check, 139 EventInjector::Dispatch, 140 NULL 141 }; 142 143 void IncrementInt(int *value) { 144 ++*value; 145 } 146 147 // Checks how many events have been processed by the injector. 148 void ExpectProcessedEvents(EventInjector* injector, int count) { 149 EXPECT_EQ(injector->processed_events(), count); 150 } 151 152 // Posts a task on the current message loop. 153 void PostMessageLoopTask(const tracked_objects::Location& from_here, 154 const Closure& task) { 155 MessageLoop::current()->PostTask(from_here, task); 156 } 157 158 // Test fixture. 159 class MessagePumpGLibTest : public testing::Test { 160 public: 161 MessagePumpGLibTest() : loop_(NULL), injector_(NULL) { } 162 163 // Overridden from testing::Test: 164 void SetUp() override { 165 loop_ = new MessageLoop(MessageLoop::TYPE_UI); 166 injector_ = new EventInjector(); 167 } 168 void TearDown() override { 169 delete injector_; 170 injector_ = NULL; 171 delete loop_; 172 loop_ = NULL; 173 } 174 175 MessageLoop* loop() const { return loop_; } 176 EventInjector* injector() const { return injector_; } 177 178 private: 179 MessageLoop* loop_; 180 EventInjector* injector_; 181 DISALLOW_COPY_AND_ASSIGN(MessagePumpGLibTest); 182 }; 183 184 } // namespace 185 186 TEST_F(MessagePumpGLibTest, TestQuit) { 187 // Checks that Quit works and that the basic infrastructure is working. 188 189 // Quit from a task 190 RunLoop().RunUntilIdle(); 191 EXPECT_EQ(0, injector()->processed_events()); 192 193 injector()->Reset(); 194 // Quit from an event 195 injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure()); 196 loop()->Run(); 197 EXPECT_EQ(1, injector()->processed_events()); 198 } 199 200 TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) { 201 // Checks that tasks posted by events are executed before the next event if 202 // the posted task queue is empty. 203 // MessageLoop doesn't make strong guarantees that it is the case, but the 204 // current implementation ensures it and the tests below rely on it. 205 // If changes cause this test to fail, it is reasonable to change it, but 206 // TestWorkWhileWaitingForEvents and TestEventsWhileWaitingForWork have to be 207 // changed accordingly, otherwise they can become flaky. 208 injector()->AddEventAsTask(0, Bind(&DoNothing)); 209 Closure check_task = 210 Bind(&ExpectProcessedEvents, Unretained(injector()), 2); 211 Closure posted_task = 212 Bind(&PostMessageLoopTask, FROM_HERE, check_task); 213 injector()->AddEventAsTask(0, posted_task); 214 injector()->AddEventAsTask(0, Bind(&DoNothing)); 215 injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure()); 216 loop()->Run(); 217 EXPECT_EQ(4, injector()->processed_events()); 218 219 injector()->Reset(); 220 injector()->AddEventAsTask(0, Bind(&DoNothing)); 221 check_task = 222 Bind(&ExpectProcessedEvents, Unretained(injector()), 2); 223 posted_task = Bind(&PostMessageLoopTask, FROM_HERE, check_task); 224 injector()->AddEventAsTask(0, posted_task); 225 injector()->AddEventAsTask(10, Bind(&DoNothing)); 226 injector()->AddEvent(0, MessageLoop::QuitWhenIdleClosure()); 227 loop()->Run(); 228 EXPECT_EQ(4, injector()->processed_events()); 229 } 230 231 TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) { 232 int task_count = 0; 233 // Tests that we process tasks while waiting for new events. 234 // The event queue is empty at first. 235 for (int i = 0; i < 10; ++i) { 236 loop()->PostTask(FROM_HERE, Bind(&IncrementInt, &task_count)); 237 } 238 // After all the previous tasks have executed, enqueue an event that will 239 // quit. 240 loop()->PostTask( 241 FROM_HERE, 242 Bind(&EventInjector::AddEvent, Unretained(injector()), 0, 243 MessageLoop::QuitWhenIdleClosure())); 244 loop()->Run(); 245 ASSERT_EQ(10, task_count); 246 EXPECT_EQ(1, injector()->processed_events()); 247 248 // Tests that we process delayed tasks while waiting for new events. 249 injector()->Reset(); 250 task_count = 0; 251 for (int i = 0; i < 10; ++i) { 252 loop()->PostDelayedTask( 253 FROM_HERE, 254 Bind(&IncrementInt, &task_count), 255 TimeDelta::FromMilliseconds(10*i)); 256 } 257 // After all the previous tasks have executed, enqueue an event that will 258 // quit. 259 // This relies on the fact that delayed tasks are executed in delay order. 260 // That is verified in message_loop_unittest.cc. 261 loop()->PostDelayedTask( 262 FROM_HERE, 263 Bind(&EventInjector::AddEvent, Unretained(injector()), 10, 264 MessageLoop::QuitWhenIdleClosure()), 265 TimeDelta::FromMilliseconds(150)); 266 loop()->Run(); 267 ASSERT_EQ(10, task_count); 268 EXPECT_EQ(1, injector()->processed_events()); 269 } 270 271 TEST_F(MessagePumpGLibTest, TestEventsWhileWaitingForWork) { 272 // Tests that we process events while waiting for work. 273 // The event queue is empty at first. 274 for (int i = 0; i < 10; ++i) { 275 injector()->AddDummyEvent(0); 276 } 277 // After all the events have been processed, post a task that will check that 278 // the events have been processed (note: the task executes after the event 279 // that posted it has been handled, so we expect 11 at that point). 280 Closure check_task = 281 Bind(&ExpectProcessedEvents, Unretained(injector()), 11); 282 Closure posted_task = 283 Bind(&PostMessageLoopTask, FROM_HERE, check_task); 284 injector()->AddEventAsTask(10, posted_task); 285 286 // And then quit (relies on the condition tested by TestEventTaskInterleave). 287 injector()->AddEvent(10, MessageLoop::QuitWhenIdleClosure()); 288 loop()->Run(); 289 290 EXPECT_EQ(12, injector()->processed_events()); 291 } 292 293 namespace { 294 295 // This class is a helper for the concurrent events / posted tasks test below. 296 // It will quit the main loop once enough tasks and events have been processed, 297 // while making sure there is always work to do and events in the queue. 298 class ConcurrentHelper : public RefCounted<ConcurrentHelper> { 299 public: 300 explicit ConcurrentHelper(EventInjector* injector) 301 : injector_(injector), 302 event_count_(kStartingEventCount), 303 task_count_(kStartingTaskCount) { 304 } 305 306 void FromTask() { 307 if (task_count_ > 0) { 308 --task_count_; 309 } 310 if (task_count_ == 0 && event_count_ == 0) { 311 MessageLoop::current()->QuitWhenIdle(); 312 } else { 313 MessageLoop::current()->PostTask( 314 FROM_HERE, Bind(&ConcurrentHelper::FromTask, this)); 315 } 316 } 317 318 void FromEvent() { 319 if (event_count_ > 0) { 320 --event_count_; 321 } 322 if (task_count_ == 0 && event_count_ == 0) { 323 MessageLoop::current()->QuitWhenIdle(); 324 } else { 325 injector_->AddEventAsTask( 326 0, Bind(&ConcurrentHelper::FromEvent, this)); 327 } 328 } 329 330 int event_count() const { return event_count_; } 331 int task_count() const { return task_count_; } 332 333 private: 334 friend class RefCounted<ConcurrentHelper>; 335 336 ~ConcurrentHelper() {} 337 338 static const int kStartingEventCount = 20; 339 static const int kStartingTaskCount = 20; 340 341 EventInjector* injector_; 342 int event_count_; 343 int task_count_; 344 }; 345 346 } // namespace 347 348 TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) { 349 // Tests that posted tasks don't starve events, nor the opposite. 350 // We use the helper class above. We keep both event and posted task queues 351 // full, the helper verifies that both tasks and events get processed. 352 // If that is not the case, either event_count_ or task_count_ will not get 353 // to 0, and MessageLoop::QuitWhenIdle() will never be called. 354 scoped_refptr<ConcurrentHelper> helper = new ConcurrentHelper(injector()); 355 356 // Add 2 events to the queue to make sure it is always full (when we remove 357 // the event before processing it). 358 injector()->AddEventAsTask( 359 0, Bind(&ConcurrentHelper::FromEvent, helper.get())); 360 injector()->AddEventAsTask( 361 0, Bind(&ConcurrentHelper::FromEvent, helper.get())); 362 363 // Similarly post 2 tasks. 364 loop()->PostTask( 365 FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper.get())); 366 loop()->PostTask( 367 FROM_HERE, Bind(&ConcurrentHelper::FromTask, helper.get())); 368 369 loop()->Run(); 370 EXPECT_EQ(0, helper->event_count()); 371 EXPECT_EQ(0, helper->task_count()); 372 } 373 374 namespace { 375 376 void AddEventsAndDrainGLib(EventInjector* injector) { 377 // Add a couple of dummy events 378 injector->AddDummyEvent(0); 379 injector->AddDummyEvent(0); 380 // Then add an event that will quit the main loop. 381 injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure()); 382 383 // Post a couple of dummy tasks 384 MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing)); 385 MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing)); 386 387 // Drain the events 388 while (g_main_context_pending(NULL)) { 389 g_main_context_iteration(NULL, FALSE); 390 } 391 } 392 393 } // namespace 394 395 TEST_F(MessagePumpGLibTest, TestDrainingGLib) { 396 // Tests that draining events using GLib works. 397 loop()->PostTask( 398 FROM_HERE, 399 Bind(&AddEventsAndDrainGLib, Unretained(injector()))); 400 loop()->Run(); 401 402 EXPECT_EQ(3, injector()->processed_events()); 403 } 404 405 namespace { 406 407 // Helper class that lets us run the GLib message loop. 408 class GLibLoopRunner : public RefCounted<GLibLoopRunner> { 409 public: 410 GLibLoopRunner() : quit_(false) { } 411 412 void RunGLib() { 413 while (!quit_) { 414 g_main_context_iteration(NULL, TRUE); 415 } 416 } 417 418 void RunLoop() { 419 while (!quit_) { 420 g_main_context_iteration(NULL, TRUE); 421 } 422 } 423 424 void Quit() { 425 quit_ = true; 426 } 427 428 void Reset() { 429 quit_ = false; 430 } 431 432 private: 433 friend class RefCounted<GLibLoopRunner>; 434 435 ~GLibLoopRunner() {} 436 437 bool quit_; 438 }; 439 440 void TestGLibLoopInternal(EventInjector* injector) { 441 // Allow tasks to be processed from 'native' event loops. 442 MessageLoop::current()->SetNestableTasksAllowed(true); 443 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner(); 444 445 int task_count = 0; 446 // Add a couple of dummy events 447 injector->AddDummyEvent(0); 448 injector->AddDummyEvent(0); 449 // Post a couple of dummy tasks 450 MessageLoop::current()->PostTask( 451 FROM_HERE, Bind(&IncrementInt, &task_count)); 452 MessageLoop::current()->PostTask( 453 FROM_HERE, Bind(&IncrementInt, &task_count)); 454 // Delayed events 455 injector->AddDummyEvent(10); 456 injector->AddDummyEvent(10); 457 // Delayed work 458 MessageLoop::current()->PostDelayedTask( 459 FROM_HERE, 460 Bind(&IncrementInt, &task_count), 461 TimeDelta::FromMilliseconds(30)); 462 MessageLoop::current()->PostDelayedTask( 463 FROM_HERE, 464 Bind(&GLibLoopRunner::Quit, runner.get()), 465 TimeDelta::FromMilliseconds(40)); 466 467 // Run a nested, straight GLib message loop. 468 runner->RunGLib(); 469 470 ASSERT_EQ(3, task_count); 471 EXPECT_EQ(4, injector->processed_events()); 472 MessageLoop::current()->QuitWhenIdle(); 473 } 474 475 void TestGtkLoopInternal(EventInjector* injector) { 476 // Allow tasks to be processed from 'native' event loops. 477 MessageLoop::current()->SetNestableTasksAllowed(true); 478 scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner(); 479 480 int task_count = 0; 481 // Add a couple of dummy events 482 injector->AddDummyEvent(0); 483 injector->AddDummyEvent(0); 484 // Post a couple of dummy tasks 485 MessageLoop::current()->PostTask( 486 FROM_HERE, Bind(&IncrementInt, &task_count)); 487 MessageLoop::current()->PostTask( 488 FROM_HERE, Bind(&IncrementInt, &task_count)); 489 // Delayed events 490 injector->AddDummyEvent(10); 491 injector->AddDummyEvent(10); 492 // Delayed work 493 MessageLoop::current()->PostDelayedTask( 494 FROM_HERE, 495 Bind(&IncrementInt, &task_count), 496 TimeDelta::FromMilliseconds(30)); 497 MessageLoop::current()->PostDelayedTask( 498 FROM_HERE, 499 Bind(&GLibLoopRunner::Quit, runner.get()), 500 TimeDelta::FromMilliseconds(40)); 501 502 // Run a nested, straight Gtk message loop. 503 runner->RunLoop(); 504 505 ASSERT_EQ(3, task_count); 506 EXPECT_EQ(4, injector->processed_events()); 507 MessageLoop::current()->QuitWhenIdle(); 508 } 509 510 } // namespace 511 512 TEST_F(MessagePumpGLibTest, TestGLibLoop) { 513 // Tests that events and posted tasks are correctly executed if the message 514 // loop is not run by MessageLoop::Run() but by a straight GLib loop. 515 // Note that in this case we don't make strong guarantees about niceness 516 // between events and posted tasks. 517 loop()->PostTask( 518 FROM_HERE, 519 Bind(&TestGLibLoopInternal, Unretained(injector()))); 520 loop()->Run(); 521 } 522 523 TEST_F(MessagePumpGLibTest, TestGtkLoop) { 524 // Tests that events and posted tasks are correctly executed if the message 525 // loop is not run by MessageLoop::Run() but by a straight Gtk loop. 526 // Note that in this case we don't make strong guarantees about niceness 527 // between events and posted tasks. 528 loop()->PostTask( 529 FROM_HERE, 530 Bind(&TestGtkLoopInternal, Unretained(injector()))); 531 loop()->Run(); 532 } 533 534 } // namespace base 535