summaryrefslogtreecommitdiffstats
path: root/mojo/edk/system/simple_dispatcher_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/edk/system/simple_dispatcher_unittest.cc')
-rw-r--r--mojo/edk/system/simple_dispatcher_unittest.cc604
1 files changed, 604 insertions, 0 deletions
diff --git a/mojo/edk/system/simple_dispatcher_unittest.cc b/mojo/edk/system/simple_dispatcher_unittest.cc
new file mode 100644
index 0000000..7d7b9c2
--- /dev/null
+++ b/mojo/edk/system/simple_dispatcher_unittest.cc
@@ -0,0 +1,604 @@
+// 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. |test::EpsilonDeadline()| may be increased to
+// increase tolerance and reduce observed flakiness (though doing so reduces the
+// meaningfulness of the test).
+
+#include "mojo/edk/system/simple_dispatcher.h"
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/system/test_utils.h"
+#include "mojo/edk/system/waiter.h"
+#include "mojo/edk/system/waiter_test_utils.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+class MockSimpleDispatcher final : public SimpleDispatcher {
+ public:
+ MockSimpleDispatcher()
+ : state_(MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE) {}
+
+ void SetSatisfiedSignals(MojoHandleSignals new_satisfied_signals) {
+ base::AutoLock locker(lock());
+
+ // Any new signals that are set should be satisfiable.
+ CHECK_EQ(new_satisfied_signals & ~state_.satisfied_signals,
+ new_satisfied_signals & ~state_.satisfied_signals &
+ state_.satisfiable_signals);
+
+ if (new_satisfied_signals == state_.satisfied_signals)
+ return;
+
+ state_.satisfied_signals = new_satisfied_signals;
+ HandleSignalsStateChangedNoLock();
+ }
+
+ void SetSatisfiableSignals(MojoHandleSignals new_satisfiable_signals) {
+ base::AutoLock locker(lock());
+
+ // Satisfied implies satisfiable.
+ CHECK_EQ(new_satisfiable_signals & state_.satisfied_signals,
+ state_.satisfied_signals);
+
+ if (new_satisfiable_signals == state_.satisfiable_signals)
+ return;
+
+ state_.satisfiable_signals = new_satisfiable_signals;
+ HandleSignalsStateChangedNoLock();
+ }
+
+ Type GetType() const override { return Type::UNKNOWN; }
+
+ private:
+ friend class base::RefCountedThreadSafe<MockSimpleDispatcher>;
+ ~MockSimpleDispatcher() override {}
+
+ scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock()
+ override {
+ scoped_refptr<MockSimpleDispatcher> rv(new MockSimpleDispatcher());
+ rv->state_ = state_;
+ return scoped_refptr<Dispatcher>(rv.get());
+ }
+
+ // |Dispatcher| override:
+ HandleSignalsState GetHandleSignalsStateImplNoLock() const override {
+ lock().AssertAcquired();
+ return state_;
+ }
+
+ // Protected by |lock()|:
+ HandleSignalsState state_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MockSimpleDispatcher);
+};
+
+#if defined(OS_WIN)
+// http://crbug.com/396404
+#define MAYBE_Basic DISABLED_Basic
+#else
+#define MAYBE_Basic Basic
+#endif
+TEST(SimpleDispatcherTest, MAYBE_Basic) {
+ test::Stopwatch stopwatch;
+
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ Waiter w;
+ uint32_t context = 0;
+ HandleSignalsState hss;
+
+ // Try adding a readable waiter when already readable.
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ hss = HandleSignalsState();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ hss.satisfiable_signals);
+ // Shouldn't need to remove the waiter (it was not added).
+
+ // Wait (forever) for writable when already writable.
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1, nullptr));
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ EXPECT_EQ(1u, context);
+ hss = HandleSignalsState();
+ d->RemoveAwakable(&w, &hss);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ hss.satisfiable_signals);
+
+ // Wait for zero time for writable when already writable.
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2, nullptr));
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_OK, w.Wait(0, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ EXPECT_EQ(2u, context);
+ hss = HandleSignalsState();
+ d->RemoveAwakable(&w, &hss);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ hss.satisfiable_signals);
+
+ // Wait for non-zero, finite time for writable when already writable.
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3, nullptr));
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_OK, w.Wait(2 * test::EpsilonDeadline(), &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ EXPECT_EQ(3u, context);
+ hss = HandleSignalsState();
+ d->RemoveAwakable(&w, &hss);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ hss.satisfiable_signals);
+
+ // Wait for zero time for writable when not writable (will time out).
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, nullptr));
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, nullptr));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ hss = HandleSignalsState();
+ d->RemoveAwakable(&w, &hss);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ hss.satisfiable_signals);
+
+ // Wait for non-zero, finite time for writable when not writable (will time
+ // out).
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 5, nullptr));
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ w.Wait(2 * test::EpsilonDeadline(), nullptr));
+ MojoDeadline elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
+ EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+ hss = HandleSignalsState();
+ d->RemoveAwakable(&w, &hss);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ hss.satisfiable_signals);
+
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+}
+
+TEST(SimpleDispatcherTest, BasicUnsatisfiable) {
+ test::Stopwatch stopwatch;
+
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ Waiter w;
+ uint32_t context = 0;
+ HandleSignalsState hss;
+
+ // Try adding a writable waiter when it can never be writable.
+ w.Init();
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ d->SetSatisfiedSignals(0);
+ hss = HandleSignalsState();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1, &hss));
+ EXPECT_EQ(0u, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+ // Shouldn't need to remove the waiter (it was not added).
+
+ // Wait (forever) for writable and then it becomes never writable.
+ w.Init();
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2, nullptr));
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ EXPECT_EQ(2u, context);
+ hss = HandleSignalsState();
+ d->RemoveAwakable(&w, &hss);
+ EXPECT_EQ(0u, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+ // Wait for zero time for writable and then it becomes never writable.
+ w.Init();
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3, nullptr));
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, w.Wait(0, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ EXPECT_EQ(3u, context);
+ hss = HandleSignalsState();
+ d->RemoveAwakable(&w, &hss);
+ EXPECT_EQ(0u, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+ // Wait for non-zero, finite time for writable and then it becomes never
+ // writable.
+ w.Init();
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, nullptr));
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ w.Wait(2 * test::EpsilonDeadline(), &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ EXPECT_EQ(4u, context);
+ hss = HandleSignalsState();
+ d->RemoveAwakable(&w, &hss);
+ EXPECT_EQ(0u, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals);
+
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+}
+
+TEST(SimpleDispatcherTest, BasicClosed) {
+ test::Stopwatch stopwatch;
+
+ scoped_refptr<MockSimpleDispatcher> d;
+ Waiter w;
+ uint32_t context = 0;
+ HandleSignalsState hss;
+
+ // Try adding a writable waiter when the dispatcher has been closed.
+ d = new MockSimpleDispatcher();
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ hss = HandleSignalsState();
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1, &hss));
+ EXPECT_EQ(0u, hss.satisfied_signals);
+ EXPECT_EQ(0u, hss.satisfiable_signals);
+ // 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();
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2, nullptr));
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ EXPECT_EQ(2u, context);
+ // 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();
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3, nullptr));
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(0, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ EXPECT_EQ(3u, context);
+ // 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();
+ ASSERT_EQ(MOJO_RESULT_OK,
+ d->AddAwakable(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4, nullptr));
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_CANCELLED,
+ w.Wait(2 * test::EpsilonDeadline(), &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ EXPECT_EQ(4u, context);
+ // Don't need to remove waiters from closed dispatchers.
+}
+
+#if defined(OS_WIN)
+// http://crbug.com/396393
+#define MAYBE_BasicThreaded DISABLED_BasicThreaded
+#else
+#define MAYBE_BasicThreaded BasicThreaded
+#endif
+TEST(SimpleDispatcherTest, MAYBE_BasicThreaded) {
+ test::Stopwatch stopwatch;
+ bool did_wait;
+ MojoResult result;
+ uint32_t context;
+ HandleSignalsState hss;
+
+ // Wait for readable (already readable).
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ {
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, 1, &did_wait, &result,
+ &context, &hss);
+ 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());
+ }
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonDeadline());
+ EXPECT_FALSE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ hss.satisfiable_signals);
+
+ // Wait for readable and becomes readable after some time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ {
+ test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, 2, &did_wait, &result,
+ &context, &hss);
+ stopwatch.Start();
+ thread.Start();
+ test::Sleep(2 * test::EpsilonDeadline());
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ } // Joins the thread.
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ }
+ MojoDeadline elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
+ EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(2u, context);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ hss.satisfiable_signals);
+
+ // Wait for readable and becomes never-readable after some time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ {
+ test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, 3, &did_wait, &result,
+ &context, &hss);
+ stopwatch.Start();
+ thread.Start();
+ test::Sleep(2 * test::EpsilonDeadline());
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_NONE);
+ } // Joins the thread.
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ }
+ elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
+ EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+ EXPECT_EQ(3u, context);
+ EXPECT_EQ(0u, hss.satisfied_signals);
+ EXPECT_EQ(0u, hss.satisfiable_signals);
+
+ // Wait for readable and dispatcher gets closed.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, 4, &did_wait, &result,
+ &context, &hss);
+ stopwatch.Start();
+ thread.Start();
+ test::Sleep(2 * test::EpsilonDeadline());
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the thread.
+ elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
+ EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ EXPECT_EQ(4u, context);
+ EXPECT_EQ(0u, hss.satisfied_signals);
+ EXPECT_EQ(0u, hss.satisfiable_signals);
+
+ // Wait for readable and times out.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ {
+ test::WaiterThread thread(d, MOJO_HANDLE_SIGNAL_READABLE,
+ 2 * test::EpsilonDeadline(), 5, &did_wait,
+ &result, &context, &hss);
+ stopwatch.Start();
+ thread.Start();
+ test::Sleep(1 * test::EpsilonDeadline());
+ // Not what we're waiting for.
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_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 = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2 - 1) * test::EpsilonDeadline());
+ EXPECT_LT(elapsed, (2 + 1) * test::EpsilonDeadline());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE,
+ hss.satisfiable_signals);
+}
+
+#if defined(OS_WIN)
+// http://crbug.com/387124
+#define MAYBE_MultipleWaiters DISABLED_MultipleWaiters
+#else
+#define MAYBE_MultipleWaiters MultipleWaiters
+#endif
+TEST(SimpleDispatcherTest, MAYBE_MultipleWaiters) {
+ static const uint32_t kNumWaiters = 20;
+
+ bool did_wait[kNumWaiters];
+ MojoResult result[kNumWaiters];
+ uint32_t context[kNumWaiters];
+ HandleSignalsState hss[kNumWaiters];
+
+ // All wait for readable and becomes readable after some time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ ScopedVector<test::WaiterThread> threads;
+ for (uint32_t i = 0; i < kNumWaiters; i++) {
+ threads.push_back(new test::WaiterThread(
+ d, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, i,
+ &did_wait[i], &result[i], &context[i], &hss[i]));
+ threads.back()->Start();
+ }
+ test::Sleep(2 * test::EpsilonDeadline());
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the threads.
+ for (uint32_t i = 0; i < kNumWaiters; i++) {
+ EXPECT_TRUE(did_wait[i]) << i;
+ EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i;
+ EXPECT_EQ(i, context[i]) << i;
+ // Since we closed before joining, we can't say much about what each thread
+ // saw as the state.
+ }
+
+ // Some wait for readable, some for writable, and becomes readable after some
+ // time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ ScopedVector<test::WaiterThread> threads;
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ threads.push_back(new test::WaiterThread(
+ d, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, i,
+ &did_wait[i], &result[i], &context[i], &hss[i]));
+ threads.back()->Start();
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ threads.push_back(new test::WaiterThread(
+ d, MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_DEADLINE_INDEFINITE, i,
+ &did_wait[i], &result[i], &context[i], &hss[i]));
+ threads.back()->Start();
+ }
+ test::Sleep(2 * test::EpsilonDeadline());
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ // This will wake up the ones waiting to write.
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the threads.
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ EXPECT_TRUE(did_wait[i]) << i;
+ EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i;
+ EXPECT_EQ(i, context[i]) << i;
+ // Since we closed before joining, we can't say much about what each thread
+ // saw as the state.
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ EXPECT_TRUE(did_wait[i]) << i;
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result[i]) << i;
+ EXPECT_EQ(i, context[i]) << i;
+ // Since we closed before joining, we can't say much about what each thread
+ // saw as the state.
+ }
+
+ // 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 (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ threads.push_back(new test::WaiterThread(
+ d, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, i,
+ &did_wait[i], &result[i], &context[i], &hss[i]));
+ threads.back()->Start();
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ threads.push_back(new test::WaiterThread(
+ d, MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_DEADLINE_INDEFINITE, i,
+ &did_wait[i], &result[i], &context[i], &hss[i]));
+ threads.back()->Start();
+ }
+ test::Sleep(1 * test::EpsilonDeadline());
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ test::Sleep(1 * test::EpsilonDeadline());
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the threads.
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ EXPECT_TRUE(did_wait[i]) << i;
+ EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i;
+ EXPECT_EQ(i, context[i]) << i;
+ // Since we closed before joining, we can't say much about what each thread
+ // saw as the state.
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ EXPECT_TRUE(did_wait[i]) << i;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result[i]) << i;
+ EXPECT_EQ(i, context[i]) << i;
+ // Since we closed before joining, we can't say much about what each thread
+ // saw as the state.
+ }
+
+ // Some wait for readable, some for writable, and becomes readable after some
+ // time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ ScopedVector<test::WaiterThread> threads;
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ threads.push_back(new test::WaiterThread(
+ d, MOJO_HANDLE_SIGNAL_READABLE, 3 * test::EpsilonDeadline(), i,
+ &did_wait[i], &result[i], &context[i], &hss[i]));
+ threads.back()->Start();
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ threads.push_back(new test::WaiterThread(
+ d, MOJO_HANDLE_SIGNAL_WRITABLE, 1 * test::EpsilonDeadline(), i,
+ &did_wait[i], &result[i], &context[i], &hss[i]));
+ threads.back()->Start();
+ }
+ test::Sleep(2 * test::EpsilonDeadline());
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ // All those waiting for writable should have timed out.
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the threads.
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ EXPECT_TRUE(did_wait[i]) << i;
+ EXPECT_EQ(MOJO_RESULT_OK, result[i]) << i;
+ EXPECT_EQ(i, context[i]) << i;
+ // Since we closed before joining, we can't say much about what each thread
+ // saw as the state.
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ EXPECT_TRUE(did_wait[i]) << i;
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result[i]) << i;
+ // Since we closed before joining, we can't say much about what each thread
+ // saw as the state.
+ }
+}
+
+// TODO(vtl): Stress test?
+
+} // namespace
+} // namespace edk
+} // namespace mojo