| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a |
| 6 // heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to |
| 7 // increase tolerance and reduce observed flakiness (though doing so reduces the |
| 8 // meaningfulness of the test). |
| 9 |
| 5 #include "mojo/edk/system/wait_set_dispatcher.h" | 10 #include "mojo/edk/system/wait_set_dispatcher.h" |
| 6 | 11 |
| 12 #include <thread> |
| 13 |
| 14 #include "mojo/edk/platform/test_stopwatch.h" |
| 15 #include "mojo/edk/platform/thread_utils.h" |
| 7 #include "mojo/edk/system/mock_simple_dispatcher.h" | 16 #include "mojo/edk/system/mock_simple_dispatcher.h" |
| 17 #include "mojo/edk/system/test/timeouts.h" |
| 8 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
| 9 | 19 |
| 20 using mojo::platform::test::Stopwatch; |
| 21 using mojo::platform::ThreadSleep; |
| 10 using mojo::util::MakeRefCounted; | 22 using mojo::util::MakeRefCounted; |
| 11 | 23 |
| 12 namespace mojo { | 24 namespace mojo { |
| 13 namespace system { | 25 namespace system { |
| 14 namespace { | 26 namespace { |
| 15 | 27 |
| 16 // Helper to check if an array of |MojoWaitSetResult|s has a result |r| for the | 28 // Helper to check if an array of |MojoWaitSetResult|s has a result |r| for the |
| 17 // given cookie, in which case: | 29 // given cookie, in which case: |
| 18 // - |r.wait_result| must equal |wait_result|. | 30 // - |r.wait_result| must equal |wait_result|. |
| 19 // - If |wait_result| is |MOJO_RESULT_OK| or | 31 // - If |wait_result| is |MOJO_RESULT_OK| or |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 } | 213 } |
| 202 | 214 |
| 203 // Can close a dispatcher that's "in" the wait set. This should make | 215 // Can close a dispatcher that's "in" the wait set. This should make |
| 204 // |kCookie1| "cancelled". | 216 // |kCookie1| "cancelled". |
| 205 EXPECT_EQ(MOJO_RESULT_OK, d_member1->Close()); | 217 EXPECT_EQ(MOJO_RESULT_OK, d_member1->Close()); |
| 206 | 218 |
| 207 // Wait. | 219 // Wait. |
| 208 { | 220 { |
| 209 uint32_t num_results = 10u; | 221 uint32_t num_results = 10u; |
| 210 MojoWaitSetResult results[10] = {}; | 222 MojoWaitSetResult results[10] = {}; |
| 211 uint32_t max_results = static_cast<uint32_t>(-1); | 223 // Try passing null for |max_results|. |
| 212 EXPECT_EQ(MOJO_RESULT_OK, | 224 EXPECT_EQ(MOJO_RESULT_OK, |
| 213 d->WaitSetWait(k10ms, MakeUserPointer(&num_results), | 225 d->WaitSetWait(k10ms, MakeUserPointer(&num_results), |
| 214 MakeUserPointer(results), | 226 MakeUserPointer(results), NullUserPointer())); |
| 215 MakeUserPointer(&max_results))); | |
| 216 EXPECT_EQ(3u, num_results); | 227 EXPECT_EQ(3u, num_results); |
| 217 EXPECT_EQ(3u, max_results); | |
| 218 | 228 |
| 219 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie0, kSignals0, | 229 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie0, kSignals0, |
| 220 MOJO_RESULT_OK, | 230 MOJO_RESULT_OK, |
| 221 d_member0->GetHandleSignalsState())); | 231 d_member0->GetHandleSignalsState())); |
| 222 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie1, kSignals1, | 232 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie1, kSignals1, |
| 223 MOJO_RESULT_CANCELLED, | 233 MOJO_RESULT_CANCELLED, |
| 224 MojoHandleSignalsState())); | 234 MojoHandleSignalsState())); |
| 225 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie2, kSignals2, | 235 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie2, kSignals2, |
| 226 MOJO_RESULT_FAILED_PRECONDITION, | 236 MOJO_RESULT_FAILED_PRECONDITION, |
| 227 d_member0->GetHandleSignalsState())); | 237 d_member0->GetHandleSignalsState())); |
| 228 } | 238 } |
| 229 | 239 |
| 240 // Wait with zero |num_results| (in which case a null |results| is OK). |
| 241 { |
| 242 uint32_t num_results = 0u; |
| 243 uint32_t max_results = static_cast<uint32_t>(-1); |
| 244 EXPECT_EQ(MOJO_RESULT_OK, |
| 245 d->WaitSetWait(k10ms, MakeUserPointer(&num_results), |
| 246 NullUserPointer(), MakeUserPointer(&max_results))); |
| 247 EXPECT_EQ(0u, num_results); |
| 248 EXPECT_EQ(3u, max_results); |
| 249 } |
| 250 |
| 230 // Can remove something whose dispatcher has been closed. | 251 // Can remove something whose dispatcher has been closed. |
| 231 EXPECT_EQ(MOJO_RESULT_OK, d->WaitSetRemove(kCookie1)); | 252 EXPECT_EQ(MOJO_RESULT_OK, d->WaitSetRemove(kCookie1)); |
| 232 | 253 |
| 233 // Can close the wait set when it's not empty. | 254 // Can close the wait set when it's not empty. |
| 234 EXPECT_EQ(MOJO_RESULT_OK, d->Close()); | 255 EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
| 235 | 256 |
| 236 EXPECT_EQ(MOJO_RESULT_OK, d_member0->Close()); | 257 EXPECT_EQ(MOJO_RESULT_OK, d_member0->Close()); |
| 237 } | 258 } |
| 238 | 259 |
| 260 TEST(WaitSetDispatcherTest, TimeOut) { |
| 261 Stopwatch stopwatch; |
| 262 |
| 263 auto d = WaitSetDispatcher::Create(WaitSetDispatcher::kDefaultCreateOptions); |
| 264 |
| 265 // Wait with timeout without any entries. |
| 266 { |
| 267 uint32_t num_results = 1u; |
| 268 MojoWaitSetResult results[1] = {{456u}}; |
| 269 uint32_t max_results = 789u; |
| 270 stopwatch.Start(); |
| 271 EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, |
| 272 d->WaitSetWait( |
| 273 2 * test::EpsilonTimeout(), MakeUserPointer(&num_results), |
| 274 MakeUserPointer(results), MakeUserPointer(&max_results))); |
| 275 MojoDeadline elapsed = stopwatch.Elapsed(); |
| 276 EXPECT_GT(elapsed, test::EpsilonTimeout()); |
| 277 EXPECT_LT(elapsed, 3 * test::EpsilonTimeout()); |
| 278 // The inputs should be untouched. |
| 279 EXPECT_EQ(1u, num_results); |
| 280 EXPECT_EQ(456u, results[0].cookie); |
| 281 EXPECT_EQ(789u, max_results); |
| 282 } |
| 283 |
| 284 auto d_member = MakeRefCounted<test::MockSimpleDispatcher>( |
| 285 MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE); |
| 286 EXPECT_EQ(MOJO_RESULT_OK, d->WaitSetAdd(NullUserPointer(), d_member.Clone(), |
| 287 MOJO_HANDLE_SIGNAL_READABLE, 123u)); |
| 288 |
| 289 // Wait with timeout with an unsatisfied (but satisfiable) entry. |
| 290 { |
| 291 uint32_t num_results = 1u; |
| 292 MojoWaitSetResult results[1] = {{456u}}; |
| 293 uint32_t max_results = 789u; |
| 294 stopwatch.Start(); |
| 295 EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, |
| 296 d->WaitSetWait( |
| 297 2 * test::EpsilonTimeout(), MakeUserPointer(&num_results), |
| 298 MakeUserPointer(results), MakeUserPointer(&max_results))); |
| 299 MojoDeadline elapsed = stopwatch.Elapsed(); |
| 300 EXPECT_GT(elapsed, test::EpsilonTimeout()); |
| 301 EXPECT_LT(elapsed, 3 * test::EpsilonTimeout()); |
| 302 // The inputs should be untouched. |
| 303 EXPECT_EQ(1u, num_results); |
| 304 EXPECT_EQ(456u, results[0].cookie); |
| 305 EXPECT_EQ(789u, max_results); |
| 306 } |
| 307 |
| 308 EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
| 309 EXPECT_EQ(MOJO_RESULT_OK, d_member->Close()); |
| 310 } |
| 311 |
| 312 TEST(WaitSetDispatcherTest, BasicThreaded) { |
| 313 static constexpr auto kNone = MOJO_HANDLE_SIGNAL_NONE; |
| 314 static constexpr auto kR = MOJO_HANDLE_SIGNAL_READABLE; |
| 315 static constexpr auto kW = MOJO_HANDLE_SIGNAL_WRITABLE; |
| 316 |
| 317 const auto epsilon = test::EpsilonTimeout(); |
| 318 |
| 319 auto d = WaitSetDispatcher::Create(WaitSetDispatcher::kDefaultCreateOptions); |
| 320 |
| 321 // These will be members of our wait set. |
| 322 auto d_member0 = MakeRefCounted<test::MockSimpleDispatcher>(kNone, kR | kW); |
| 323 auto d_member1 = MakeRefCounted<test::MockSimpleDispatcher>(kNone, kR); |
| 324 |
| 325 // Add |d_member0|. |
| 326 static constexpr uint64_t kCookie0 = 123u; |
| 327 static constexpr auto kSignals0 = kR; |
| 328 EXPECT_EQ(MOJO_RESULT_OK, d->WaitSetAdd(NullUserPointer(), d_member0.Clone(), |
| 329 kSignals0, kCookie0)); |
| 330 |
| 331 // Add |d_member1|. |
| 332 static constexpr uint64_t kCookie1 = 456u; |
| 333 static constexpr auto kSignals1 = kR; |
| 334 EXPECT_EQ(MOJO_RESULT_OK, d->WaitSetAdd(NullUserPointer(), d_member1.Clone(), |
| 335 kSignals1, kCookie1)); |
| 336 |
| 337 // Can add |d_member0| again with a different cookie. |
| 338 static constexpr uint64_t kCookie2 = 789u; |
| 339 static constexpr auto kSignals2 = kW; |
| 340 EXPECT_EQ(MOJO_RESULT_OK, d->WaitSetAdd(NullUserPointer(), d_member0.Clone(), |
| 341 kSignals2, kCookie2)); |
| 342 |
| 343 // We'll wait on the main thread, and do stuff on another thread. |
| 344 |
| 345 { |
| 346 // Trigger |kCookie0|. |
| 347 std::thread t([epsilon, d_member0]() { |
| 348 // Sleep to try to ensure that waiting has started. |
| 349 ThreadSleep(epsilon); |
| 350 d_member0->SetSatisfiedSignals(kR); |
| 351 }); |
| 352 |
| 353 uint32_t num_results = 10u; |
| 354 MojoWaitSetResult results[10] = {}; |
| 355 uint32_t max_results = static_cast<uint32_t>(-1); |
| 356 EXPECT_EQ(MOJO_RESULT_OK, |
| 357 d->WaitSetWait(3 * epsilon, MakeUserPointer(&num_results), |
| 358 MakeUserPointer(results), |
| 359 MakeUserPointer(&max_results))); |
| 360 EXPECT_EQ(1u, num_results); |
| 361 EXPECT_EQ(1u, max_results); |
| 362 |
| 363 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie0, kSignals0, |
| 364 MOJO_RESULT_OK, |
| 365 d_member0->GetHandleSignalsState())); |
| 366 |
| 367 t.join(); |
| 368 } |
| 369 |
| 370 // Untrigger |kCookie0|. |
| 371 d_member0->SetSatisfiedSignals(kNone); |
| 372 |
| 373 { |
| 374 // Make |kCookie2| unsatisfiable. |
| 375 std::thread t([epsilon, d_member0]() { |
| 376 // Sleep to try to ensure that waiting has started. |
| 377 ThreadSleep(epsilon); |
| 378 d_member0->SetSatisfiableSignals(kR); |
| 379 }); |
| 380 |
| 381 uint32_t num_results = 10u; |
| 382 MojoWaitSetResult results[10] = {}; |
| 383 EXPECT_EQ(MOJO_RESULT_OK, |
| 384 d->WaitSetWait(3 * epsilon, MakeUserPointer(&num_results), |
| 385 MakeUserPointer(results), NullUserPointer())); |
| 386 EXPECT_EQ(1u, num_results); |
| 387 |
| 388 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie2, kSignals2, |
| 389 MOJO_RESULT_FAILED_PRECONDITION, |
| 390 d_member0->GetHandleSignalsState())); |
| 391 |
| 392 t.join(); |
| 393 } |
| 394 |
| 395 { |
| 396 // Trigger |kCookie1|. |
| 397 std::thread t( |
| 398 [epsilon, d_member1]() { d_member1->SetSatisfiedSignals(kR); }); |
| 399 |
| 400 // Sleep to try to ensure that |kCookie1| has been triggered. |
| 401 ThreadSleep(epsilon); |
| 402 |
| 403 uint32_t num_results = 10u; |
| 404 MojoWaitSetResult results[10] = {}; |
| 405 EXPECT_EQ(MOJO_RESULT_OK, |
| 406 d->WaitSetWait(3 * epsilon, MakeUserPointer(&num_results), |
| 407 MakeUserPointer(results), NullUserPointer())); |
| 408 EXPECT_EQ(2u, num_results); |
| 409 |
| 410 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie1, kSignals1, |
| 411 MOJO_RESULT_OK, |
| 412 d_member1->GetHandleSignalsState())); |
| 413 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie2, kSignals2, |
| 414 MOJO_RESULT_FAILED_PRECONDITION, |
| 415 d_member0->GetHandleSignalsState())); |
| 416 |
| 417 t.join(); |
| 418 } |
| 419 |
| 420 // Make |kCookie0| satisfiable again. |
| 421 d_member0->SetSatisfiableSignals(kR | kW); |
| 422 // Untrigger |kCookie1|. |
| 423 d_member1->SetSatisfiedSignals(kNone); |
| 424 |
| 425 { |
| 426 // Cancel |kCookie0| and |kCookie2| by closing |d_member0|. |
| 427 std::thread t([epsilon, d_member0]() { |
| 428 // Sleep to try to ensure that waiting has started. |
| 429 ThreadSleep(epsilon); |
| 430 EXPECT_EQ(MOJO_RESULT_OK, d_member0->Close()); |
| 431 }); |
| 432 |
| 433 uint32_t num_results = 10u; |
| 434 MojoWaitSetResult results[10] = {}; |
| 435 EXPECT_EQ(MOJO_RESULT_OK, |
| 436 d->WaitSetWait(3 * epsilon, MakeUserPointer(&num_results), |
| 437 MakeUserPointer(results), NullUserPointer())); |
| 438 EXPECT_EQ(2u, num_results); |
| 439 |
| 440 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie0, kSignals0, |
| 441 MOJO_RESULT_CANCELLED, |
| 442 MojoHandleSignalsState())); |
| 443 EXPECT_TRUE(CheckHasResult(num_results, results, kCookie2, kSignals2, |
| 444 MOJO_RESULT_CANCELLED, |
| 445 MojoHandleSignalsState())); |
| 446 |
| 447 t.join(); |
| 448 } |
| 449 |
| 450 EXPECT_EQ(MOJO_RESULT_OK, d_member1->Close()); |
| 451 EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
| 452 } |
| 453 |
| 454 // TODO(vtl): Test adding/removing on another thread. |
| 455 // TODO(vtl): Test waiting on multiple threads. |
| 456 // TODO(vtl): Stress tests. |
| 457 |
| 239 // TODO(vtl): Test options validation for "create" and "add" (not that there's | 458 // TODO(vtl): Test options validation for "create" and "add" (not that there's |
| 240 // much to test). | 459 // much to test). |
| 241 | 460 |
| 242 } // namespace | 461 } // namespace |
| 243 } // namespace system | 462 } // namespace system |
| 244 } // namespace mojo | 463 } // namespace mojo |
| OLD | NEW |