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