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