| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "mojo/edk/system/wait_set_dispatcher.h" | 5 #include "mojo/edk/system/wait_set_dispatcher.h" |
| 6 | 6 |
| 7 #include <utility> |
| 8 |
| 7 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "mojo/edk/system/configuration.h" |
| 8 #include "mojo/edk/system/options_validation.h" | 11 #include "mojo/edk/system/options_validation.h" |
| 9 | 12 |
| 10 using mojo::util::MutexLocker; | 13 using mojo::util::MutexLocker; |
| 11 using mojo::util::RefPtr; | 14 using mojo::util::RefPtr; |
| 12 | 15 |
| 13 namespace mojo { | 16 namespace mojo { |
| 14 namespace system { | 17 namespace system { |
| 15 | 18 |
| 19 WaitSetDispatcher::Entry::Entry(MojoHandleSignals signals, uint64_t cookie) |
| 20 : signals(signals), cookie(cookie) {} |
| 21 |
| 22 WaitSetDispatcher::Entry::~Entry() {} |
| 23 |
| 16 // static | 24 // static |
| 17 constexpr MojoHandleRights WaitSetDispatcher::kDefaultHandleRights; | 25 constexpr MojoHandleRights WaitSetDispatcher::kDefaultHandleRights; |
| 18 | 26 |
| 19 // static | 27 // static |
| 20 const MojoCreateWaitSetOptions WaitSetDispatcher::kDefaultCreateOptions = { | 28 const MojoCreateWaitSetOptions WaitSetDispatcher::kDefaultCreateOptions = { |
| 21 static_cast<uint32_t>(sizeof(MojoCreateWaitSetOptions)), | 29 static_cast<uint32_t>(sizeof(MojoCreateWaitSetOptions)), |
| 22 MOJO_CREATE_WAIT_SET_OPTIONS_FLAG_NONE}; | 30 MOJO_CREATE_WAIT_SET_OPTIONS_FLAG_NONE}; |
| 23 | 31 |
| 24 // static | 32 // static |
| 25 MojoResult WaitSetDispatcher::ValidateCreateOptions( | 33 MojoResult WaitSetDispatcher::ValidateCreateOptions( |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 87 bool WaitSetDispatcher::SupportsEntrypointClass( | 95 bool WaitSetDispatcher::SupportsEntrypointClass( |
| 88 EntrypointClass entrypoint_class) const { | 96 EntrypointClass entrypoint_class) const { |
| 89 return (entrypoint_class == EntrypointClass::NONE || | 97 return (entrypoint_class == EntrypointClass::NONE || |
| 90 entrypoint_class == EntrypointClass::WAIT_SET); | 98 entrypoint_class == EntrypointClass::WAIT_SET); |
| 91 } | 99 } |
| 92 | 100 |
| 93 WaitSetDispatcher::WaitSetDispatcher() {} | 101 WaitSetDispatcher::WaitSetDispatcher() {} |
| 94 | 102 |
| 95 WaitSetDispatcher::~WaitSetDispatcher() {} | 103 WaitSetDispatcher::~WaitSetDispatcher() {} |
| 96 | 104 |
| 105 void WaitSetDispatcher::CloseImplNoLock() { |
| 106 mutex().AssertHeld(); |
| 107 |
| 108 CookieToEntryMap entries; |
| 109 std::swap(entries_, entries); |
| 110 possibly_triggered_head_ = nullptr; |
| 111 possibly_triggered_tail_ = nullptr; |
| 112 possibly_triggered_count_ = 0u; |
| 113 |
| 114 // We want to remove the awakables outside the lock, so we have to unlock |
| 115 // |mutex()|. Note that while unlocked, |Awake()| may get called. |
| 116 // TODO(vtl): This is pretty terrible, but changing it would require pretty |
| 117 // invasive changes in many other places. We really count on |Dispatcher| not |
| 118 // doing anything interesting after calling |CloseImplNoLock()|, and since |
| 119 // |CloseImplNoLock()| is allowed to do nothing all the lock invariants are |
| 120 // satisfied. |
| 121 DCHECK(is_closed_no_lock()); |
| 122 mutex().Unlock(); |
| 123 |
| 124 for (auto& p : entries) { |
| 125 const auto& entry = p.second; |
| 126 if (entry->dispatcher) |
| 127 entry->dispatcher->RemoveAwakableWithContext(this, entry->cookie, |
| 128 nullptr); |
| 129 } |
| 130 |
| 131 // The caller of |CloseImplNoLock()| expects |mutex()| to be locked, so we |
| 132 // have to re-lock it (even though it really should do nothing afterwards). |
| 133 mutex().Lock(); |
| 134 } |
| 135 |
| 97 RefPtr<Dispatcher> | 136 RefPtr<Dispatcher> |
| 98 WaitSetDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock( | 137 WaitSetDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock( |
| 99 MessagePipe* /*message_pipe*/, | 138 MessagePipe* /*message_pipe*/, |
| 100 unsigned /*port*/) { | 139 unsigned /*port*/) { |
| 101 mutex().AssertHeld(); | 140 mutex().AssertHeld(); |
| 102 NOTREACHED(); | 141 NOTREACHED(); |
| 103 return nullptr; | 142 return nullptr; |
| 104 } | 143 } |
| 105 | 144 |
| 106 MojoResult WaitSetDispatcher::WaitSetAddImpl( | 145 MojoResult WaitSetDispatcher::WaitSetAddImpl( |
| 107 UserPointer<const MojoWaitSetAddOptions> options, | 146 UserPointer<const MojoWaitSetAddOptions> options, |
| 108 Handle&& handle, | 147 RefPtr<Dispatcher>&& dispatcher, |
| 109 MojoHandleSignals signals, | 148 MojoHandleSignals signals, |
| 110 uint64_t cookie) { | 149 uint64_t cookie) { |
| 111 MutexLocker locker(&mutex()); | 150 Entry* entry = nullptr; |
| 112 if (is_closed_no_lock()) | 151 { |
| 152 MutexLocker locker(&mutex()); |
| 153 if (is_closed_no_lock()) |
| 154 return MOJO_RESULT_INVALID_ARGUMENT; |
| 155 if (is_busy_) |
| 156 return MOJO_RESULT_BUSY; |
| 157 MojoWaitSetAddOptions validated_options; |
| 158 MojoResult result = ValidateWaitSetAddOptions(options, &validated_options); |
| 159 if (result != MOJO_RESULT_OK) |
| 160 return result; |
| 161 if (entries_.find(cookie) != entries_.end()) |
| 162 return MOJO_RESULT_ALREADY_EXISTS; |
| 163 if (entries_.size() >= GetConfiguration().max_wait_set_num_entries) |
| 164 return MOJO_RESULT_RESOURCE_EXHAUSTED; |
| 165 // Note: We'll have to set the entry's dispatcher later. |
| 166 entry = new Entry(signals, cookie); |
| 167 entries_[cookie] = std::unique_ptr<Entry>(entry); |
| 168 |
| 169 is_busy_ = true; |
| 170 } |
| 171 |
| 172 HandleSignalsState signals_state; |
| 173 MojoResult result = dispatcher->AddAwakableUnconditional( |
| 174 this, signals, cookie, &signals_state); |
| 175 |
| 176 // Can't use |MutexLocker|, since we need to do some work outside the lock |
| 177 // in some code paths. |
| 178 mutex().Lock(); |
| 179 DCHECK(is_busy_); |
| 180 is_busy_ = false; |
| 181 |
| 182 // Note: We may have been closed while |mutex()| was unlocked, so we have to |
| 183 // check again! |
| 184 if (is_closed_no_lock()) { |
| 185 // Warning: In this case, |entry| has been invalidated, since it was owned |
| 186 // by |entries_|. |
| 187 DCHECK(entries_.empty()); |
| 188 mutex().Unlock(); |
| 189 if (result == MOJO_RESULT_OK || result == MOJO_RESULT_ALREADY_EXISTS) { |
| 190 // We have to remove ourself from the target dispatcher's awakable list. |
| 191 dispatcher->RemoveAwakableWithContext(this, cookie, nullptr); |
| 192 } |
| 113 return MOJO_RESULT_INVALID_ARGUMENT; | 193 return MOJO_RESULT_INVALID_ARGUMENT; |
| 114 MojoWaitSetAddOptions validated_options; | 194 } |
| 115 MojoResult result = ValidateWaitSetAddOptions(options, &validated_options); | 195 |
| 116 if (result != MOJO_RESULT_OK) | 196 DCHECK(entries_.find(cookie) != entries_.end()); |
| 197 DCHECK_EQ(entries_[cookie].get(), entry); |
| 198 |
| 199 if (result == MOJO_RESULT_ALREADY_EXISTS) { |
| 200 // It was added, but the wait condition is already satisfied. |
| 201 AddPossiblyTriggeredNoLock(entry, Entry::TriggerState::POSSIBLY_SATISFIED); |
| 202 } else if (result == MOJO_RESULT_FAILED_PRECONDITION) { |
| 203 // The condition is never-satisfiable. Leave a zombie entry (i.e., leave |
| 204 // |dispatcher| null). |
| 205 mutex().Unlock(); |
| 206 return MOJO_RESULT_OK; |
| 207 } else if (result != MOJO_RESULT_OK) { |
| 208 size_t num_erased = entries_.erase(cookie); |
| 209 DCHECK_EQ(num_erased, 1u); |
| 210 mutex().Unlock(); |
| 117 return result; | 211 return result; |
| 212 } |
| 118 | 213 |
| 119 // TODO(vtl) | 214 // Update the entry to actually have the dispatcher. |
| 120 NOTIMPLEMENTED(); | 215 entry->dispatcher = std::move(dispatcher); |
| 121 return MOJO_RESULT_UNIMPLEMENTED; | 216 |
| 217 mutex().Unlock(); |
| 218 return MOJO_RESULT_OK; |
| 122 } | 219 } |
| 123 | 220 |
| 124 MojoResult WaitSetDispatcher::WaitSetRemoveImpl(uint64_t cookie) { | 221 MojoResult WaitSetDispatcher::WaitSetRemoveImpl(uint64_t cookie) { |
| 125 MutexLocker locker(&mutex()); | 222 RefPtr<Dispatcher> dispatcher; |
| 126 if (is_closed_no_lock()) | 223 { |
| 127 return MOJO_RESULT_INVALID_ARGUMENT; | 224 MutexLocker locker(&mutex()); |
| 225 if (is_closed_no_lock()) |
| 226 return MOJO_RESULT_INVALID_ARGUMENT; |
| 227 if (is_busy_) |
| 228 return MOJO_RESULT_BUSY; |
| 229 auto it = entries_.find(cookie); |
| 230 if (it == entries_.end()) |
| 231 return MOJO_RESULT_NOT_FOUND; |
| 128 | 232 |
| 129 // TODO(vtl) | 233 Entry* entry = it->second.get(); |
| 130 NOTIMPLEMENTED(); | 234 // We'll remove ourself from the target dispatcher's awakable list outside |
| 131 return MOJO_RESULT_UNIMPLEMENTED; | 235 // the lock. |
| 236 dispatcher = std::move(entry->dispatcher); |
| 237 |
| 238 if (entry->trigger_state != Entry::TriggerState::NOT_TRIGGERED) |
| 239 RemovePossiblyTriggeredNoLock(entry); |
| 240 |
| 241 // Note: This invalidates |entry|. |
| 242 entries_.erase(it); |
| 243 } |
| 244 |
| 245 if (dispatcher) |
| 246 dispatcher->RemoveAwakableWithContext(this, cookie, nullptr); |
| 247 return MOJO_RESULT_OK; |
| 132 } | 248 } |
| 133 | 249 |
| 134 MojoResult WaitSetDispatcher::WaitSetWaitImpl( | 250 MojoResult WaitSetDispatcher::WaitSetWaitImpl( |
| 135 MojoDeadline deadline, | 251 MojoDeadline deadline, |
| 136 UserPointer<uint32_t> num_results, | 252 UserPointer<uint32_t> num_results, |
| 137 UserPointer<MojoWaitSetResult> results, | 253 UserPointer<MojoWaitSetResult> results, |
| 138 UserPointer<uint32_t> max_results) { | 254 UserPointer<uint32_t> max_results) { |
| 139 MutexLocker locker(&mutex()); | 255 MutexLocker locker(&mutex()); |
| 140 if (is_closed_no_lock()) | 256 if (is_closed_no_lock()) |
| 141 return MOJO_RESULT_INVALID_ARGUMENT; | 257 return MOJO_RESULT_INVALID_ARGUMENT; |
| 142 | 258 |
| 143 // TODO(vtl) | 259 // TODO(vtl) |
| 144 NOTIMPLEMENTED(); | 260 NOTIMPLEMENTED(); |
| 145 return MOJO_RESULT_UNIMPLEMENTED; | 261 return MOJO_RESULT_UNIMPLEMENTED; |
| 146 } | 262 } |
| 147 | 263 |
| 264 bool WaitSetDispatcher::Awake(MojoResult result, uint64_t context) { |
| 265 MutexLocker locker(&mutex()); |
| 266 |
| 267 if (is_closed_no_lock()) { |
| 268 // See |CloseImplNoLock()|: This case may occur while we're unlocked in |
| 269 // |CloseImplNoLock()| (after that, we will have been removed from all the |
| 270 // awakable lists, so |Awake()| should no longer be called). We may as well |
| 271 // return false here, which will automatically remove ourselves from the |
| 272 // awakable list (|CloseImplNoLock()| will call |
| 273 // |RemoveAwakableWithContext()| anyway, but that's OK). |
| 274 return false; |
| 275 } |
| 276 |
| 277 auto it = entries_.find(context); |
| 278 DCHECK(it != entries_.end()); |
| 279 const auto& entry = it->second; |
| 280 switch (result) { |
| 281 case MOJO_RESULT_OK: |
| 282 if (entry->trigger_state == Entry::TriggerState::NOT_TRIGGERED) { |
| 283 AddPossiblyTriggeredNoLock(entry.get(), |
| 284 Entry::TriggerState::POSSIBLY_SATISFIED); |
| 285 } |
| 286 return true; |
| 287 case MOJO_RESULT_CANCELLED: |
| 288 if (entry->trigger_state == Entry::TriggerState::NOT_TRIGGERED) { |
| 289 AddPossiblyTriggeredNoLock(entry.get(), Entry::TriggerState::CLOSED); |
| 290 } else { |
| 291 // We should only ever get at most one "closed". |
| 292 DCHECK_NE(static_cast<int>(entry->trigger_state), |
| 293 static_cast<int>(Entry::TriggerState::CLOSED)); |
| 294 entry->trigger_state = Entry::TriggerState::CLOSED; |
| 295 } |
| 296 entry->dispatcher = nullptr; |
| 297 return false; |
| 298 case MOJO_RESULT_FAILED_PRECONDITION: |
| 299 // Never satisfiable. |
| 300 if (entry->trigger_state == Entry::TriggerState::NOT_TRIGGERED) { |
| 301 AddPossiblyTriggeredNoLock(entry.get(), |
| 302 Entry::TriggerState::NEVER_SATISFIABLE); |
| 303 } else { |
| 304 if (entry->trigger_state == Entry::TriggerState::POSSIBLY_SATISFIED) { |
| 305 entry->trigger_state = Entry::TriggerState::NEVER_SATISFIABLE; |
| 306 } else { |
| 307 // It's possible to get repeated "never satisfiable" triggers, but we |
| 308 // shouldn't get anything after "closed". |
| 309 DCHECK_NE(static_cast<int>(entry->trigger_state), |
| 310 static_cast<int>(Entry::TriggerState::CLOSED)); |
| 311 } |
| 312 } |
| 313 // Due to some action on some other thread, it may become satisfiable |
| 314 // again, so continue to be awoken. |
| 315 return true; |
| 316 default: |
| 317 NOTREACHED(); |
| 318 break; |
| 319 } |
| 320 return false; |
| 321 } |
| 322 |
| 323 void WaitSetDispatcher::AddPossiblyTriggeredNoLock( |
| 324 Entry* entry, |
| 325 Entry::TriggerState new_trigger_state) { |
| 326 DCHECK_EQ(static_cast<int>(entry->trigger_state), |
| 327 static_cast<int>(Entry::TriggerState::NOT_TRIGGERED)); |
| 328 DCHECK(!entry->possibly_triggered_previous); |
| 329 DCHECK(!entry->possibly_triggered_next); |
| 330 DCHECK_NE(static_cast<int>(new_trigger_state), |
| 331 static_cast<int>(Entry::TriggerState::NOT_TRIGGERED)); |
| 332 |
| 333 entry->trigger_state = new_trigger_state; |
| 334 possibly_triggered_count_++; |
| 335 |
| 336 if (!possibly_triggered_tail_) { |
| 337 DCHECK(!possibly_triggered_head_); |
| 338 possibly_triggered_head_ = entry; |
| 339 possibly_triggered_tail_ = entry; |
| 340 return; |
| 341 } |
| 342 |
| 343 Entry* old_tail = possibly_triggered_tail_; |
| 344 entry->possibly_triggered_previous = old_tail; |
| 345 DCHECK(!old_tail->possibly_triggered_next); |
| 346 old_tail->possibly_triggered_next = entry; |
| 347 possibly_triggered_tail_ = entry; |
| 348 } |
| 349 |
| 350 void WaitSetDispatcher::RemovePossiblyTriggeredNoLock(Entry* entry) { |
| 351 DCHECK_NE(static_cast<int>(entry->trigger_state), |
| 352 static_cast<int>(Entry::TriggerState::NOT_TRIGGERED)); |
| 353 entry->trigger_state = Entry::TriggerState::NOT_TRIGGERED; |
| 354 possibly_triggered_count_--; |
| 355 |
| 356 if (!entry->possibly_triggered_previous) { |
| 357 DCHECK_EQ(entry, possibly_triggered_head_); |
| 358 possibly_triggered_head_ = entry->possibly_triggered_next; |
| 359 } else { |
| 360 entry->possibly_triggered_previous->possibly_triggered_next = |
| 361 entry->possibly_triggered_next; |
| 362 } |
| 363 |
| 364 if (!entry->possibly_triggered_next) { |
| 365 DCHECK_EQ(entry, possibly_triggered_tail_); |
| 366 possibly_triggered_tail_ = entry->possibly_triggered_previous; |
| 367 } else { |
| 368 entry->possibly_triggered_next->possibly_triggered_previous = |
| 369 entry->possibly_triggered_previous; |
| 370 } |
| 371 |
| 372 entry->possibly_triggered_previous = nullptr; |
| 373 entry->possibly_triggered_next = nullptr; |
| 374 } |
| 375 |
| 148 } // namespace system | 376 } // namespace system |
| 149 } // namespace mojo | 377 } // namespace mojo |
| OLD | NEW |