| /* | |
| * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | |
| * | |
| * Use of this source code is governed by a BSD-style license | |
| * that can be found in the LICENSE file in the root of the source | |
| * tree. An additional intellectual property rights grant can be found | |
| * in the file PATENTS. All contributing project authors may | |
| * be found in the AUTHORS file in the root of the source tree. | |
| */ | |
| #include <memory> | |
| #include <utility> | |
| #include "modules/include/module.h" | |
| #include "modules/utility/source/process_thread_impl.h" | |
| #include "rtc_base/location.h" | |
| #include "rtc_base/task_queue.h" | |
| #include "rtc_base/timeutils.h" | |
| #include "test/gmock.h" | |
| #include "test/gtest.h" | |
| namespace webrtc { | |
| using ::testing::_; | |
| using ::testing::DoAll; | |
| using ::testing::InSequence; | |
| using ::testing::Invoke; | |
| using ::testing::Return; | |
| using ::testing::SetArgPointee; | |
| // The length of time, in milliseconds, to wait for an event to become signaled. | |
| // Set to a fairly large value as there is quite a bit of variation on some | |
| // Windows bots. | |
| static const int kEventWaitTimeout = 500; | |
| class MockModule : public Module { | |
| public: | |
| MOCK_METHOD0(TimeUntilNextProcess, int64_t()); | |
| MOCK_METHOD0(Process, void()); | |
| MOCK_METHOD1(ProcessThreadAttached, void(ProcessThread*)); | |
| }; | |
| class RaiseEventTask : public rtc::QueuedTask { | |
| public: | |
| RaiseEventTask(EventWrapper* event) : event_(event) {} | |
| bool Run() override { | |
| event_->Set(); | |
| return true; | |
| } | |
| private: | |
| EventWrapper* event_; | |
| }; | |
| ACTION_P(SetEvent, event) { | |
| event->Set(); | |
| } | |
| ACTION_P(Increment, counter) { | |
| ++(*counter); | |
| } | |
| ACTION_P(SetTimestamp, ptr) { | |
| *ptr = rtc::TimeMillis(); | |
| } | |
| TEST(ProcessThreadImpl, StartStop) { | |
| ProcessThreadImpl thread("ProcessThread"); | |
| thread.Start(); | |
| thread.Stop(); | |
| } | |
| TEST(ProcessThreadImpl, MultipleStartStop) { | |
| ProcessThreadImpl thread("ProcessThread"); | |
| for (int i = 0; i < 5; ++i) { | |
| thread.Start(); | |
| thread.Stop(); | |
| } | |
| } | |
| // Verifies that we get at least call back to Process() on the worker thread. | |
| TEST(ProcessThreadImpl, ProcessCall) { | |
| ProcessThreadImpl thread("ProcessThread"); | |
| thread.Start(); | |
| std::unique_ptr<EventWrapper> event(EventWrapper::Create()); | |
| MockModule module; | |
| EXPECT_CALL(module, TimeUntilNextProcess()) | |
| .WillOnce(Return(0)) | |
| .WillRepeatedly(Return(1)); | |
| EXPECT_CALL(module, Process()) | |
| .WillOnce(DoAll(SetEvent(event.get()), Return())) | |
| .WillRepeatedly(Return()); | |
| EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); | |
| thread.RegisterModule(&module, RTC_FROM_HERE); | |
| EXPECT_EQ(kEventSignaled, event->Wait(kEventWaitTimeout)); | |
| EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); | |
| thread.Stop(); | |
| } | |
| // Same as ProcessCall except the module is registered before the | |
| // call to Start(). | |
| TEST(ProcessThreadImpl, ProcessCall2) { | |
| ProcessThreadImpl thread("ProcessThread"); | |
| std::unique_ptr<EventWrapper> event(EventWrapper::Create()); | |
| MockModule module; | |
| EXPECT_CALL(module, TimeUntilNextProcess()) | |
| .WillOnce(Return(0)) | |
| .WillRepeatedly(Return(1)); | |
| EXPECT_CALL(module, Process()) | |
| .WillOnce(DoAll(SetEvent(event.get()), Return())) | |
| .WillRepeatedly(Return()); | |
| thread.RegisterModule(&module, RTC_FROM_HERE); | |
| EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); | |
| thread.Start(); | |
| EXPECT_EQ(kEventSignaled, event->Wait(kEventWaitTimeout)); | |
| EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); | |
| thread.Stop(); | |
| } | |
| // Tests setting up a module for callbacks and then unregister that module. | |
| // After unregistration, we should not receive any further callbacks. | |
| TEST(ProcessThreadImpl, Deregister) { | |
| ProcessThreadImpl thread("ProcessThread"); | |
| std::unique_ptr<EventWrapper> event(EventWrapper::Create()); | |
| int process_count = 0; | |
| MockModule module; | |
| EXPECT_CALL(module, TimeUntilNextProcess()) | |
| .WillOnce(Return(0)) | |
| .WillRepeatedly(Return(1)); | |
| EXPECT_CALL(module, Process()) | |
| .WillOnce(DoAll(SetEvent(event.get()), | |
| Increment(&process_count), | |
| Return())) | |
| .WillRepeatedly(DoAll(Increment(&process_count), Return())); | |
| thread.RegisterModule(&module, RTC_FROM_HERE); | |
| EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); | |
| thread.Start(); | |
| EXPECT_EQ(kEventSignaled, event->Wait(kEventWaitTimeout)); | |
| EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); | |
| thread.DeRegisterModule(&module); | |
| EXPECT_GE(process_count, 1); | |
| int count_after_deregister = process_count; | |
| // We shouldn't get any more callbacks. | |
| EXPECT_EQ(kEventTimeout, event->Wait(20)); | |
| EXPECT_EQ(count_after_deregister, process_count); | |
| thread.Stop(); | |
| } | |
| // Helper function for testing receiving a callback after a certain amount of | |
| // time. There's some variance of timing built into it to reduce chance of | |
| // flakiness on bots. | |
| void ProcessCallAfterAFewMs(int64_t milliseconds) { | |
| ProcessThreadImpl thread("ProcessThread"); | |
| thread.Start(); | |
| std::unique_ptr<EventWrapper> event(EventWrapper::Create()); | |
| MockModule module; | |
| int64_t start_time = 0; | |
| int64_t called_time = 0; | |
| EXPECT_CALL(module, TimeUntilNextProcess()) | |
| .WillOnce(DoAll(SetTimestamp(&start_time), | |
| Return(milliseconds))) | |
| .WillRepeatedly(Return(milliseconds)); | |
| EXPECT_CALL(module, Process()) | |
| .WillOnce(DoAll(SetTimestamp(&called_time), | |
| SetEvent(event.get()), | |
| Return())) | |
| .WillRepeatedly(Return()); | |
| EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); | |
| thread.RegisterModule(&module, RTC_FROM_HERE); | |
| // Add a buffer of 50ms due to slowness of some trybots | |
| // (e.g. win_drmemory_light) | |
| EXPECT_EQ(kEventSignaled, event->Wait(milliseconds + 50)); | |
| EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); | |
| thread.Stop(); | |
| ASSERT_GT(start_time, 0); | |
| ASSERT_GT(called_time, 0); | |
| // Use >= instead of > since due to rounding and timer accuracy (or lack | |
| // thereof), can make the test run in "0"ms time. | |
| EXPECT_GE(called_time, start_time); | |
| // Check for an acceptable range. | |
| uint32_t diff = called_time - start_time; | |
| EXPECT_GE(diff, milliseconds - 15); | |
| EXPECT_LT(diff, milliseconds + 15); | |
| } | |
| // DISABLED for now since the virtual build bots are too slow :( | |
| // TODO(tommi): Fix. | |
| TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter5ms) { | |
| ProcessCallAfterAFewMs(5); | |
| } | |
| // DISABLED for now since the virtual build bots are too slow :( | |
| // TODO(tommi): Fix. | |
| TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter50ms) { | |
| ProcessCallAfterAFewMs(50); | |
| } | |
| // DISABLED for now since the virtual build bots are too slow :( | |
| // TODO(tommi): Fix. | |
| TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter200ms) { | |
| ProcessCallAfterAFewMs(200); | |
| } | |
| // Runs callbacks with the goal of getting up to 50 callbacks within a second | |
| // (on average 1 callback every 20ms). On real hardware, we're usually pretty | |
| // close to that, but the test bots that run on virtual machines, will | |
| // typically be in the range 30-40 callbacks. | |
| // DISABLED for now since this can take up to 2 seconds to run on the slowest | |
| // build bots. | |
| // TODO(tommi): Fix. | |
| TEST(ProcessThreadImpl, DISABLED_Process50Times) { | |
| ProcessThreadImpl thread("ProcessThread"); | |
| thread.Start(); | |
| std::unique_ptr<EventWrapper> event(EventWrapper::Create()); | |
| MockModule module; | |
| int callback_count = 0; | |
| // Ask for a callback after 20ms. | |
| EXPECT_CALL(module, TimeUntilNextProcess()) | |
| .WillRepeatedly(Return(20)); | |
| EXPECT_CALL(module, Process()) | |
| .WillRepeatedly(DoAll(Increment(&callback_count), | |
| Return())); | |
| EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); | |
| thread.RegisterModule(&module, RTC_FROM_HERE); | |
| EXPECT_EQ(kEventTimeout, event->Wait(1000)); | |
| EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); | |
| thread.Stop(); | |
| printf("Callback count: %i\n", callback_count); | |
| // Check that we got called back up to 50 times. | |
| // Some of the try bots run on slow virtual machines, so the lower bound | |
| // is much more relaxed to avoid flakiness. | |
| EXPECT_GE(callback_count, 25); | |
| EXPECT_LE(callback_count, 50); | |
| } | |
| // Tests that we can wake up the worker thread to give us a callback right | |
| // away when we know the thread is sleeping. | |
| TEST(ProcessThreadImpl, WakeUp) { | |
| ProcessThreadImpl thread("ProcessThread"); | |
| thread.Start(); | |
| std::unique_ptr<EventWrapper> started(EventWrapper::Create()); | |
| std::unique_ptr<EventWrapper> called(EventWrapper::Create()); | |
| MockModule module; | |
| int64_t start_time; | |
| int64_t called_time; | |
| // Ask for a callback after 1000ms. | |
| // TimeUntilNextProcess will be called twice. | |
| // The first time we use it to get the thread into a waiting state. | |
| // Then we wake the thread and there should not be another call made to | |
| // TimeUntilNextProcess before Process() is called. | |
| // The second time TimeUntilNextProcess is then called, is after Process | |
| // has been called and we don't expect any more calls. | |
| EXPECT_CALL(module, TimeUntilNextProcess()) | |
| .WillOnce(DoAll(SetTimestamp(&start_time), | |
| SetEvent(started.get()), | |
| Return(1000))) | |
| .WillOnce(Return(1000)); | |
| EXPECT_CALL(module, Process()) | |
| .WillOnce( | |
| DoAll(SetTimestamp(&called_time), SetEvent(called.get()), Return())) | |
| .WillRepeatedly(Return()); | |
| EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); | |
| thread.RegisterModule(&module, RTC_FROM_HERE); | |
| EXPECT_EQ(kEventSignaled, started->Wait(kEventWaitTimeout)); | |
| thread.WakeUp(&module); | |
| EXPECT_EQ(kEventSignaled, called->Wait(kEventWaitTimeout)); | |
| EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); | |
| thread.Stop(); | |
| EXPECT_GE(called_time, start_time); | |
| uint32_t diff = called_time - start_time; | |
| // We should have been called back much quicker than 1sec. | |
| EXPECT_LE(diff, 100u); | |
| } | |
| // Tests that we can post a task that gets run straight away on the worker | |
| // thread. | |
| TEST(ProcessThreadImpl, PostTask) { | |
| ProcessThreadImpl thread("ProcessThread"); | |
| std::unique_ptr<EventWrapper> task_ran(EventWrapper::Create()); | |
| std::unique_ptr<RaiseEventTask> task(new RaiseEventTask(task_ran.get())); | |
| thread.Start(); | |
| thread.PostTask(std::move(task)); | |
| EXPECT_EQ(kEventSignaled, task_ran->Wait(kEventWaitTimeout)); | |
| thread.Stop(); | |
| } | |
| } // namespace webrtc |