| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "mojo/edk/system/dispatcher.h" | |
| 6 | |
| 7 #include "base/macros.h" | |
| 8 #include "base/memory/ref_counted.h" | |
| 9 #include "base/memory/scoped_vector.h" | |
| 10 #include "base/synchronization/waitable_event.h" | |
| 11 #include "base/threading/simple_thread.h" | |
| 12 #include "mojo/edk/embedder/platform_shared_buffer.h" | |
| 13 #include "mojo/edk/system/memory.h" | |
| 14 #include "mojo/edk/system/waiter.h" | |
| 15 #include "testing/gtest/include/gtest/gtest.h" | |
| 16 | |
| 17 namespace mojo { | |
| 18 namespace system { | |
| 19 namespace { | |
| 20 | |
| 21 // Trivial subclass that makes the constructor public. | |
| 22 class TrivialDispatcher : public Dispatcher { | |
| 23 public: | |
| 24 TrivialDispatcher() {} | |
| 25 | |
| 26 Type GetType() const override { return kTypeUnknown; } | |
| 27 | |
| 28 private: | |
| 29 friend class base::RefCountedThreadSafe<TrivialDispatcher>; | |
| 30 ~TrivialDispatcher() override {} | |
| 31 | |
| 32 scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseImplNoLock() | |
| 33 override { | |
| 34 lock().AssertAcquired(); | |
| 35 return scoped_refptr<Dispatcher>(new TrivialDispatcher()); | |
| 36 } | |
| 37 | |
| 38 DISALLOW_COPY_AND_ASSIGN(TrivialDispatcher); | |
| 39 }; | |
| 40 | |
| 41 TEST(DispatcherTest, Basic) { | |
| 42 scoped_refptr<Dispatcher> d(new TrivialDispatcher()); | |
| 43 | |
| 44 EXPECT_EQ(Dispatcher::kTypeUnknown, d->GetType()); | |
| 45 | |
| 46 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 47 d->WriteMessage(NullUserPointer(), 0, nullptr, | |
| 48 MOJO_WRITE_MESSAGE_FLAG_NONE)); | |
| 49 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 50 d->ReadMessage(NullUserPointer(), NullUserPointer(), nullptr, | |
| 51 nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); | |
| 52 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 53 d->WriteData(NullUserPointer(), NullUserPointer(), | |
| 54 MOJO_WRITE_DATA_FLAG_NONE)); | |
| 55 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 56 d->BeginWriteData(NullUserPointer(), NullUserPointer(), | |
| 57 MOJO_WRITE_DATA_FLAG_NONE)); | |
| 58 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndWriteData(0)); | |
| 59 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 60 d->ReadData(NullUserPointer(), NullUserPointer(), | |
| 61 MOJO_READ_DATA_FLAG_NONE)); | |
| 62 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 63 d->BeginReadData(NullUserPointer(), NullUserPointer(), | |
| 64 MOJO_READ_DATA_FLAG_NONE)); | |
| 65 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndReadData(0)); | |
| 66 Waiter w; | |
| 67 w.Init(); | |
| 68 HandleSignalsState hss; | |
| 69 EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, | |
| 70 d->AddAwakable(&w, ~MOJO_HANDLE_SIGNAL_NONE, 0, &hss)); | |
| 71 EXPECT_EQ(0u, hss.satisfied_signals); | |
| 72 EXPECT_EQ(0u, hss.satisfiable_signals); | |
| 73 // Okay to remove even if it wasn't added (or was already removed). | |
| 74 hss = HandleSignalsState(); | |
| 75 d->RemoveAwakable(&w, &hss); | |
| 76 EXPECT_EQ(0u, hss.satisfied_signals); | |
| 77 EXPECT_EQ(0u, hss.satisfiable_signals); | |
| 78 hss = HandleSignalsState(); | |
| 79 d->RemoveAwakable(&w, &hss); | |
| 80 EXPECT_EQ(0u, hss.satisfied_signals); | |
| 81 EXPECT_EQ(0u, hss.satisfiable_signals); | |
| 82 | |
| 83 EXPECT_EQ(MOJO_RESULT_OK, d->Close()); | |
| 84 | |
| 85 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 86 d->WriteMessage(NullUserPointer(), 0, nullptr, | |
| 87 MOJO_WRITE_MESSAGE_FLAG_NONE)); | |
| 88 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 89 d->ReadMessage(NullUserPointer(), NullUserPointer(), nullptr, | |
| 90 nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE)); | |
| 91 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 92 d->WriteData(NullUserPointer(), NullUserPointer(), | |
| 93 MOJO_WRITE_DATA_FLAG_NONE)); | |
| 94 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 95 d->BeginWriteData(NullUserPointer(), NullUserPointer(), | |
| 96 MOJO_WRITE_DATA_FLAG_NONE)); | |
| 97 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndWriteData(0)); | |
| 98 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 99 d->ReadData(NullUserPointer(), NullUserPointer(), | |
| 100 MOJO_READ_DATA_FLAG_NONE)); | |
| 101 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 102 d->BeginReadData(NullUserPointer(), NullUserPointer(), | |
| 103 MOJO_READ_DATA_FLAG_NONE)); | |
| 104 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->EndReadData(0)); | |
| 105 hss = HandleSignalsState(); | |
| 106 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 107 d->AddAwakable(&w, ~MOJO_HANDLE_SIGNAL_NONE, 0, &hss)); | |
| 108 EXPECT_EQ(0u, hss.satisfied_signals); | |
| 109 EXPECT_EQ(0u, hss.satisfiable_signals); | |
| 110 hss = HandleSignalsState(); | |
| 111 d->RemoveAwakable(&w, &hss); | |
| 112 EXPECT_EQ(0u, hss.satisfied_signals); | |
| 113 EXPECT_EQ(0u, hss.satisfiable_signals); | |
| 114 } | |
| 115 | |
| 116 class ThreadSafetyStressThread : public base::SimpleThread { | |
| 117 public: | |
| 118 enum DispatcherOp { | |
| 119 CLOSE = 0, | |
| 120 WRITE_MESSAGE, | |
| 121 READ_MESSAGE, | |
| 122 WRITE_DATA, | |
| 123 BEGIN_WRITE_DATA, | |
| 124 END_WRITE_DATA, | |
| 125 READ_DATA, | |
| 126 BEGIN_READ_DATA, | |
| 127 END_READ_DATA, | |
| 128 DUPLICATE_BUFFER_HANDLE, | |
| 129 MAP_BUFFER, | |
| 130 ADD_WAITER, | |
| 131 REMOVE_WAITER, | |
| 132 DISPATCHER_OP_COUNT | |
| 133 }; | |
| 134 | |
| 135 ThreadSafetyStressThread(base::WaitableEvent* event, | |
| 136 scoped_refptr<Dispatcher> dispatcher, | |
| 137 DispatcherOp op) | |
| 138 : base::SimpleThread("thread_safety_stress_thread"), | |
| 139 event_(event), | |
| 140 dispatcher_(dispatcher), | |
| 141 op_(op) { | |
| 142 CHECK_LE(0, op_); | |
| 143 CHECK_LT(op_, DISPATCHER_OP_COUNT); | |
| 144 } | |
| 145 | |
| 146 ~ThreadSafetyStressThread() override { Join(); } | |
| 147 | |
| 148 private: | |
| 149 void Run() override { | |
| 150 event_->Wait(); | |
| 151 | |
| 152 waiter_.Init(); | |
| 153 switch (op_) { | |
| 154 case CLOSE: { | |
| 155 MojoResult r = dispatcher_->Close(); | |
| 156 EXPECT_TRUE(r == MOJO_RESULT_OK || r == MOJO_RESULT_INVALID_ARGUMENT) | |
| 157 << "Result: " << r; | |
| 158 break; | |
| 159 } | |
| 160 case WRITE_MESSAGE: | |
| 161 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 162 dispatcher_->WriteMessage(NullUserPointer(), 0, nullptr, | |
| 163 MOJO_WRITE_MESSAGE_FLAG_NONE)); | |
| 164 break; | |
| 165 case READ_MESSAGE: | |
| 166 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 167 dispatcher_->ReadMessage(NullUserPointer(), NullUserPointer(), | |
| 168 nullptr, nullptr, | |
| 169 MOJO_WRITE_MESSAGE_FLAG_NONE)); | |
| 170 break; | |
| 171 case WRITE_DATA: | |
| 172 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 173 dispatcher_->WriteData(NullUserPointer(), NullUserPointer(), | |
| 174 MOJO_WRITE_DATA_FLAG_NONE)); | |
| 175 break; | |
| 176 case BEGIN_WRITE_DATA: | |
| 177 EXPECT_EQ( | |
| 178 MOJO_RESULT_INVALID_ARGUMENT, | |
| 179 dispatcher_->BeginWriteData(NullUserPointer(), NullUserPointer(), | |
| 180 MOJO_WRITE_DATA_FLAG_NONE)); | |
| 181 break; | |
| 182 case END_WRITE_DATA: | |
| 183 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dispatcher_->EndWriteData(0)); | |
| 184 break; | |
| 185 case READ_DATA: | |
| 186 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, | |
| 187 dispatcher_->ReadData(NullUserPointer(), NullUserPointer(), | |
| 188 MOJO_READ_DATA_FLAG_NONE)); | |
| 189 break; | |
| 190 case BEGIN_READ_DATA: | |
| 191 EXPECT_EQ( | |
| 192 MOJO_RESULT_INVALID_ARGUMENT, | |
| 193 dispatcher_->BeginReadData(NullUserPointer(), NullUserPointer(), | |
| 194 MOJO_READ_DATA_FLAG_NONE)); | |
| 195 break; | |
| 196 case END_READ_DATA: | |
| 197 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dispatcher_->EndReadData(0)); | |
| 198 break; | |
| 199 case DUPLICATE_BUFFER_HANDLE: { | |
| 200 scoped_refptr<Dispatcher> unused; | |
| 201 EXPECT_EQ( | |
| 202 MOJO_RESULT_INVALID_ARGUMENT, | |
| 203 dispatcher_->DuplicateBufferHandle(NullUserPointer(), &unused)); | |
| 204 break; | |
| 205 } | |
| 206 case MAP_BUFFER: { | |
| 207 scoped_ptr<embedder::PlatformSharedBufferMapping> unused; | |
| 208 EXPECT_EQ( | |
| 209 MOJO_RESULT_INVALID_ARGUMENT, | |
| 210 dispatcher_->MapBuffer(0u, 0u, MOJO_MAP_BUFFER_FLAG_NONE, &unused)); | |
| 211 break; | |
| 212 } | |
| 213 case ADD_WAITER: { | |
| 214 HandleSignalsState hss; | |
| 215 MojoResult r = dispatcher_->AddAwakable( | |
| 216 &waiter_, ~MOJO_HANDLE_SIGNAL_NONE, 0, &hss); | |
| 217 EXPECT_TRUE(r == MOJO_RESULT_FAILED_PRECONDITION || | |
| 218 r == MOJO_RESULT_INVALID_ARGUMENT); | |
| 219 EXPECT_EQ(0u, hss.satisfied_signals); | |
| 220 EXPECT_EQ(0u, hss.satisfiable_signals); | |
| 221 break; | |
| 222 } | |
| 223 case REMOVE_WAITER: { | |
| 224 HandleSignalsState hss; | |
| 225 dispatcher_->RemoveAwakable(&waiter_, &hss); | |
| 226 EXPECT_EQ(0u, hss.satisfied_signals); | |
| 227 EXPECT_EQ(0u, hss.satisfiable_signals); | |
| 228 break; | |
| 229 } | |
| 230 default: | |
| 231 NOTREACHED(); | |
| 232 break; | |
| 233 } | |
| 234 | |
| 235 // Always try to remove the waiter, in case we added it. | |
| 236 HandleSignalsState hss; | |
| 237 dispatcher_->RemoveAwakable(&waiter_, &hss); | |
| 238 EXPECT_EQ(0u, hss.satisfied_signals); | |
| 239 EXPECT_EQ(0u, hss.satisfiable_signals); | |
| 240 } | |
| 241 | |
| 242 base::WaitableEvent* const event_; | |
| 243 const scoped_refptr<Dispatcher> dispatcher_; | |
| 244 const DispatcherOp op_; | |
| 245 | |
| 246 Waiter waiter_; | |
| 247 | |
| 248 DISALLOW_COPY_AND_ASSIGN(ThreadSafetyStressThread); | |
| 249 }; | |
| 250 | |
| 251 TEST(DispatcherTest, ThreadSafetyStress) { | |
| 252 static const size_t kRepeatCount = 20; | |
| 253 static const size_t kNumThreads = 100; | |
| 254 | |
| 255 for (size_t i = 0; i < kRepeatCount; i++) { | |
| 256 // Manual reset, not initially signalled. | |
| 257 base::WaitableEvent event(true, false); | |
| 258 scoped_refptr<Dispatcher> d(new TrivialDispatcher()); | |
| 259 | |
| 260 { | |
| 261 ScopedVector<ThreadSafetyStressThread> threads; | |
| 262 for (size_t j = 0; j < kNumThreads; j++) { | |
| 263 ThreadSafetyStressThread::DispatcherOp op = | |
| 264 static_cast<ThreadSafetyStressThread::DispatcherOp>( | |
| 265 (i + j) % ThreadSafetyStressThread::DISPATCHER_OP_COUNT); | |
| 266 threads.push_back(new ThreadSafetyStressThread(&event, d, op)); | |
| 267 threads.back()->Start(); | |
| 268 } | |
| 269 // Kicks off real work on the threads: | |
| 270 event.Signal(); | |
| 271 } // Joins all the threads. | |
| 272 | |
| 273 // One of the threads should already have closed the dispatcher. | |
| 274 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->Close()); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 TEST(DispatcherTest, ThreadSafetyStressNoClose) { | |
| 279 static const size_t kRepeatCount = 20; | |
| 280 static const size_t kNumThreads = 100; | |
| 281 | |
| 282 for (size_t i = 0; i < kRepeatCount; i++) { | |
| 283 // Manual reset, not initially signalled. | |
| 284 base::WaitableEvent event(true, false); | |
| 285 scoped_refptr<Dispatcher> d(new TrivialDispatcher()); | |
| 286 | |
| 287 { | |
| 288 ScopedVector<ThreadSafetyStressThread> threads; | |
| 289 for (size_t j = 0; j < kNumThreads; j++) { | |
| 290 ThreadSafetyStressThread::DispatcherOp op = | |
| 291 static_cast<ThreadSafetyStressThread::DispatcherOp>( | |
| 292 (i + j) % (ThreadSafetyStressThread::DISPATCHER_OP_COUNT - 1) + | |
| 293 1); | |
| 294 threads.push_back(new ThreadSafetyStressThread(&event, d, op)); | |
| 295 threads.back()->Start(); | |
| 296 } | |
| 297 // Kicks off real work on the threads: | |
| 298 event.Signal(); | |
| 299 } // Joins all the threads. | |
| 300 | |
| 301 EXPECT_EQ(MOJO_RESULT_OK, d->Close()); | |
| 302 } | |
| 303 } | |
| 304 | |
| 305 } // namespace | |
| 306 } // namespace system | |
| 307 } // namespace mojo | |
| OLD | NEW |