// 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() {}
virtual scoped_refptr<Dispatcher>
CreateEquivalentDispatcherAndCloseImplNoLock() OVERRIDE {
scoped_refptr<MockSimpleDispatcher> rv(new MockSimpleDispatcher());
rv->satisfied_flags_ = satisfied_flags_;
rv->satisfiable_flags_ = satisfiable_flags_;
return scoped_refptr<Dispatcher>(rv.get());
}
// |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