123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "media/midi/task_service.h"
- #include <memory>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/callback_helpers.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/ref_counted.h"
- #include "base/run_loop.h"
- #include "base/synchronization/lock.h"
- #include "base/test/test_simple_task_runner.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/time.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace midi {
- namespace {
- enum {
- kDefaultRunner = TaskService::kDefaultRunnerId,
- kFirstRunner,
- kSecondRunner
- };
- base::WaitableEvent* GetEvent() {
- static base::WaitableEvent* event =
- new base::WaitableEvent(base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
- return event;
- }
- void SignalEvent() {
- GetEvent()->Signal();
- }
- void WaitEvent() {
- GetEvent()->Wait();
- }
- void ResetEvent() {
- GetEvent()->Reset();
- }
- class TaskServiceClient {
- public:
- TaskServiceClient(TaskService* task_service)
- : task_service_(task_service),
- wait_task_event_(std::make_unique<base::WaitableEvent>(
- base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::NOT_SIGNALED)),
- count_(0u) {
- DCHECK(task_service);
- }
- TaskServiceClient(const TaskServiceClient&) = delete;
- TaskServiceClient& operator=(const TaskServiceClient&) = delete;
- bool Bind() { return task_service()->BindInstance(); }
- bool Unbind() { return task_service()->UnbindInstance(); }
- void PostBoundTask(TaskService::RunnerId runner_id) {
- task_service()->PostBoundTask(
- runner_id, base::BindOnce(&TaskServiceClient::IncrementCount,
- base::Unretained(this)));
- }
- void PostBoundSignalTask(TaskService::RunnerId runner_id) {
- task_service()->PostBoundTask(
- runner_id, base::BindOnce(&TaskServiceClient::SignalEvent,
- base::Unretained(this)));
- }
- void PostBoundWaitTask(TaskService::RunnerId runner_id) {
- wait_task_event_->Reset();
- task_service()->PostBoundTask(
- runner_id,
- base::BindOnce(&TaskServiceClient::WaitEvent, base::Unretained(this)));
- }
- void PostBoundDelayedSignalTask(TaskService::RunnerId runner_id) {
- task_service()->PostBoundDelayedTask(
- runner_id,
- base::BindOnce(&TaskServiceClient::SignalEvent, base::Unretained(this)),
- base::Milliseconds(100));
- }
- void WaitTask() { wait_task_event_->Wait(); }
- size_t count() {
- base::AutoLock lock(lock_);
- return count_;
- }
- private:
- TaskService* task_service() { return task_service_; }
- void IncrementCount() {
- base::AutoLock lock(lock_);
- count_++;
- }
- void SignalEvent() {
- IncrementCount();
- midi::SignalEvent();
- }
- void WaitEvent() {
- IncrementCount();
- wait_task_event_->Signal();
- midi::WaitEvent();
- }
- base::Lock lock_;
- raw_ptr<TaskService> task_service_;
- std::unique_ptr<base::WaitableEvent> wait_task_event_;
- size_t count_;
- };
- class MidiTaskServiceTest : public ::testing::Test {
- public:
- MidiTaskServiceTest() = default;
- MidiTaskServiceTest(const MidiTaskServiceTest&) = delete;
- MidiTaskServiceTest& operator=(const MidiTaskServiceTest&) = delete;
- protected:
- TaskService* task_service() { return &task_service_; }
- void RunUntilIdle() { task_runner_->RunUntilIdle(); }
- private:
- void SetUp() override {
- ResetEvent();
- task_runner_ = new base::TestSimpleTaskRunner();
- thread_task_runner_handle_ =
- std::make_unique<base::ThreadTaskRunnerHandle>(task_runner_);
- }
- void TearDown() override {
- thread_task_runner_handle_.reset();
- task_runner_.reset();
- }
- scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
- std::unique_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_;
- TaskService task_service_;
- };
- // Tests if posted tasks without calling BindInstance() are ignored.
- TEST_F(MidiTaskServiceTest, RunUnauthorizedBoundTask) {
- std::unique_ptr<TaskServiceClient> client =
- std::make_unique<TaskServiceClient>(task_service());
- client->PostBoundTask(kFirstRunner);
- // Destruct |client| immediately, then see if the posted task is just ignored.
- // If it isn't, another thread will touch the destructed instance and will
- // cause a crash due to a use-after-free.
- client = nullptr;
- }
- // Tests if invalid BindInstance() calls are correctly rejected, and it does not
- // make the service insanity.
- TEST_F(MidiTaskServiceTest, BindTwice) {
- std::unique_ptr<TaskServiceClient> client =
- std::make_unique<TaskServiceClient>(task_service());
- EXPECT_TRUE(client->Bind());
- // Should not be able to call BindInstance() twice before unbinding current
- // bound instance.
- EXPECT_FALSE(client->Bind());
- // Should be able to unbind only the first instance.
- EXPECT_TRUE(client->Unbind());
- EXPECT_FALSE(client->Unbind());
- }
- // Tests if posted static tasks can be processed correctly.
- TEST_F(MidiTaskServiceTest, RunStaticTask) {
- std::unique_ptr<TaskServiceClient> client =
- std::make_unique<TaskServiceClient>(task_service());
- EXPECT_TRUE(client->Bind());
- // Should be able to post a static task while an instance is bound.
- task_service()->PostStaticTask(kFirstRunner, base::BindOnce(&SignalEvent));
- WaitEvent();
- EXPECT_TRUE(client->Unbind());
- ResetEvent();
- EXPECT_TRUE(client->Bind());
- task_service()->PostStaticTask(kFirstRunner, base::BindOnce(&SignalEvent));
- // Should be able to unbind the instance to process a static task.
- EXPECT_TRUE(client->Unbind());
- WaitEvent();
- ResetEvent();
- // Should be able to post a static task without a bound instance.
- task_service()->PostStaticTask(kFirstRunner, base::BindOnce(&SignalEvent));
- WaitEvent();
- }
- // Tests functionalities to run bound tasks.
- TEST_F(MidiTaskServiceTest, RunBoundTasks) {
- std::unique_ptr<TaskServiceClient> client =
- std::make_unique<TaskServiceClient>(task_service());
- EXPECT_TRUE(client->Bind());
- // Tests if a post task run.
- EXPECT_EQ(0u, client->count());
- client->PostBoundSignalTask(kFirstRunner);
- WaitEvent();
- EXPECT_EQ(1u, client->count());
- // Tests if another posted task is handled correctly even if the instance is
- // unbound immediately. The posted task should run safely if it starts before
- // UnboundInstance() is call. Otherwise, it should be ignored. It completely
- // depends on timing.
- client->PostBoundTask(kFirstRunner);
- EXPECT_TRUE(client->Unbind());
- client = std::make_unique<TaskServiceClient>(task_service());
- // Tests if an immediate call of another BindInstance() works correctly.
- EXPECT_TRUE(client->Bind());
- // Runs two tasks in two runners.
- ResetEvent();
- client->PostBoundSignalTask(kFirstRunner);
- client->PostBoundTask(kSecondRunner);
- // Waits only the first runner completion to see if the second runner handles
- // the task correctly even if the bound instance is destructed.
- WaitEvent();
- EXPECT_TRUE(client->Unbind());
- client = nullptr;
- }
- // Tests if a blocking task does not block other task runners.
- TEST_F(MidiTaskServiceTest, RunBlockingTask) {
- std::unique_ptr<TaskServiceClient> client =
- std::make_unique<TaskServiceClient>(task_service());
- EXPECT_TRUE(client->Bind());
- // Posts a task that waits until the event is signaled.
- client->PostBoundWaitTask(kFirstRunner);
- // Confirms if the posted task starts. Now, the task should block in the task
- // until the second task is invoked.
- client->WaitTask();
- // Posts another task to the second runner. The task should be able to run
- // even though another posted task is blocking inside a critical section that
- // protects running tasks from an instance unbinding.
- client->PostBoundSignalTask(kSecondRunner);
- // Wait until the second task runs.
- WaitEvent();
- // UnbindInstance() should wait until any running task finishes so that the
- // instance can be destructed safely.
- EXPECT_TRUE(client->Unbind());
- EXPECT_EQ(2u, client->count());
- client = nullptr;
- }
- // Tests if a bound delayed task runs correctly.
- TEST_F(MidiTaskServiceTest, RunBoundDelayedTask) {
- std::unique_ptr<TaskServiceClient> client =
- std::make_unique<TaskServiceClient>(task_service());
- EXPECT_TRUE(client->Bind());
- // Posts a delayed task that signals after 100msec.
- client->PostBoundDelayedSignalTask(kFirstRunner);
- // Wait until the delayed task runs.
- WaitEvent();
- EXPECT_TRUE(client->Unbind());
- EXPECT_EQ(1u, client->count());
- client = nullptr;
- }
- // Tests if a bound task runs on the thread that bound the instance.
- TEST_F(MidiTaskServiceTest, RunBoundTaskOnDefaultRunner) {
- std::unique_ptr<TaskServiceClient> client =
- std::make_unique<TaskServiceClient>(task_service());
- EXPECT_TRUE(client->Bind());
- // Posts a task that increments the count on the caller thread.
- client->PostBoundTask(kDefaultRunner);
- // The posted task should not run until the current message loop is processed.
- EXPECT_EQ(0u, client->count());
- RunUntilIdle();
- EXPECT_EQ(1u, client->count());
- EXPECT_TRUE(client->Unbind());
- }
- } // namespace
- } // namespace midi
|