| Index: mojo/system/dispatcher_unittest.cc
|
| diff --git a/mojo/system/dispatcher_unittest.cc b/mojo/system/dispatcher_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a479fcec9e5907a86f41b663219b68404713a652
|
| --- /dev/null
|
| +++ b/mojo/system/dispatcher_unittest.cc
|
| @@ -0,0 +1,189 @@
|
| +// 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.
|
| +
|
| +#include "mojo/system/dispatcher.h"
|
| +
|
| +#include "base/basictypes.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/memory/scoped_vector.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "base/threading/simple_thread.h"
|
| +#include "mojo/system/waiter.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace mojo {
|
| +namespace system {
|
| +namespace {
|
| +
|
| +// Trivial subclass that makes the constructor public.
|
| +class TrivialDispatcher : public Dispatcher {
|
| + public:
|
| + TrivialDispatcher() {}
|
| +
|
| + private:
|
| + friend class base::RefCountedThreadSafe<TrivialDispatcher>;
|
| + virtual ~TrivialDispatcher() {}
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TrivialDispatcher);
|
| +};
|
| +
|
| +TEST(DispatcherTest, Basic) {
|
| + scoped_refptr<Dispatcher> d(new TrivialDispatcher());
|
| +
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
| + d->WriteMessage(NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
| + d->ReadMessage(NULL, NULL, NULL, NULL,
|
| + MOJO_WRITE_MESSAGE_FLAG_NONE));
|
| + Waiter w;
|
| + w.Init();
|
| + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
|
| + d->AddWaiter(&w, MOJO_WAIT_FLAG_EVERYTHING, 0));
|
| + // Okay to remove even if it wasn't added (or was already removed).
|
| + d->RemoveWaiter(&w);
|
| + d->RemoveWaiter(&w);
|
| +
|
| + EXPECT_EQ(MOJO_RESULT_OK, d->Close());
|
| +
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
| + d->WriteMessage(NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
| + d->ReadMessage(NULL, NULL, NULL, NULL,
|
| + MOJO_WRITE_MESSAGE_FLAG_NONE));
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
| + d->AddWaiter(&w, MOJO_WAIT_FLAG_EVERYTHING, 0));
|
| + d->RemoveWaiter(&w);
|
| +}
|
| +
|
| +class ThreadSafetyStressThread : public base::SimpleThread {
|
| + public:
|
| + enum DispatcherOp {
|
| + CLOSE = 0,
|
| + WRITE_MESSAGE,
|
| + READ_MESSAGE,
|
| + ADD_WAITER,
|
| + REMOVE_WAITER,
|
| +
|
| + DISPATCHER_OP_COUNT
|
| + };
|
| +
|
| + ThreadSafetyStressThread(base::WaitableEvent* event,
|
| + scoped_refptr<Dispatcher> dispatcher,
|
| + DispatcherOp op)
|
| + : base::SimpleThread("thread_safety_stress_thread"),
|
| + event_(event),
|
| + dispatcher_(dispatcher),
|
| + op_(op) {
|
| + CHECK_LE(0, op_);
|
| + CHECK_LT(op_, DISPATCHER_OP_COUNT);
|
| + }
|
| +
|
| + virtual ~ThreadSafetyStressThread() {
|
| + Join();
|
| + }
|
| +
|
| + private:
|
| + virtual void Run() OVERRIDE {
|
| + event_->Wait();
|
| +
|
| + waiter_.Init();
|
| + switch(op_) {
|
| + case CLOSE: {
|
| + MojoResult r = dispatcher_->Close();
|
| + EXPECT_TRUE(r == MOJO_RESULT_OK || r == MOJO_RESULT_INVALID_ARGUMENT)
|
| + << "Result: " << r;
|
| + break;
|
| + }
|
| + case WRITE_MESSAGE:
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
| + dispatcher_->WriteMessage(NULL, 0, NULL, 0,
|
| + MOJO_WRITE_MESSAGE_FLAG_NONE));
|
| + break;
|
| + case READ_MESSAGE:
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
| + dispatcher_->ReadMessage(NULL, NULL, NULL, NULL,
|
| + MOJO_WRITE_MESSAGE_FLAG_NONE));
|
| + break;
|
| + case ADD_WAITER: {
|
| + MojoResult r = dispatcher_->AddWaiter(&waiter_,
|
| + MOJO_WAIT_FLAG_EVERYTHING, 0);
|
| + EXPECT_TRUE(r == MOJO_RESULT_FAILED_PRECONDITION ||
|
| + r == MOJO_RESULT_INVALID_ARGUMENT);
|
| + break;
|
| + }
|
| + case REMOVE_WAITER:
|
| + dispatcher_->RemoveWaiter(&waiter_);
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| +
|
| + // Always try to remove the waiter, in case we added it.
|
| + dispatcher_->RemoveWaiter(&waiter_);
|
| + }
|
| +
|
| + base::WaitableEvent* const event_;
|
| + const scoped_refptr<Dispatcher> dispatcher_;
|
| + const DispatcherOp op_;
|
| +
|
| + Waiter waiter_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ThreadSafetyStressThread);
|
| +};
|
| +
|
| +TEST(DispatcherTest, ThreadSafetyStress) {
|
| + static const size_t kRepeatCount = 20;
|
| + static const size_t kNumThreads = 100;
|
| +
|
| + for (size_t i = 0; i < kRepeatCount; i++) {
|
| + // Manual reset, not initially signalled.
|
| + base::WaitableEvent event(true, false);
|
| + scoped_refptr<Dispatcher> d(new TrivialDispatcher());
|
| +
|
| + {
|
| + ScopedVector<ThreadSafetyStressThread> threads;
|
| + for (size_t j = 0; j < kNumThreads; j++) {
|
| + ThreadSafetyStressThread::DispatcherOp op =
|
| + static_cast<ThreadSafetyStressThread::DispatcherOp>(
|
| + (i+j) % ThreadSafetyStressThread::DISPATCHER_OP_COUNT);
|
| + threads.push_back(new ThreadSafetyStressThread(&event, d, op));
|
| + threads.back()->Start();
|
| + }
|
| + event.Signal(); // Kicks off real work on the threads.
|
| + } // Joins all the threads.
|
| +
|
| + // One of the threads should already have closed the dispatcher.
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->Close());
|
| + }
|
| +}
|
| +
|
| +TEST(DispatcherTest, ThreadSafetyStressNoClose) {
|
| + static const size_t kRepeatCount = 20;
|
| + static const size_t kNumThreads = 100;
|
| +
|
| + for (size_t i = 0; i < kRepeatCount; i++) {
|
| + // Manual reset, not initially signalled.
|
| + base::WaitableEvent event(true, false);
|
| + scoped_refptr<Dispatcher> d(new TrivialDispatcher());
|
| +
|
| + {
|
| + ScopedVector<ThreadSafetyStressThread> threads;
|
| + for (size_t j = 0; j < kNumThreads; j++) {
|
| + ThreadSafetyStressThread::DispatcherOp op =
|
| + static_cast<ThreadSafetyStressThread::DispatcherOp>(
|
| + (i+j) % (ThreadSafetyStressThread::DISPATCHER_OP_COUNT-1) + 1);
|
| + threads.push_back(new ThreadSafetyStressThread(&event, d, op));
|
| + threads.back()->Start();
|
| + }
|
| + event.Signal(); // Kicks off real work on the threads.
|
| + } // Joins all the threads.
|
| +
|
| + EXPECT_EQ(MOJO_RESULT_OK, d->Close());
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace system
|
| +} // namespace mojo
|
|
|