TestMediaEventSource.cpp (24731B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include <memory> 8 9 #include "MediaEventSource.h" 10 #include "VideoUtils.h" 11 #include "gmock/gmock-matchers.h" // testing::ElementsAre 12 #include "gmock/gmock.h" 13 #include "gtest/gtest.h" 14 #include "mozilla/SharedThreadPool.h" 15 #include "mozilla/TaskQueue.h" 16 #include "mozilla/UniquePtr.h" 17 18 using namespace mozilla; 19 using testing::InSequence; 20 using testing::MockFunction; 21 using testing::StrEq; 22 23 // TODO(bug 1954634): Once all of our toolchains support c++ requires 24 // expression, we should be validating that ineligible function signatures will 25 // not compile. (eg; NonExclusive does not work with non-const refs or rvalue 26 // refs) 27 28 /* 29 * Test if listeners receive the event data correctly. 30 */ 31 TEST(MediaEventSource, SingleListener) 32 { 33 RefPtr<TaskQueue> queue = 34 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 35 "TestMediaEventSource SingleListener"); 36 37 MediaEventProducer<int> source; 38 39 static std::vector<int> callbackLog; 40 callbackLog.clear(); 41 42 auto func = [&](int j) { callbackLog.push_back(j); }; 43 MediaEventListener listener = source.Connect(queue, func); 44 45 // Call Notify 3 times. The listener should be also called 3 times. 46 source.Notify(3); 47 source.Notify(5); 48 source.Notify(7); 49 50 queue->BeginShutdown(); 51 queue->AwaitShutdownAndIdle(); 52 53 // Verify the event data is passed correctly to the listener. 54 EXPECT_THAT(callbackLog, testing::ElementsAre(3, 5, 7)); 55 56 listener.Disconnect(); 57 } 58 59 TEST(MediaEventSource, MultiListener) 60 { 61 RefPtr<TaskQueue> queue = 62 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 63 "TestMediaEventSource MultiListener"); 64 65 MediaEventProducer<int> source; 66 67 static std::vector<int> callbackLog; 68 callbackLog.clear(); 69 70 auto func1 = [&](int k) { callbackLog.push_back(k * 2); }; 71 auto func2 = [&](int k) { callbackLog.push_back(k * 3); }; 72 MediaEventListener listener1 = source.Connect(queue, func1); 73 MediaEventListener listener2 = source.Connect(queue, func2); 74 75 // Both listeners should receive the event. 76 source.Notify(11); 77 78 queue->BeginShutdown(); 79 queue->AwaitShutdownAndIdle(); 80 81 // Verify the event data is passed correctly to the listener. 82 EXPECT_THAT(callbackLog, testing::ElementsAre(22, 33)); 83 84 listener1.Disconnect(); 85 listener2.Disconnect(); 86 } 87 88 /* 89 * Test if disconnecting a listener prevents events from coming. 90 */ 91 TEST(MediaEventSource, DisconnectAfterNotification) 92 { 93 RefPtr<TaskQueue> queue = 94 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 95 "TestMediaEventSource DisconnectAfterNotification"); 96 97 MediaEventProducer<int> source; 98 99 static std::vector<int> callbackLog; 100 callbackLog.clear(); 101 102 MediaEventListener listener; 103 auto func = [&](int j) { 104 callbackLog.push_back(j); 105 listener.Disconnect(); 106 }; 107 listener = source.Connect(queue, func); 108 109 // Call Notify() twice. Since we disconnect the listener when receiving 110 // the 1st event, the 2nd event should not reach the listener. 111 source.Notify(11); 112 source.Notify(11); 113 114 queue->BeginShutdown(); 115 queue->AwaitShutdownAndIdle(); 116 117 // Check only the 1st event is received. 118 EXPECT_THAT(callbackLog, testing::ElementsAre(11)); 119 } 120 121 TEST(MediaEventSource, DisconnectBeforeNotification) 122 { 123 RefPtr<TaskQueue> queue = 124 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 125 "TestMediaEventSource DisconnectBeforeNotification"); 126 127 MediaEventProducer<int> source; 128 129 static std::vector<int> callbackLog; 130 callbackLog.clear(); 131 132 auto func1 = [&](int k) { callbackLog.push_back(k * 2); }; 133 auto func2 = [&](int k) { callbackLog.push_back(k * 3); }; 134 MediaEventListener listener1 = source.Connect(queue, func1); 135 MediaEventListener listener2 = source.Connect(queue, func2); 136 137 // Disconnect listener2 before notification. Only listener1 should receive 138 // the event. 139 listener2.Disconnect(); 140 source.Notify(11); 141 142 queue->BeginShutdown(); 143 queue->AwaitShutdownAndIdle(); 144 145 EXPECT_THAT(callbackLog, testing::ElementsAre(22)); 146 147 listener1.Disconnect(); 148 } 149 150 /* 151 * Test we don't hit the assertion when calling Connect() and Disconnect() 152 * repeatedly. 153 */ 154 TEST(MediaEventSource, DisconnectAndConnect) 155 { 156 RefPtr<TaskQueue> queue = 157 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 158 "TestMediaEventSource DisconnectAndConnect"); 159 160 MediaEventProducerExc<int> source; 161 MediaEventListener listener = source.Connect(queue, []() {}); 162 listener.Disconnect(); 163 listener = source.Connect(queue, []() {}); 164 listener.Disconnect(); 165 } 166 167 /* 168 * Test void event type. 169 */ 170 TEST(MediaEventSource, VoidEventType) 171 { 172 RefPtr<TaskQueue> queue = 173 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 174 "TestMediaEventSource VoidEventType"); 175 176 MediaEventProducer<void> source; 177 178 static std::vector<int> callbackLog; 179 callbackLog.clear(); 180 181 // Test function object. 182 auto func = [&]() { callbackLog.push_back(1); }; 183 MediaEventListener listener1 = source.Connect(queue, func); 184 185 // Test member function. 186 struct Foo { 187 Foo() {} 188 void OnNotify() { callbackLog.push_back(2); } 189 } foo; 190 MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify); 191 192 // Call Notify 2 times. The listener should be also called 2 times. 193 source.Notify(); 194 source.Notify(); 195 196 queue->BeginShutdown(); 197 queue->AwaitShutdownAndIdle(); 198 199 EXPECT_THAT(callbackLog, testing::ElementsAre(1, 2, 1, 2)); 200 201 listener1.Disconnect(); 202 listener2.Disconnect(); 203 } 204 205 /* 206 * Test listeners can take various event types (T, const T&, and void). 207 */ 208 TEST(MediaEventSource, ListenerType1) 209 { 210 RefPtr<TaskQueue> queue = 211 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 212 "TestMediaEventSource ListenerType1"); 213 214 MediaEventProducer<int> source; 215 216 static std::vector<int> callbackLog; 217 callbackLog.clear(); 218 219 // Test various argument types. 220 // func(int&&) and func(int&) are ineligible because we're in NonExclusive 221 // mode, which passes a const. 222 auto func1 = [&](int j) { callbackLog.push_back(1); }; 223 auto func2 = [&](const int& j) { callbackLog.push_back(2); }; 224 auto func3 = [&]() { callbackLog.push_back(3); }; 225 MediaEventListener listener1 = source.Connect(queue, func1); 226 MediaEventListener listener2 = source.Connect(queue, func2); 227 MediaEventListener listener3 = source.Connect(queue, func3); 228 229 source.Notify(1); 230 231 queue->BeginShutdown(); 232 queue->AwaitShutdownAndIdle(); 233 234 EXPECT_THAT(callbackLog, testing::ElementsAre(1, 2, 3)); 235 236 listener1.Disconnect(); 237 listener2.Disconnect(); 238 listener3.Disconnect(); 239 } 240 241 TEST(MediaEventSource, ListenerType2) 242 { 243 RefPtr<TaskQueue> queue = 244 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 245 "TestMediaEventSource ListenerType2"); 246 247 MediaEventProducer<int> source; 248 249 static std::vector<int> callbackLog; 250 callbackLog.clear(); 251 252 struct Foo { 253 void OnNotify1(const int& i) { callbackLog.push_back(1); } 254 void OnNotify2() { callbackLog.push_back(2); } 255 void OnNotify3(int i) const { callbackLog.push_back(3); } 256 void OnNotify4(int i) volatile { callbackLog.push_back(4); } 257 } foo; 258 259 // Test member functions which might be CV qualified. 260 MediaEventListener listener1 = source.Connect(queue, &foo, &Foo::OnNotify1); 261 MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify2); 262 MediaEventListener listener3 = source.Connect(queue, &foo, &Foo::OnNotify3); 263 MediaEventListener listener4 = source.Connect(queue, &foo, &Foo::OnNotify4); 264 265 source.Notify(1); 266 267 queue->BeginShutdown(); 268 queue->AwaitShutdownAndIdle(); 269 270 EXPECT_THAT(callbackLog, testing::ElementsAre(1, 2, 3, 4)); 271 272 listener1.Disconnect(); 273 listener2.Disconnect(); 274 listener3.Disconnect(); 275 listener4.Disconnect(); 276 } 277 278 struct SomeEvent { 279 explicit SomeEvent(int& aCount) : mCount(aCount) {} 280 // Increment mCount when copy constructor is called to know how many times 281 // the event data is copied. 282 SomeEvent(const SomeEvent& aOther) : mCount(aOther.mCount) { ++mCount; } 283 SomeEvent(SomeEvent&& aOther) : mCount(aOther.mCount) {} 284 int& mCount; 285 }; 286 287 /* 288 * Test we don't have unnecessary copies of the event data. 289 */ 290 TEST(MediaEventSource, ZeroCopyNonExclusiveOneTarget) 291 { 292 RefPtr<TaskQueue> queue = 293 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 294 "TestMediaEventSource ZeroCopyNonExclusiveOneTarget"); 295 296 MediaEventProducer<SomeEvent> source; 297 int copies = 0; 298 299 static std::vector<int> callbackLog; 300 callbackLog.clear(); 301 302 auto func = []() { callbackLog.push_back(1); }; 303 struct Foo { 304 void OnNotify() { callbackLog.push_back(2); } 305 } foo; 306 307 MediaEventListener listener1 = source.Connect(queue, func); 308 MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify); 309 310 // We expect i to be 0 since Notify can take ownership of the temp object, 311 // and use it as shared state for all listeners. 312 source.Notify(SomeEvent(copies)); 313 314 queue->BeginShutdown(); 315 queue->AwaitShutdownAndIdle(); 316 EXPECT_EQ(copies, 0); 317 318 EXPECT_THAT(callbackLog, testing::ElementsAre(1, 2)); 319 320 listener1.Disconnect(); 321 listener2.Disconnect(); 322 } 323 324 TEST(MediaEventSource, ZeroCopyNonExclusiveTwoTarget) 325 { 326 RefPtr<TaskQueue> queue1 = TaskQueue::Create( 327 GetMediaThreadPool(MediaThreadType::SUPERVISOR), 328 "TestMediaEventSource ZeroCopyNonExclusiveTwoTarget(first)"); 329 RefPtr<TaskQueue> queue2 = TaskQueue::Create( 330 GetMediaThreadPool(MediaThreadType::SUPERVISOR), 331 "TestMediaEventSource ZeroCopyNonExclusiveTwoTarget(second)"); 332 333 MediaEventProducer<SomeEvent> source; 334 int copies = 0; 335 336 static std::vector<int> callbackLog1; 337 callbackLog1.clear(); 338 339 static std::vector<int> callbackLog2; 340 callbackLog2.clear(); 341 342 auto func1 = []() { callbackLog1.push_back(1); }; 343 struct Foo1 { 344 void OnNotify() { callbackLog1.push_back(2); } 345 } foo1; 346 347 auto func2 = []() { callbackLog2.push_back(1); }; 348 struct Foo2 { 349 void OnNotify() { callbackLog2.push_back(2); } 350 } foo2; 351 352 MediaEventListener listener1 = source.Connect(queue1, func1); 353 MediaEventListener listener2 = source.Connect(queue1, &foo1, &Foo1::OnNotify); 354 MediaEventListener listener3 = source.Connect(queue2, func2); 355 MediaEventListener listener4 = source.Connect(queue2, &foo2, &Foo2::OnNotify); 356 357 // We expect i to be 0 since Notify can take ownership of the temp object, 358 // and use it as shared state for all listeners. 359 source.Notify(SomeEvent(copies)); 360 361 queue1->BeginShutdown(); 362 queue1->AwaitShutdownAndIdle(); 363 queue2->BeginShutdown(); 364 queue2->AwaitShutdownAndIdle(); 365 EXPECT_EQ(copies, 0); 366 EXPECT_THAT(callbackLog1, testing::ElementsAre(1, 2)); 367 EXPECT_THAT(callbackLog2, testing::ElementsAre(1, 2)); 368 369 listener1.Disconnect(); 370 listener2.Disconnect(); 371 listener3.Disconnect(); 372 listener4.Disconnect(); 373 } 374 375 TEST(MediaEventSource, ZeroCopyOneCopyPerThreadOneTarget) 376 { 377 RefPtr<TaskQueue> queue = TaskQueue::Create( 378 GetMediaThreadPool(MediaThreadType::SUPERVISOR), 379 "TestMediaEventSource ZeroCopyOneCopyPerThreadOneTarget"); 380 381 MediaEventProducerOneCopyPerThread<SomeEvent> source; 382 int copies = 0; 383 384 static std::vector<int> callbackLog; 385 callbackLog.clear(); 386 387 auto func = []() { callbackLog.push_back(1); }; 388 struct Foo { 389 void OnNotify() { callbackLog.push_back(2); } 390 } foo; 391 392 MediaEventListener listener1 = source.Connect(queue, func); 393 MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify); 394 395 // We expect i to be 0 since Notify can take ownership of the temp object, 396 // which is then used to notify listeners on the single target. 397 source.Notify(SomeEvent(copies)); 398 399 queue->BeginShutdown(); 400 queue->AwaitShutdownAndIdle(); 401 EXPECT_EQ(copies, 0); 402 EXPECT_THAT(callbackLog, testing::ElementsAre(1, 2)); 403 404 listener1.Disconnect(); 405 listener2.Disconnect(); 406 } 407 408 TEST(MediaEventSource, ZeroCopyOneCopyPerThreadNoArglessCopy) 409 { 410 RefPtr<TaskQueue> queue1 = TaskQueue::Create( 411 GetMediaThreadPool(MediaThreadType::SUPERVISOR), 412 "TestMediaEventSource ZeroCopyOneCopyPerThreadNoArglessCopy(first)"); 413 RefPtr<TaskQueue> queue2 = TaskQueue::Create( 414 GetMediaThreadPool(MediaThreadType::SUPERVISOR), 415 "TestMediaEventSource ZeroCopyOneCopyPerThreadNoArglessCopy(second)"); 416 417 MediaEventProducerOneCopyPerThread<SomeEvent> source; 418 int copies = 0; 419 420 // func(SomeEvent&&) is ineligible, because OneCopyPerThread passes an lvalue 421 // ref. 422 auto arglessFunc = []() {}; 423 auto func = [](SomeEvent& aEvent) {}; 424 auto func2 = [](const SomeEvent& aEvent) {}; 425 struct Foo { 426 void OnNotify(SomeEvent& aEvent) {} 427 void OnNotify2(const SomeEvent& aEvent) {} 428 } foo; 429 430 MediaEventListener listener1 = source.Connect(queue1, func); 431 MediaEventListener listener2 = source.Connect(queue1, &foo, &Foo::OnNotify); 432 MediaEventListener listener3 = source.Connect(queue1, func2); 433 MediaEventListener listener4 = source.Connect(queue1, &foo, &Foo::OnNotify2); 434 MediaEventListener listener5 = source.Connect(queue2, arglessFunc); 435 436 // We expect i to be 0 since Notify can take ownership of the temp object, 437 // and use it to notify the listeners on queue1, since none of the listeners 438 // on queue2 take arguments. 439 source.Notify(SomeEvent(copies)); 440 441 queue1->BeginShutdown(); 442 queue1->AwaitShutdownAndIdle(); 443 queue2->BeginShutdown(); 444 queue2->AwaitShutdownAndIdle(); 445 EXPECT_EQ(copies, 0); 446 listener1.Disconnect(); 447 listener2.Disconnect(); 448 listener3.Disconnect(); 449 listener4.Disconnect(); 450 listener5.Disconnect(); 451 } 452 453 TEST(MediaEventSource, CopyForAdditionalTargets) 454 { 455 RefPtr<TaskQueue> queue1 = 456 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 457 "TestMediaEventSource CopyForAdditionalTargets(first)"); 458 RefPtr<TaskQueue> queue2 = TaskQueue::Create( 459 GetMediaThreadPool(MediaThreadType::SUPERVISOR), 460 "TestMediaEventSource CopyForAdditionalTargets(second)"); 461 462 MediaEventProducerOneCopyPerThread<SomeEvent> source; 463 int copies = 0; 464 465 static std::vector<int> callbackLog1; 466 callbackLog1.clear(); 467 auto func1 = [](SomeEvent& aEvent) { callbackLog1.push_back(0); }; 468 struct Foo1 { 469 void OnNotify(SomeEvent& aEvent) { callbackLog1.push_back(1); } 470 } foo1; 471 472 static std::vector<int> callbackLog2; 473 callbackLog2.clear(); 474 auto func2 = [](const SomeEvent& aEvent) { callbackLog2.push_back(0); }; 475 struct Foo2 { 476 void OnNotify(const SomeEvent& aEvent) { callbackLog2.push_back(1); } 477 } foo2; 478 479 MediaEventListener listener1 = source.Connect(queue1, func1); 480 MediaEventListener listener2 = source.Connect(queue1, &foo1, &Foo1::OnNotify); 481 MediaEventListener listener3 = source.Connect(queue2, func2); 482 MediaEventListener listener4 = source.Connect(queue2, &foo2, &Foo2::OnNotify); 483 484 // We expect i to be 1 since Notify can take ownership of the temp object, 485 // make a copy for the listeners on queue1, and then give the original to the 486 // listeners on queue2. 487 source.Notify(SomeEvent(copies)); 488 489 queue1->BeginShutdown(); 490 queue1->AwaitShutdownAndIdle(); 491 queue2->BeginShutdown(); 492 queue2->AwaitShutdownAndIdle(); 493 EXPECT_EQ(copies, 1); 494 EXPECT_THAT(callbackLog1, testing::ElementsAre(0, 1)); 495 EXPECT_THAT(callbackLog2, testing::ElementsAre(0, 1)); 496 497 listener1.Disconnect(); 498 listener2.Disconnect(); 499 listener3.Disconnect(); 500 listener4.Disconnect(); 501 } 502 503 TEST(MediaEventSource, CopyEventUnneeded) 504 { 505 RefPtr<TaskQueue> queue = 506 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 507 "TestMediaEventSource CopyEventUnneeded"); 508 509 MediaEventProducer<SomeEvent> source; 510 int copies = 0; 511 512 static std::vector<int> callbackLog; 513 callbackLog.clear(); 514 auto func = []() { callbackLog.push_back(0); }; 515 struct Foo { 516 void OnNotify() { callbackLog.push_back(1); } 517 } foo; 518 519 MediaEventListener listener1 = source.Connect(queue, func); 520 MediaEventListener listener2 = source.Connect(queue, &foo, &Foo::OnNotify); 521 522 // Non-temporary; if Notify takes the event at all, it will need to make at 523 // least one copy. It should not need to take it at all, since all listeners 524 // are argless. 525 std::unique_ptr<SomeEvent> event(new SomeEvent(copies)); 526 // SomeEvent won't be copied at all since the listeners take no arguments. 527 source.Notify(*event); 528 529 queue->BeginShutdown(); 530 queue->AwaitShutdownAndIdle(); 531 EXPECT_EQ(copies, 0); 532 EXPECT_THAT(callbackLog, testing::ElementsAre(0, 1)); 533 534 listener1.Disconnect(); 535 listener2.Disconnect(); 536 } 537 538 /* 539 * Test move-only types. 540 */ 541 TEST(MediaEventSource, MoveOnly) 542 { 543 RefPtr<TaskQueue> queue = 544 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 545 "TestMediaEventSource MoveOnly"); 546 547 MediaEventProducerExc<UniquePtr<int>> source; 548 static std::vector<int> callbackLog; 549 callbackLog.clear(); 550 551 auto func = [](UniquePtr<int>&& aEvent) { callbackLog.push_back(*aEvent); }; 552 MediaEventListener listener = source.Connect(queue, func); 553 554 // It is OK to pass an rvalue which is move-only. 555 source.Notify(UniquePtr<int>(new int(20))); 556 // It is an error to pass an lvalue which is move-only. 557 // UniquePtr<int> event(new int(30)); 558 // source.Notify(event); 559 560 queue->BeginShutdown(); 561 queue->AwaitShutdownAndIdle(); 562 563 EXPECT_THAT(callbackLog, testing::ElementsAre(20)); 564 565 listener.Disconnect(); 566 } 567 568 TEST(MediaEventSource, ExclusiveConstLvalueRef) 569 { 570 RefPtr<TaskQueue> queue = 571 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 572 "TestMediaEventSource ExclusiveConstLvalueRef"); 573 574 MediaEventProducerExc<UniquePtr<int>> source; 575 static std::vector<int> callbackLog; 576 callbackLog.clear(); 577 578 auto func = [](const UniquePtr<int>& aEvent) { 579 callbackLog.push_back(*aEvent); 580 }; 581 MediaEventListener listener = source.Connect(queue, func); 582 583 source.Notify(UniquePtr<int>(new int(20))); 584 585 queue->BeginShutdown(); 586 queue->AwaitShutdownAndIdle(); 587 588 EXPECT_THAT(callbackLog, testing::ElementsAre(20)); 589 590 listener.Disconnect(); 591 } 592 593 TEST(MediaEventSource, ExclusiveNoArgs) 594 { 595 RefPtr<TaskQueue> queue = 596 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 597 "TestMediaEventSource ExclusiveNoArgs"); 598 599 MediaEventProducerExc<UniquePtr<int>> source; 600 static int callbackCount = 0; 601 602 auto func = []() { ++callbackCount; }; 603 MediaEventListener listener = source.Connect(queue, func); 604 605 source.Notify(UniquePtr<int>(new int(20))); 606 607 queue->BeginShutdown(); 608 queue->AwaitShutdownAndIdle(); 609 610 ASSERT_EQ(callbackCount, 1); 611 612 listener.Disconnect(); 613 } 614 615 struct RefCounter { 616 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCounter) 617 explicit RefCounter(int aVal) : mVal(aVal) {} 618 int mVal; 619 620 private: 621 ~RefCounter() = default; 622 }; 623 624 /* 625 * Test we should copy instead of move in NonExclusive mode 626 * for each listener must get a copy. 627 */ 628 TEST(MediaEventSource, NoMove) 629 { 630 RefPtr<TaskQueue> queue = 631 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 632 "TestMediaEventSource NoMove"); 633 634 MediaEventProducer<RefPtr<RefCounter>> source; 635 636 auto func1 = [](const RefPtr<RefCounter>& aEvent) { 637 EXPECT_EQ(aEvent->mVal, 20); 638 }; 639 auto func2 = [](const RefPtr<RefCounter>& aEvent) { 640 EXPECT_EQ(aEvent->mVal, 20); 641 }; 642 MediaEventListener listener1 = source.Connect(queue, func1); 643 MediaEventListener listener2 = source.Connect(queue, func2); 644 645 // We should copy this rvalue instead of move it in NonExclusive mode. 646 RefPtr<RefCounter> val = new RefCounter(20); 647 source.Notify(std::move(val)); 648 649 queue->BeginShutdown(); 650 queue->AwaitShutdownAndIdle(); 651 listener1.Disconnect(); 652 listener2.Disconnect(); 653 } 654 655 /* 656 * Rvalue lambda should be moved instead of copied. 657 */ 658 TEST(MediaEventSource, MoveLambda) 659 { 660 RefPtr<TaskQueue> queue = 661 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 662 "TestMediaEventSource MoveLambda"); 663 664 MediaEventProducer<void> source; 665 666 int counter = 0; 667 SomeEvent someEvent(counter); 668 669 auto func = [someEvent]() {}; 670 // someEvent is copied when captured by the lambda. 671 EXPECT_EQ(someEvent.mCount, 1); 672 673 // someEvent should be copied for we pass |func| as an lvalue. 674 MediaEventListener listener1 = source.Connect(queue, func); 675 EXPECT_EQ(someEvent.mCount, 2); 676 677 // someEvent should be moved for we pass |func| as an rvalue. 678 MediaEventListener listener2 = source.Connect(queue, std::move(func)); 679 EXPECT_EQ(someEvent.mCount, 2); 680 681 listener1.Disconnect(); 682 listener2.Disconnect(); 683 } 684 685 template <typename Bool> 686 struct DestroyChecker { 687 explicit DestroyChecker(Bool* aIsDestroyed) : mIsDestroyed(aIsDestroyed) { 688 EXPECT_FALSE(*mIsDestroyed); 689 } 690 ~DestroyChecker() { 691 EXPECT_FALSE(*mIsDestroyed); 692 *mIsDestroyed = true; 693 } 694 695 private: 696 Bool* const mIsDestroyed; 697 }; 698 699 class ClassForDestroyCheck final : private DestroyChecker<bool> { 700 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ClassForDestroyCheck); 701 702 explicit ClassForDestroyCheck(bool* aIsDestroyed) 703 : DestroyChecker(aIsDestroyed) {} 704 705 int32_t RefCountNums() const { return mRefCnt; } 706 707 protected: 708 ~ClassForDestroyCheck() = default; 709 }; 710 711 TEST(MediaEventSource, ResetFuncReferenceAfterDisconnect) 712 { 713 const RefPtr<TaskQueue> queue = TaskQueue::Create( 714 GetMediaThreadPool(MediaThreadType::SUPERVISOR), 715 "TestMediaEventSource ResetFuncReferenceAfterDisconnect"); 716 MediaEventProducer<void> source; 717 718 // Using a class that supports refcounting to check the object destruction. 719 bool isDestroyed = false; 720 auto object = MakeRefPtr<ClassForDestroyCheck>(&isDestroyed); 721 EXPECT_FALSE(isDestroyed); 722 EXPECT_EQ(object->RefCountNums(), 1); 723 724 // Function holds a strong reference to object. 725 MediaEventListener listener = source.Connect(queue, [ptr = object] {}); 726 EXPECT_FALSE(isDestroyed); 727 EXPECT_EQ(object->RefCountNums(), 2); 728 729 // This should destroy the function and release the object reference from the 730 // function on the task queue, 731 listener.Disconnect(); 732 queue->BeginShutdown(); 733 queue->AwaitShutdownAndIdle(); 734 EXPECT_FALSE(isDestroyed); 735 EXPECT_EQ(object->RefCountNums(), 1); 736 737 // No one is holding reference to object, it should be destroyed 738 // immediately. 739 object = nullptr; 740 EXPECT_TRUE(isDestroyed); 741 } 742 743 TEST(MediaEventSource, ResetTargetAfterDisconnect) 744 { 745 RefPtr<TaskQueue> queue = 746 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 747 "TestMediaEventSource ResetTargetAfterDisconnect"); 748 MediaEventProducer<void> source; 749 MediaEventListener listener = source.Connect(queue, [] {}); 750 751 // MediaEventListener::Disconnect eventually gives up its target 752 listener.Disconnect(); 753 queue->AwaitIdle(); 754 755 // `queue` should be the last reference to the TaskQueue, meaning that this 756 // Release destroys it. 757 EXPECT_EQ(queue.forget().take()->Release(), 0u); 758 } 759 760 TEST(MediaEventSource, TailDispatch) 761 { 762 MockFunction<void(const char*)> checkpoint; 763 { 764 InSequence seq; 765 EXPECT_CALL(checkpoint, Call(StrEq("normal runnable"))); 766 EXPECT_CALL(checkpoint, Call(StrEq("source1"))); 767 EXPECT_CALL(checkpoint, Call(StrEq("tail-dispatched runnable"))); 768 EXPECT_CALL(checkpoint, Call(StrEq("source2"))); 769 } 770 771 MediaEventProducer<void> source1; 772 MediaEventListener listener1 = source1.Connect( 773 AbstractThread::MainThread(), [&] { checkpoint.Call("source1"); }); 774 MediaEventProducer<void> source2; 775 MediaEventListener listener2 = source2.Connect( 776 AbstractThread::MainThread(), [&] { checkpoint.Call("source2"); }); 777 778 AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction(__func__, [&] { 779 // Notify, using tail-dispatch. 780 source1.Notify(); 781 // Dispatch runnable, using tail-dispatch. 782 AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction( 783 __func__, [&] { checkpoint.Call("tail-dispatched runnable"); })); 784 // Notify other event, using tail-dispatch. 785 source2.Notify(); 786 // Dispatch runnable to the underlying event target, i.e. without 787 // tail-dispatch. Doesn't dispatch from a direct task so should run before 788 // tail-dispatched tasks. 789 GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction( 790 __func__, [&] { checkpoint.Call("normal runnable"); })); 791 })); 792 793 NS_ProcessPendingEvents(nullptr); 794 795 listener1.Disconnect(); 796 listener2.Disconnect(); 797 }