OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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 "media/midi/task_service.h" |
| 6 |
| 7 #include <memory> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" |
| 11 #include "base/callback.h" |
| 12 #include "base/memory/ptr_util.h" |
| 13 #include "base/memory/ref_counted.h" |
| 14 #include "base/run_loop.h" |
| 15 #include "base/synchronization/lock.h" |
| 16 #include "base/test/test_simple_task_runner.h" |
| 17 #include "base/threading/thread_task_runner_handle.h" |
| 18 #include "base/time/time.h" |
| 19 #include "testing/gtest/include/gtest/gtest.h" |
| 20 |
| 21 namespace midi { |
| 22 |
| 23 namespace { |
| 24 |
| 25 enum { |
| 26 kDefaultRunner = TaskService::kDefaultRunnerId, |
| 27 kFirstRunner, |
| 28 kSecondRunner |
| 29 }; |
| 30 |
| 31 base::WaitableEvent* GetEvent() { |
| 32 static base::WaitableEvent* event = |
| 33 new base::WaitableEvent(base::WaitableEvent::ResetPolicy::MANUAL, |
| 34 base::WaitableEvent::InitialState::NOT_SIGNALED); |
| 35 return event; |
| 36 } |
| 37 |
| 38 void SignalEvent() { |
| 39 GetEvent()->Signal(); |
| 40 } |
| 41 |
| 42 void WaitEvent() { |
| 43 GetEvent()->Wait(); |
| 44 } |
| 45 |
| 46 void ResetEvent() { |
| 47 GetEvent()->Reset(); |
| 48 } |
| 49 |
| 50 class TaskServiceClient { |
| 51 public: |
| 52 TaskServiceClient(TaskService* task_service) |
| 53 : task_service_(task_service), |
| 54 wait_task_event_(base::MakeUnique<base::WaitableEvent>( |
| 55 base::WaitableEvent::ResetPolicy::MANUAL, |
| 56 base::WaitableEvent::InitialState::NOT_SIGNALED)), |
| 57 count_(0u) { |
| 58 DCHECK(task_service); |
| 59 } |
| 60 |
| 61 bool Bind() { return task_service()->BindInstance(); } |
| 62 |
| 63 bool Unbind() { return task_service()->UnbindInstance(); } |
| 64 |
| 65 void PostBoundTask(TaskService::RunnerId runner_id) { |
| 66 task_service()->PostBoundTask( |
| 67 runner_id, base::BindOnce(&TaskServiceClient::IncrementCount, |
| 68 base::Unretained(this))); |
| 69 } |
| 70 |
| 71 void PostBoundSignalTask(TaskService::RunnerId runner_id) { |
| 72 task_service()->PostBoundTask( |
| 73 runner_id, base::BindOnce(&TaskServiceClient::SignalEvent, |
| 74 base::Unretained(this))); |
| 75 } |
| 76 |
| 77 void PostBoundWaitTask(TaskService::RunnerId runner_id) { |
| 78 wait_task_event_->Reset(); |
| 79 task_service()->PostBoundTask( |
| 80 runner_id, |
| 81 base::BindOnce(&TaskServiceClient::WaitEvent, base::Unretained(this))); |
| 82 } |
| 83 |
| 84 void PostBoundDelayedSignalTask(TaskService::RunnerId runner_id) { |
| 85 task_service()->PostBoundDelayedTask( |
| 86 runner_id, |
| 87 base::BindOnce(&TaskServiceClient::SignalEvent, base::Unretained(this)), |
| 88 base::TimeDelta::FromMilliseconds(100)); |
| 89 } |
| 90 |
| 91 void WaitTask() { wait_task_event_->Wait(); } |
| 92 |
| 93 size_t count() { |
| 94 base::AutoLock lock(lock_); |
| 95 return count_; |
| 96 } |
| 97 |
| 98 private: |
| 99 TaskService* task_service() { return task_service_; } |
| 100 |
| 101 void IncrementCount() { |
| 102 base::AutoLock lock(lock_); |
| 103 count_++; |
| 104 } |
| 105 |
| 106 void SignalEvent() { |
| 107 midi::SignalEvent(); |
| 108 IncrementCount(); |
| 109 } |
| 110 |
| 111 void WaitEvent() { |
| 112 wait_task_event_->Signal(); |
| 113 midi::WaitEvent(); |
| 114 IncrementCount(); |
| 115 } |
| 116 |
| 117 base::Lock lock_; |
| 118 TaskService* task_service_; |
| 119 std::unique_ptr<base::WaitableEvent> wait_task_event_; |
| 120 size_t count_; |
| 121 |
| 122 DISALLOW_COPY_AND_ASSIGN(TaskServiceClient); |
| 123 }; |
| 124 |
| 125 class MidiTaskServiceTest : public ::testing::Test { |
| 126 public: |
| 127 MidiTaskServiceTest() {} |
| 128 |
| 129 protected: |
| 130 TaskService* task_service() { return &task_service_; } |
| 131 void RunUntilIdle() { task_runner_->RunUntilIdle(); } |
| 132 |
| 133 private: |
| 134 void SetUp() override { |
| 135 ResetEvent(); |
| 136 task_runner_ = new base::TestSimpleTaskRunner(); |
| 137 thread_task_runner_handle_ = |
| 138 base::MakeUnique<base::ThreadTaskRunnerHandle>(task_runner_); |
| 139 } |
| 140 |
| 141 void TearDown() override { |
| 142 thread_task_runner_handle_.reset(); |
| 143 task_runner_ = NULL; |
| 144 } |
| 145 |
| 146 scoped_refptr<base::TestSimpleTaskRunner> task_runner_; |
| 147 std::unique_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_; |
| 148 TaskService task_service_; |
| 149 |
| 150 DISALLOW_COPY_AND_ASSIGN(MidiTaskServiceTest); |
| 151 }; |
| 152 |
| 153 // Tests if posted static tasks can be processed without any bound instance. |
| 154 TEST_F(MidiTaskServiceTest, RunStaticTask) { |
| 155 task_service()->PostStaticTask(kFirstRunner, base::BindOnce(&SignalEvent)); |
| 156 WaitEvent(); |
| 157 } |
| 158 |
| 159 // Tests if posted tasks without calling BindInstance() are ignored. |
| 160 TEST_F(MidiTaskServiceTest, RunUnauthorizedBoundTask) { |
| 161 std::unique_ptr<TaskServiceClient> client = |
| 162 base::MakeUnique<TaskServiceClient>(task_service()); |
| 163 |
| 164 client->PostBoundTask(kFirstRunner); |
| 165 |
| 166 // Destruct |client| immediately, then see if the posted task is just ignored. |
| 167 // If it isn't, another thread will touch the destructed instance and will |
| 168 // cause a crash due to a use-after-free. |
| 169 client = nullptr; |
| 170 } |
| 171 |
| 172 // Tests if invalid BindInstance() calls are correctly rejected, and it does not |
| 173 // make the service insanity. |
| 174 TEST_F(MidiTaskServiceTest, BindTwice) { |
| 175 std::unique_ptr<TaskServiceClient> client = |
| 176 base::MakeUnique<TaskServiceClient>(task_service()); |
| 177 |
| 178 EXPECT_TRUE(client->Bind()); |
| 179 |
| 180 // Should not be able to call BindInstance() twice before unbinding current |
| 181 // bound instance. |
| 182 EXPECT_FALSE(client->Bind()); |
| 183 |
| 184 // Should be able to unbind only the first instance. |
| 185 EXPECT_TRUE(client->Unbind()); |
| 186 EXPECT_FALSE(client->Unbind()); |
| 187 } |
| 188 |
| 189 // Tests if posted static tasks can be processed even with a bound instance. |
| 190 TEST_F(MidiTaskServiceTest, RunStaticTaskWithBoundInstance) { |
| 191 std::unique_ptr<TaskServiceClient> client = |
| 192 base::MakeUnique<TaskServiceClient>(task_service()); |
| 193 |
| 194 EXPECT_TRUE(client->Bind()); |
| 195 // Should be able to post a static task even with a bound instance. |
| 196 task_service()->PostStaticTask(kFirstRunner, base::BindOnce(&SignalEvent)); |
| 197 WaitEvent(); |
| 198 EXPECT_TRUE(client->Unbind()); |
| 199 |
| 200 ResetEvent(); |
| 201 |
| 202 EXPECT_TRUE(client->Bind()); |
| 203 task_service()->PostStaticTask(kFirstRunner, base::Bind(&SignalEvent)); |
| 204 // Should be able to unbind the instance to process a static task. |
| 205 EXPECT_TRUE(client->Unbind()); |
| 206 WaitEvent(); |
| 207 } |
| 208 |
| 209 // Tests functionalities to run bound tasks. |
| 210 TEST_F(MidiTaskServiceTest, RunBoundTasks) { |
| 211 std::unique_ptr<TaskServiceClient> client = |
| 212 base::MakeUnique<TaskServiceClient>(task_service()); |
| 213 |
| 214 EXPECT_TRUE(client->Bind()); |
| 215 |
| 216 // Tests if a post task run. |
| 217 EXPECT_EQ(0u, client->count()); |
| 218 client->PostBoundSignalTask(kFirstRunner); |
| 219 WaitEvent(); |
| 220 EXPECT_EQ(1u, client->count()); |
| 221 |
| 222 // Tests if another posted task is handled correctly even if the instance is |
| 223 // unbound immediately. The posted task should run safely if it starts before |
| 224 // UnboundInstance() is call. Otherwise, it should be ignored. It completely |
| 225 // depends on timing. |
| 226 client->PostBoundTask(kFirstRunner); |
| 227 EXPECT_TRUE(client->Unbind()); |
| 228 client = base::MakeUnique<TaskServiceClient>(task_service()); |
| 229 |
| 230 // Tests if an immediate call of another BindInstance() works correctly. |
| 231 EXPECT_TRUE(client->Bind()); |
| 232 |
| 233 // Runs two tasks in two runners. |
| 234 ResetEvent(); |
| 235 client->PostBoundSignalTask(kFirstRunner); |
| 236 client->PostBoundTask(kSecondRunner); |
| 237 |
| 238 // Waits only the first runner completion to see if the second runner handles |
| 239 // the task correctly even if the bound instance is destructed. |
| 240 WaitEvent(); |
| 241 EXPECT_TRUE(client->Unbind()); |
| 242 client = nullptr; |
| 243 } |
| 244 |
| 245 // Tests if a blocking task does not block other task runners. |
| 246 TEST_F(MidiTaskServiceTest, RunBlockingTask) { |
| 247 std::unique_ptr<TaskServiceClient> client = |
| 248 base::MakeUnique<TaskServiceClient>(task_service()); |
| 249 |
| 250 EXPECT_TRUE(client->Bind()); |
| 251 |
| 252 // Posts a task that waits until the event is signaled. |
| 253 client->PostBoundWaitTask(kFirstRunner); |
| 254 // Confirms if the posted task starts. Now, the task should block in the task |
| 255 // until the second task is invoked. |
| 256 client->WaitTask(); |
| 257 |
| 258 // Posts another task to the second runner. The task should be able to run |
| 259 // even though another posted task is blocking inside a critical section that |
| 260 // protects running tasks from an instance unbinding. |
| 261 client->PostBoundSignalTask(kSecondRunner); |
| 262 |
| 263 // Wait until the second task runs. |
| 264 WaitEvent(); |
| 265 |
| 266 // UnbindInstance() should wait until any running task finishes so that the |
| 267 // instance can be destructed safely. |
| 268 EXPECT_TRUE(client->Unbind()); |
| 269 EXPECT_EQ(2u, client->count()); |
| 270 client = nullptr; |
| 271 } |
| 272 |
| 273 // Tests if a bound delayed task runs correctly. |
| 274 TEST_F(MidiTaskServiceTest, RunBoundDelayedTask) { |
| 275 std::unique_ptr<TaskServiceClient> client = |
| 276 base::MakeUnique<TaskServiceClient>(task_service()); |
| 277 |
| 278 EXPECT_TRUE(client->Bind()); |
| 279 |
| 280 // Posts a delayed task that signals after 100msec. |
| 281 client->PostBoundDelayedSignalTask(kFirstRunner); |
| 282 |
| 283 // Wait until the delayed task runs. |
| 284 WaitEvent(); |
| 285 |
| 286 EXPECT_TRUE(client->Unbind()); |
| 287 EXPECT_EQ(1u, client->count()); |
| 288 client = nullptr; |
| 289 } |
| 290 |
| 291 // Tests if a bound task runs on the thread that bound the instance. |
| 292 TEST_F(MidiTaskServiceTest, RunBoundTaskOnDefaultRunner) { |
| 293 std::unique_ptr<TaskServiceClient> client = |
| 294 base::MakeUnique<TaskServiceClient>(task_service()); |
| 295 |
| 296 EXPECT_TRUE(client->Bind()); |
| 297 |
| 298 // Posts a task that increments the count on the caller thread. |
| 299 client->PostBoundTask(kDefaultRunner); |
| 300 |
| 301 // The posted task should not run until the current message loop is processed. |
| 302 EXPECT_EQ(0u, client->count()); |
| 303 RunUntilIdle(); |
| 304 EXPECT_EQ(1u, client->count()); |
| 305 |
| 306 EXPECT_TRUE(client->Unbind()); |
| 307 } |
| 308 |
| 309 } // namespace |
| 310 |
| 311 } // namespace midi |
OLD | NEW |