Index: mojo/system/simple_dispatcher_unittest.cc |
diff --git a/mojo/system/simple_dispatcher_unittest.cc b/mojo/system/simple_dispatcher_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9822f95292054cc1a89cb14a020ba400c07725c7 |
--- /dev/null |
+++ b/mojo/system/simple_dispatcher_unittest.cc |
@@ -0,0 +1,514 @@ |
+// Copyright 2013 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. |
+ |
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a |
+// heavily-loaded system). Sorry. |kEpsilonMicros| may be increased to increase |
+// tolerance and reduce observed flakiness. |
+ |
+#include "mojo/system/simple_dispatcher.h" |
+ |
+#include "base/basictypes.h" |
+#include "base/logging.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_vector.h" |
+#include "base/synchronization/lock.h" |
+#include "base/threading/platform_thread.h" // For |Sleep()|. |
+#include "base/time/time.h" |
+#include "mojo/system/test_utils.h" |
+#include "mojo/system/waiter.h" |
+#include "mojo/system/waiter_test_utils.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace mojo { |
+namespace system { |
+namespace { |
+ |
+const int64_t kMicrosPerMs = 1000; |
+const int64_t kEpsilonMicros = 15 * kMicrosPerMs; // 15 ms. |
+ |
+class MockSimpleDispatcher : public SimpleDispatcher { |
+ public: |
+ MockSimpleDispatcher() |
+ : satisfied_flags_(MOJO_WAIT_FLAG_NONE), |
+ satisfiable_flags_(MOJO_WAIT_FLAG_READABLE | MOJO_WAIT_FLAG_WRITABLE) {} |
+ |
+ void SetSatisfiedFlags(MojoWaitFlags new_satisfied_flags) { |
+ base::AutoLock locker(lock()); |
+ |
+ // Any new flags that are set should be satisfiable. |
+ CHECK_EQ(new_satisfied_flags & ~satisfied_flags_, |
+ new_satisfied_flags & ~satisfied_flags_ & satisfiable_flags_); |
+ |
+ if (new_satisfied_flags == satisfied_flags_) |
+ return; |
+ |
+ satisfied_flags_ = new_satisfied_flags; |
+ StateChangedNoLock(); |
+ } |
+ |
+ void SetSatisfiableFlags(MojoWaitFlags new_satisfiable_flags) { |
+ base::AutoLock locker(lock()); |
+ |
+ if (new_satisfiable_flags == satisfiable_flags_) |
+ return; |
+ |
+ satisfiable_flags_ = new_satisfiable_flags; |
+ StateChangedNoLock(); |
+ } |
+ |
+ private: |
+ friend class base::RefCountedThreadSafe<MockSimpleDispatcher>; |
+ virtual ~MockSimpleDispatcher() {} |
+ |
+ // |SimpleDispatcher| implementation: |
+ virtual MojoWaitFlags SatisfiedFlagsNoLock() const OVERRIDE { |
+ lock().AssertAcquired(); |
+ return satisfied_flags_; |
+ } |
+ |
+ virtual MojoWaitFlags SatisfiableFlagsNoLock() const OVERRIDE { |
+ lock().AssertAcquired(); |
+ return satisfiable_flags_; |
+ } |
+ |
+ // Protected by |lock()|: |
+ MojoWaitFlags satisfied_flags_; |
+ MojoWaitFlags satisfiable_flags_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MockSimpleDispatcher); |
+}; |
+ |
+TEST(SimpleDispatcherTest, Basic) { |
+ test::Stopwatch stopwatch; |
+ int64_t elapsed_micros; |
+ |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ Waiter w; |
+ |
+ // Try adding a readable waiter when already readable. |
+ w.Init(); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, |
+ d->AddWaiter(&w, MOJO_WAIT_FLAG_READABLE, 0)); |
+ // Shouldn't need to remove the waiter (it was not added). |
+ |
+ // Wait (forever) for writable when already writable. |
+ w.Init(); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 1)); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_WRITABLE); |
+ stopwatch.Start(); |
+ EXPECT_EQ(1, w.Wait(MOJO_DEADLINE_INDEFINITE)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ d->RemoveWaiter(&w); |
+ |
+ // Wait for zero time for writable when already writable. |
+ w.Init(); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 2)); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_WRITABLE); |
+ stopwatch.Start(); |
+ EXPECT_EQ(2, w.Wait(0)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ d->RemoveWaiter(&w); |
+ |
+ // Wait for non-zero, finite time for writable when already writable. |
+ w.Init(); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 3)); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_WRITABLE); |
+ stopwatch.Start(); |
+ EXPECT_EQ(3, w.Wait(2 * kEpsilonMicros)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ d->RemoveWaiter(&w); |
+ |
+ // Wait for zero time for writable when not writable (will time out). |
+ w.Init(); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 4)); |
+ stopwatch.Start(); |
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ d->RemoveWaiter(&w); |
+ |
+ // Wait for non-zero, finite time for writable when not writable (will time |
+ // out). |
+ w.Init(); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 4)); |
+ stopwatch.Start(); |
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(2 * kEpsilonMicros)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_GT(elapsed_micros, (2-1) * kEpsilonMicros); |
+ EXPECT_LT(elapsed_micros, (2+1) * kEpsilonMicros); |
+ d->RemoveWaiter(&w); |
+ |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+} |
+ |
+TEST(SimpleDispatcherTest, BasicUnsatisfiable) { |
+ test::Stopwatch stopwatch; |
+ int64_t elapsed_micros; |
+ |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ Waiter w; |
+ |
+ // Try adding a writable waiter when it can never be writable. |
+ w.Init(); |
+ d->SetSatisfiableFlags(MOJO_WAIT_FLAG_READABLE); |
+ d->SetSatisfiedFlags(0); |
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, |
+ d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 5)); |
+ // Shouldn't need to remove the waiter (it was not added). |
+ |
+ // Wait (forever) for writable and then it becomes never writable. |
+ w.Init(); |
+ d->SetSatisfiableFlags(MOJO_WAIT_FLAG_READABLE | MOJO_WAIT_FLAG_WRITABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 6)); |
+ d->SetSatisfiableFlags(MOJO_WAIT_FLAG_READABLE); |
+ stopwatch.Start(); |
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, w.Wait(MOJO_DEADLINE_INDEFINITE)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ d->RemoveWaiter(&w); |
+ |
+ // Wait for zero time for writable and then it becomes never writable. |
+ w.Init(); |
+ d->SetSatisfiableFlags(MOJO_WAIT_FLAG_READABLE | MOJO_WAIT_FLAG_WRITABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 6)); |
+ d->SetSatisfiableFlags(MOJO_WAIT_FLAG_READABLE); |
+ stopwatch.Start(); |
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, w.Wait(0)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ d->RemoveWaiter(&w); |
+ |
+ // Wait for non-zero, finite time for writable and then it becomes never |
+ // writable. |
+ w.Init(); |
+ d->SetSatisfiableFlags(MOJO_WAIT_FLAG_READABLE | MOJO_WAIT_FLAG_WRITABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 7)); |
+ d->SetSatisfiableFlags(MOJO_WAIT_FLAG_READABLE); |
+ stopwatch.Start(); |
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, w.Wait(2 * kEpsilonMicros)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ d->RemoveWaiter(&w); |
+ |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+} |
+ |
+TEST(SimpleDispatcherTest, BasicClosed) { |
+ test::Stopwatch stopwatch; |
+ int64_t elapsed_micros; |
+ |
+ scoped_refptr<MockSimpleDispatcher> d; |
+ Waiter w; |
+ |
+ // Try adding a writable waiter when the dispatcher has been closed. |
+ d = new MockSimpleDispatcher(); |
+ w.Init(); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, |
+ d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 8)); |
+ // Shouldn't need to remove the waiter (it was not added). |
+ |
+ // Wait (forever) for writable and then the dispatcher is closed. |
+ d = new MockSimpleDispatcher(); |
+ w.Init(); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 9)); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ stopwatch.Start(); |
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(MOJO_DEADLINE_INDEFINITE)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ // Don't need to remove waiters from closed dispatchers. |
+ |
+ // Wait for zero time for writable and then the dispatcher is closed. |
+ d = new MockSimpleDispatcher(); |
+ w.Init(); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 10)); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ stopwatch.Start(); |
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(0)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ // Don't need to remove waiters from closed dispatchers. |
+ |
+ // Wait for non-zero, finite time for writable and then the dispatcher is |
+ // closed. |
+ d = new MockSimpleDispatcher(); |
+ w.Init(); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_WAIT_FLAG_WRITABLE, 11)); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ stopwatch.Start(); |
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(2 * kEpsilonMicros)); |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ // Don't need to remove waiters from closed dispatchers. |
+} |
+ |
+TEST(SimpleDispatcherTest, BasicThreaded) { |
+ test::Stopwatch stopwatch; |
+ bool did_wait; |
+ MojoResult result; |
+ int64_t elapsed_micros; |
+ |
+ // Wait for readable (already readable). |
+ { |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ { |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ test::WaiterThread thread(d, |
+ MOJO_WAIT_FLAG_READABLE, |
+ MOJO_DEADLINE_INDEFINITE, |
+ 0, |
+ &did_wait, &result); |
+ stopwatch.Start(); |
+ thread.Start(); |
+ } // Joins the thread. |
+ // If we closed earlier, then probably we'd get a |MOJO_RESULT_CANCELLED|. |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ } |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_FALSE(did_wait); |
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result); |
+ EXPECT_LT(elapsed_micros, kEpsilonMicros); |
+ |
+ // Wait for readable and becomes readable after some time. |
+ { |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ test::WaiterThread thread(d, |
+ MOJO_WAIT_FLAG_READABLE, |
+ MOJO_DEADLINE_INDEFINITE, |
+ 1, |
+ &did_wait, &result); |
+ stopwatch.Start(); |
+ thread.Start(); |
+ base::PlatformThread::Sleep( |
+ base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros)); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ } // Joins the thread. |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_TRUE(did_wait); |
+ EXPECT_EQ(1, result); |
+ EXPECT_GT(elapsed_micros, (2-1) * kEpsilonMicros); |
+ EXPECT_LT(elapsed_micros, (2+1) * kEpsilonMicros); |
+ |
+ // Wait for readable and becomes never-readable after some time. |
+ { |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ test::WaiterThread thread(d, |
+ MOJO_WAIT_FLAG_READABLE, |
+ MOJO_DEADLINE_INDEFINITE, |
+ 2, |
+ &did_wait, &result); |
+ stopwatch.Start(); |
+ thread.Start(); |
+ base::PlatformThread::Sleep( |
+ base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros)); |
+ d->SetSatisfiableFlags(MOJO_WAIT_FLAG_NONE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ } // Joins the thread. |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_TRUE(did_wait); |
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); |
+ EXPECT_GT(elapsed_micros, (2-1) * kEpsilonMicros); |
+ EXPECT_LT(elapsed_micros, (2+1) * kEpsilonMicros); |
+ |
+ // Wait for readable and dispatcher gets closed. |
+ { |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ test::WaiterThread thread(d, |
+ MOJO_WAIT_FLAG_READABLE, |
+ MOJO_DEADLINE_INDEFINITE, |
+ 3, |
+ &did_wait, &result); |
+ stopwatch.Start(); |
+ thread.Start(); |
+ base::PlatformThread::Sleep( |
+ base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros)); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ } // Joins the thread. |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_TRUE(did_wait); |
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result); |
+ EXPECT_GT(elapsed_micros, (2-1) * kEpsilonMicros); |
+ EXPECT_LT(elapsed_micros, (2+1) * kEpsilonMicros); |
+ |
+ // Wait for readable and times out. |
+ { |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ { |
+ test::WaiterThread thread(d, |
+ MOJO_WAIT_FLAG_READABLE, |
+ 2 * kEpsilonMicros, |
+ 4, |
+ &did_wait, &result); |
+ stopwatch.Start(); |
+ thread.Start(); |
+ base::PlatformThread::Sleep( |
+ base::TimeDelta::FromMicroseconds(1 * kEpsilonMicros)); |
+ // Not what we're waiting for. |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_WRITABLE); |
+ } // Joins the thread (after its wait times out). |
+ // If we closed earlier, then probably we'd get a |MOJO_RESULT_CANCELLED|. |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ } |
+ elapsed_micros = stopwatch.Elapsed(); |
+ EXPECT_TRUE(did_wait); |
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result); |
+ EXPECT_GT(elapsed_micros, (2-1) * kEpsilonMicros); |
+ EXPECT_LT(elapsed_micros, (2+1) * kEpsilonMicros); |
+} |
+ |
+TEST(SimpleDispatcherTest, MultipleWaiters) { |
+ static const size_t kNumWaiters = 20; |
+ |
+ bool did_wait[kNumWaiters]; |
+ MojoResult result[kNumWaiters]; |
+ |
+ // All wait for readable and becomes readable after some time. |
+ { |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ ScopedVector<test::WaiterThread> threads; |
+ for (size_t i = 0; i < kNumWaiters; i++) { |
+ threads.push_back(new test::WaiterThread(d, |
+ MOJO_WAIT_FLAG_READABLE, |
+ MOJO_DEADLINE_INDEFINITE, |
+ static_cast<MojoResult>(i), |
+ &did_wait[i], &result[i])); |
+ threads.back()->Start(); |
+ } |
+ base::PlatformThread::Sleep( |
+ base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros)); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ } // Joins the threads. |
+ for (size_t i = 0; i < kNumWaiters; i++) { |
+ EXPECT_TRUE(did_wait[i]); |
+ EXPECT_EQ(static_cast<MojoResult>(i), result[i]); |
+ } |
+ |
+ // Some wait for readable, some for writable, and becomes readable after some |
+ // time. |
+ { |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ ScopedVector<test::WaiterThread> threads; |
+ for (size_t i = 0; i < kNumWaiters / 2; i++) { |
+ threads.push_back(new test::WaiterThread(d, |
+ MOJO_WAIT_FLAG_READABLE, |
+ MOJO_DEADLINE_INDEFINITE, |
+ static_cast<MojoResult>(i), |
+ &did_wait[i], &result[i])); |
+ threads.back()->Start(); |
+ } |
+ for (size_t i = kNumWaiters / 2; i < kNumWaiters; i++) { |
+ threads.push_back(new test::WaiterThread(d, |
+ MOJO_WAIT_FLAG_WRITABLE, |
+ MOJO_DEADLINE_INDEFINITE, |
+ static_cast<MojoResult>(i), |
+ &did_wait[i], &result[i])); |
+ threads.back()->Start(); |
+ } |
+ base::PlatformThread::Sleep( |
+ base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros)); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ // This will wake up the ones waiting to write. |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ } // Joins the threads. |
+ for (size_t i = 0; i < kNumWaiters / 2; i++) { |
+ EXPECT_TRUE(did_wait[i]); |
+ EXPECT_EQ(static_cast<MojoResult>(i), result[i]); |
+ } |
+ for (size_t i = kNumWaiters / 2; i < kNumWaiters; i++) { |
+ EXPECT_TRUE(did_wait[i]); |
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result[i]); |
+ } |
+ |
+ // Some wait for readable, some for writable, and becomes readable and |
+ // never-writable after some time. |
+ { |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ ScopedVector<test::WaiterThread> threads; |
+ for (size_t i = 0; i < kNumWaiters / 2; i++) { |
+ threads.push_back(new test::WaiterThread(d, |
+ MOJO_WAIT_FLAG_READABLE, |
+ MOJO_DEADLINE_INDEFINITE, |
+ static_cast<MojoResult>(i), |
+ &did_wait[i], &result[i])); |
+ threads.back()->Start(); |
+ } |
+ for (size_t i = kNumWaiters / 2; i < kNumWaiters; i++) { |
+ threads.push_back(new test::WaiterThread(d, |
+ MOJO_WAIT_FLAG_WRITABLE, |
+ MOJO_DEADLINE_INDEFINITE, |
+ static_cast<MojoResult>(i), |
+ &did_wait[i], &result[i])); |
+ threads.back()->Start(); |
+ } |
+ base::PlatformThread::Sleep( |
+ base::TimeDelta::FromMicroseconds(1 * kEpsilonMicros)); |
+ d->SetSatisfiableFlags(MOJO_WAIT_FLAG_READABLE); |
+ base::PlatformThread::Sleep( |
+ base::TimeDelta::FromMicroseconds(1 * kEpsilonMicros)); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ } // Joins the threads. |
+ for (size_t i = 0; i < kNumWaiters / 2; i++) { |
+ EXPECT_TRUE(did_wait[i]); |
+ EXPECT_EQ(static_cast<MojoResult>(i), result[i]); |
+ } |
+ for (size_t i = kNumWaiters / 2; i < kNumWaiters; i++) { |
+ EXPECT_TRUE(did_wait[i]); |
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result[i]); |
+ } |
+ |
+ // Some wait for readable, some for writable, and becomes readable after some |
+ // time. |
+ { |
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher()); |
+ ScopedVector<test::WaiterThread> threads; |
+ for (size_t i = 0; i < kNumWaiters / 2; i++) { |
+ threads.push_back(new test::WaiterThread(d, |
+ MOJO_WAIT_FLAG_READABLE, |
+ 3 * kEpsilonMicros, |
+ static_cast<MojoResult>(i), |
+ &did_wait[i], &result[i])); |
+ threads.back()->Start(); |
+ } |
+ for (size_t i = kNumWaiters / 2; i < kNumWaiters; i++) { |
+ threads.push_back(new test::WaiterThread(d, |
+ MOJO_WAIT_FLAG_WRITABLE, |
+ 1 * kEpsilonMicros, |
+ static_cast<MojoResult>(i), |
+ &did_wait[i], &result[i])); |
+ threads.back()->Start(); |
+ } |
+ base::PlatformThread::Sleep( |
+ base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros)); |
+ d->SetSatisfiedFlags(MOJO_WAIT_FLAG_READABLE); |
+ // All those waiting for writable should have timed out. |
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close()); |
+ } // Joins the threads. |
+ for (size_t i = 0; i < kNumWaiters / 2; i++) { |
+ EXPECT_TRUE(did_wait[i]); |
+ EXPECT_EQ(static_cast<MojoResult>(i), result[i]); |
+ } |
+ for (size_t i = kNumWaiters / 2; i < kNumWaiters; i++) { |
+ EXPECT_TRUE(did_wait[i]); |
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result[i]); |
+ } |
+} |
+ |
+// TODO(vtl): Stress test? |
+ |
+} // namespace |
+} // namespace system |
+} // namespace mojo |