Home | History | Annotate | Download | only in system
      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/system/core_impl.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/logging.h"
     10 #include "base/time/time.h"
     11 #include "mojo/system/constants.h"
     12 #include "mojo/system/data_pipe_consumer_dispatcher.h"
     13 #include "mojo/system/data_pipe_producer_dispatcher.h"
     14 #include "mojo/system/dispatcher.h"
     15 #include "mojo/system/local_data_pipe.h"
     16 #include "mojo/system/memory.h"
     17 #include "mojo/system/message_pipe.h"
     18 #include "mojo/system/message_pipe_dispatcher.h"
     19 #include "mojo/system/waiter.h"
     20 
     21 namespace mojo {
     22 namespace system {
     23 
     24 // Implementation notes
     25 //
     26 // Mojo primitives are implemented by the singleton |CoreImpl| object. Most
     27 // calls are for a "primary" handle (the first argument).
     28 // |CoreImpl::GetDispatcher()| is used to look up a |Dispatcher| object for a
     29 // given handle. That object implements most primitives for that object. The
     30 // wait primitives are not attached to objects and are implemented by |CoreImpl|
     31 // itself.
     32 //
     33 // Some objects have multiple handles associated to them, e.g., message pipes
     34 // (which have two). In such a case, there is still a |Dispatcher| (e.g.,
     35 // |MessagePipeDispatcher|) for each handle, with each handle having a strong
     36 // reference to the common "secondary" object (e.g., |MessagePipe|). This
     37 // secondary object does NOT have any references to the |Dispatcher|s (even if
     38 // it did, it wouldn't be able to do anything with them due to lock order
     39 // requirements -- see below).
     40 //
     41 // Waiting is implemented by having the thread that wants to wait call the
     42 // |Dispatcher|s for the handles that it wants to wait on with a |Waiter|
     43 // object; this |Waiter| object may be created on the stack of that thread or be
     44 // kept in thread local storage for that thread (TODO(vtl): future improvement).
     45 // The |Dispatcher| then adds the |Waiter| to a |WaiterList| that's either owned
     46 // by that |Dispatcher| (see |SimpleDispatcher|) or by a secondary object (e.g.,
     47 // |MessagePipe|). To signal/wake a |Waiter|, the object in question -- either a
     48 // |SimpleDispatcher| or a secondary object -- talks to its |WaiterList|.
     49 
     50 // Thread-safety notes
     51 //
     52 // Mojo primitives calls are thread-safe. We achieve this with relatively
     53 // fine-grained locking. There is a global handle table lock. This lock should
     54 // be held as briefly as possible (TODO(vtl): a future improvement would be to
     55 // switch it to a reader-writer lock). Each |Dispatcher| object then has a lock
     56 // (which subclasses can use to protect their data).
     57 //
     58 // The lock ordering is as follows:
     59 //   1. global handle table lock
     60 //   2. |Dispatcher| locks
     61 //   3. secondary object locks
     62 //   ...
     63 //   INF. |Waiter| locks
     64 //
     65 // Notes:
     66 //    - While holding a |Dispatcher| lock, you may not unconditionally attempt
     67 //      to take another |Dispatcher| lock. (This has consequences on the
     68 //      concurrency semantics of |MojoWriteMessage()| when passing handles.)
     69 //      Doing so would lead to deadlock.
     70 //    - Locks at the "INF" level may not have any locks taken while they are
     71 //      held.
     72 
     73 CoreImpl::HandleTableEntry::HandleTableEntry()
     74     : busy(false) {
     75 }
     76 
     77 CoreImpl::HandleTableEntry::HandleTableEntry(
     78     const scoped_refptr<Dispatcher>& dispatcher)
     79     : dispatcher(dispatcher),
     80       busy(false) {
     81 }
     82 
     83 CoreImpl::HandleTableEntry::~HandleTableEntry() {
     84   DCHECK(!busy);
     85 }
     86 
     87 // static
     88 void CoreImpl::Init() {
     89   CorePrivate::Init(new CoreImpl());
     90 }
     91 
     92 MojoTimeTicks CoreImpl::GetTimeTicksNow() {
     93   return base::TimeTicks::Now().ToInternalValue();
     94 }
     95 
     96 MojoResult CoreImpl::Close(MojoHandle handle) {
     97   if (handle == MOJO_HANDLE_INVALID)
     98     return MOJO_RESULT_INVALID_ARGUMENT;
     99 
    100   scoped_refptr<Dispatcher> dispatcher;
    101   {
    102     base::AutoLock locker(handle_table_lock_);
    103     HandleTableMap::iterator it = handle_table_.find(handle);
    104     if (it == handle_table_.end())
    105       return MOJO_RESULT_INVALID_ARGUMENT;
    106     if (it->second.busy)
    107       return MOJO_RESULT_BUSY;
    108     dispatcher = it->second.dispatcher;
    109     handle_table_.erase(it);
    110   }
    111 
    112   // The dispatcher doesn't have a say in being closed, but gets notified of it.
    113   // Note: This is done outside of |handle_table_lock_|. As a result, there's a
    114   // race condition that the dispatcher must handle; see the comment in
    115   // |Dispatcher| in dispatcher.h.
    116   return dispatcher->Close();
    117 }
    118 
    119 MojoResult CoreImpl::Wait(MojoHandle handle,
    120                           MojoWaitFlags flags,
    121                           MojoDeadline deadline) {
    122   return WaitManyInternal(&handle, &flags, 1, deadline);
    123 }
    124 
    125 MojoResult CoreImpl::WaitMany(const MojoHandle* handles,
    126                               const MojoWaitFlags* flags,
    127                               uint32_t num_handles,
    128                               MojoDeadline deadline) {
    129   if (!VerifyUserPointer<MojoHandle>(handles, num_handles))
    130     return MOJO_RESULT_INVALID_ARGUMENT;
    131   if (!VerifyUserPointer<MojoWaitFlags>(flags, num_handles))
    132     return MOJO_RESULT_INVALID_ARGUMENT;
    133   if (num_handles < 1)
    134     return MOJO_RESULT_INVALID_ARGUMENT;
    135   if (num_handles > kMaxWaitManyNumHandles)
    136     return MOJO_RESULT_RESOURCE_EXHAUSTED;
    137   return WaitManyInternal(handles, flags, num_handles, deadline);
    138 }
    139 
    140 MojoResult CoreImpl::CreateMessagePipe(MojoHandle* message_pipe_handle_0,
    141                                        MojoHandle* message_pipe_handle_1) {
    142   if (!VerifyUserPointer<MojoHandle>(message_pipe_handle_0, 1))
    143     return MOJO_RESULT_INVALID_ARGUMENT;
    144   if (!VerifyUserPointer<MojoHandle>(message_pipe_handle_1, 1))
    145     return MOJO_RESULT_INVALID_ARGUMENT;
    146 
    147   scoped_refptr<MessagePipeDispatcher> dispatcher_0(
    148       new MessagePipeDispatcher());
    149   scoped_refptr<MessagePipeDispatcher> dispatcher_1(
    150       new MessagePipeDispatcher());
    151 
    152   MojoHandle h0, h1;
    153   {
    154     base::AutoLock locker(handle_table_lock_);
    155 
    156     h0 = AddDispatcherNoLock(dispatcher_0);
    157     if (h0 == MOJO_HANDLE_INVALID)
    158       return MOJO_RESULT_RESOURCE_EXHAUSTED;
    159 
    160     h1 = AddDispatcherNoLock(dispatcher_1);
    161     if (h1 == MOJO_HANDLE_INVALID) {
    162       handle_table_.erase(h0);
    163       return MOJO_RESULT_RESOURCE_EXHAUSTED;
    164     }
    165   }
    166 
    167   scoped_refptr<MessagePipe> message_pipe(new MessagePipe());
    168   dispatcher_0->Init(message_pipe, 0);
    169   dispatcher_1->Init(message_pipe, 1);
    170 
    171   *message_pipe_handle_0 = h0;
    172   *message_pipe_handle_1 = h1;
    173   return MOJO_RESULT_OK;
    174 }
    175 
    176 MojoResult CoreImpl::WriteMessage(MojoHandle message_pipe_handle,
    177                                   const void* bytes,
    178                                   uint32_t num_bytes,
    179                                   const MojoHandle* handles,
    180                                   uint32_t num_handles,
    181                                   MojoWriteMessageFlags flags) {
    182   scoped_refptr<Dispatcher> dispatcher(GetDispatcher(message_pipe_handle));
    183   if (!dispatcher.get())
    184     return MOJO_RESULT_INVALID_ARGUMENT;
    185 
    186   // Easy case: not sending any handles.
    187   if (num_handles == 0)
    188     return dispatcher->WriteMessage(bytes, num_bytes, NULL, flags);
    189 
    190   // We have to handle |handles| here, since we have to mark them busy in the
    191   // global handle table. We can't delegate this to the dispatcher, since the
    192   // handle table lock must be acquired before the dispatcher lock.
    193   //
    194   // (This leads to an oddity: |handles|/|num_handles| are always verified for
    195   // validity, even for dispatchers that don't support |WriteMessage()| and will
    196   // simply return failure unconditionally. It also breaks the usual
    197   // left-to-right verification order of arguments.)
    198   if (!VerifyUserPointer<MojoHandle>(handles, num_handles))
    199     return MOJO_RESULT_INVALID_ARGUMENT;
    200   if (num_handles > kMaxMessageNumHandles)
    201     return MOJO_RESULT_RESOURCE_EXHAUSTED;
    202 
    203   // We'll need to hold on to the dispatchers so that we can pass them on to
    204   // |WriteMessage()| and also so that we can unlock their locks afterwards
    205   // without accessing the handle table. These can be dumb pointers, since their
    206   // entries in the handle table won't get removed (since they'll be marked as
    207   // busy).
    208   std::vector<Dispatcher*> dispatchers(num_handles);
    209 
    210   // When we pass handles, we have to try to take all their dispatchers' locks
    211   // and mark the handles as busy. If the call succeeds, we then remove the
    212   // handles from the handle table.
    213   {
    214     base::AutoLock locker(handle_table_lock_);
    215 
    216     std::vector<HandleTableEntry*> entries(num_handles);
    217 
    218     // First verify all the handles and get their dispatchers.
    219     uint32_t i;
    220     MojoResult error_result = MOJO_RESULT_INTERNAL;
    221     for (i = 0; i < num_handles; i++) {
    222       // Sending your own handle is not allowed (and, for consistency, returns
    223       // "busy").
    224       if (handles[i] == message_pipe_handle) {
    225         error_result = MOJO_RESULT_BUSY;
    226         break;
    227       }
    228 
    229       HandleTableMap::iterator it = handle_table_.find(handles[i]);
    230       if (it == handle_table_.end()) {
    231         error_result = MOJO_RESULT_INVALID_ARGUMENT;
    232         break;
    233       }
    234 
    235       entries[i] = &it->second;
    236       if (entries[i]->busy) {
    237         error_result = MOJO_RESULT_BUSY;
    238         break;
    239       }
    240       // Note: By marking the handle as busy here, we're also preventing the
    241       // same handle from being sent multiple times in the same message.
    242       entries[i]->busy = true;
    243 
    244       // Try to take the lock.
    245       if (!entries[i]->dispatcher->lock().Try()) {
    246         // Unset the busy flag (since it won't be unset below).
    247         entries[i]->busy = false;
    248         error_result = MOJO_RESULT_BUSY;
    249         break;
    250       }
    251 
    252       // We shouldn't race with things that close dispatchers, since closing can
    253       // only take place either under |handle_table_lock_| or when the handle is
    254       // marked as busy.
    255       DCHECK(!entries[i]->dispatcher->is_closed_no_lock());
    256 
    257       // Hang on to the pointer to the dispatcher (which we'll need to release
    258       // the lock without going through the handle table).
    259       dispatchers[i] = entries[i]->dispatcher;
    260     }
    261     if (i < num_handles) {
    262       DCHECK_NE(error_result, MOJO_RESULT_INTERNAL);
    263 
    264       // Unset the busy flags and release the locks.
    265       for (uint32_t j = 0; j < i; j++) {
    266         DCHECK(entries[j]->busy);
    267         entries[j]->busy = false;
    268         entries[j]->dispatcher->lock().Release();
    269       }
    270       return error_result;
    271     }
    272   }
    273 
    274   MojoResult rv = dispatcher->WriteMessage(bytes, num_bytes,
    275                                            &dispatchers,
    276                                            flags);
    277 
    278   // We need to release the dispatcher locks before we take the handle table
    279   // lock.
    280   for (uint32_t i = 0; i < num_handles; i++) {
    281     dispatchers[i]->lock().AssertAcquired();
    282     dispatchers[i]->lock().Release();
    283   }
    284 
    285   if (rv == MOJO_RESULT_OK) {
    286     base::AutoLock locker(handle_table_lock_);
    287 
    288     // Succeeded, so the handles should be removed from the handle table. (The
    289     // transferring to new dispatchers/closing must have already been done.)
    290     for (uint32_t i = 0; i < num_handles; i++) {
    291       HandleTableMap::iterator it = handle_table_.find(handles[i]);
    292       DCHECK(it != handle_table_.end());
    293       DCHECK(it->second.busy);
    294       it->second.busy = false;  // For the sake of a |DCHECK()|.
    295       handle_table_.erase(it);
    296     }
    297   } else {
    298     base::AutoLock locker(handle_table_lock_);
    299 
    300     // Failed, so the handles should go back to their normal state.
    301     for (uint32_t i = 0; i < num_handles; i++) {
    302       HandleTableMap::iterator it = handle_table_.find(handles[i]);
    303       DCHECK(it != handle_table_.end());
    304       DCHECK(it->second.busy);
    305       it->second.busy = false;
    306     }
    307   }
    308 
    309   return rv;
    310 }
    311 
    312 MojoResult CoreImpl::ReadMessage(MojoHandle message_pipe_handle,
    313                                  void* bytes,
    314                                  uint32_t* num_bytes,
    315                                  MojoHandle* handles,
    316                                  uint32_t* num_handles,
    317                                  MojoReadMessageFlags flags) {
    318   scoped_refptr<Dispatcher> dispatcher(GetDispatcher(message_pipe_handle));
    319   if (!dispatcher.get())
    320     return MOJO_RESULT_INVALID_ARGUMENT;
    321 
    322   if (num_handles) {
    323     if (!VerifyUserPointer<uint32_t>(num_handles, 1))
    324       return MOJO_RESULT_INVALID_ARGUMENT;
    325     if (!VerifyUserPointer<MojoHandle>(handles, *num_handles))
    326       return MOJO_RESULT_INVALID_ARGUMENT;
    327   }
    328 
    329   // Easy case: won't receive any handles.
    330   if (!num_handles || *num_handles == 0)
    331     return dispatcher->ReadMessage(bytes, num_bytes, NULL, num_handles, flags);
    332 
    333   std::vector<scoped_refptr<Dispatcher> > dispatchers;
    334   MojoResult rv = dispatcher->ReadMessage(bytes, num_bytes,
    335                                           &dispatchers, num_handles,
    336                                           flags);
    337   if (!dispatchers.empty()) {
    338     DCHECK_EQ(rv, MOJO_RESULT_OK);
    339     DCHECK(num_handles);
    340     DCHECK_LE(dispatchers.size(), static_cast<size_t>(*num_handles));
    341 
    342     base::AutoLock locker(handle_table_lock_);
    343 
    344     for (size_t i = 0; i < dispatchers.size(); i++) {
    345       // TODO(vtl): What should we do if we hit the maximum handle table size
    346       // here? Currently, we'll just fill in those handles with
    347       // |MOJO_HANDLE_INVALID| (and return success anyway).
    348       handles[i] = AddDispatcherNoLock(dispatchers[i]);
    349     }
    350   }
    351 
    352   return rv;
    353 }
    354 
    355 MojoResult CoreImpl::CreateDataPipe(const MojoCreateDataPipeOptions* options,
    356                                     MojoHandle* data_pipe_producer_handle,
    357                                     MojoHandle* data_pipe_consumer_handle) {
    358   if (options && !VerifyUserPointer<void>(options, sizeof(*options)))
    359     return MOJO_RESULT_INVALID_ARGUMENT;
    360   if (!VerifyUserPointer<MojoHandle>(data_pipe_producer_handle, 1))
    361     return MOJO_RESULT_INVALID_ARGUMENT;
    362   if (!VerifyUserPointer<MojoHandle>(data_pipe_consumer_handle, 1))
    363     return MOJO_RESULT_INVALID_ARGUMENT;
    364 
    365   scoped_refptr<LocalDataPipe> data_pipe(new LocalDataPipe());
    366   MojoResult result = data_pipe->Init(options);
    367   if (result != MOJO_RESULT_OK)
    368     return result;
    369 
    370   scoped_refptr<DataPipeProducerDispatcher> producer_dispatcher(
    371       new DataPipeProducerDispatcher());
    372   scoped_refptr<DataPipeConsumerDispatcher> consumer_dispatcher(
    373       new DataPipeConsumerDispatcher());
    374 
    375   MojoHandle producer_handle, consumer_handle;
    376   {
    377     base::AutoLock locker(handle_table_lock_);
    378 
    379     producer_handle = AddDispatcherNoLock(producer_dispatcher);
    380     if (producer_handle == MOJO_HANDLE_INVALID)
    381       return MOJO_RESULT_RESOURCE_EXHAUSTED;
    382 
    383     consumer_handle = AddDispatcherNoLock(consumer_dispatcher);
    384     if (consumer_handle == MOJO_HANDLE_INVALID) {
    385       handle_table_.erase(producer_handle);
    386       return MOJO_RESULT_RESOURCE_EXHAUSTED;
    387     }
    388   }
    389 
    390   producer_dispatcher->Init(data_pipe);
    391   consumer_dispatcher->Init(data_pipe);
    392 
    393   *data_pipe_producer_handle = producer_handle;
    394   *data_pipe_consumer_handle = consumer_handle;
    395   return MOJO_RESULT_OK;
    396 }
    397 
    398 MojoResult CoreImpl::WriteData(MojoHandle data_pipe_producer_handle,
    399                                const void* elements,
    400                                uint32_t* num_elements,
    401                                MojoWriteDataFlags flags) {
    402   scoped_refptr<Dispatcher> dispatcher(
    403       GetDispatcher(data_pipe_producer_handle));
    404   if (!dispatcher.get())
    405     return MOJO_RESULT_INVALID_ARGUMENT;
    406 
    407   return dispatcher->WriteData(elements, num_elements, flags);
    408 }
    409 
    410 MojoResult CoreImpl::BeginWriteData(MojoHandle data_pipe_producer_handle,
    411                                     void** buffer,
    412                                     uint32_t* buffer_num_elements,
    413                                     MojoWriteDataFlags flags) {
    414   scoped_refptr<Dispatcher> dispatcher(
    415       GetDispatcher(data_pipe_producer_handle));
    416   if (!dispatcher.get())
    417     return MOJO_RESULT_INVALID_ARGUMENT;
    418 
    419   return dispatcher->BeginWriteData(buffer, buffer_num_elements, flags);
    420 }
    421 
    422 MojoResult CoreImpl::EndWriteData(MojoHandle data_pipe_producer_handle,
    423                                   uint32_t num_elements_written) {
    424   scoped_refptr<Dispatcher> dispatcher(
    425       GetDispatcher(data_pipe_producer_handle));
    426   if (!dispatcher.get())
    427     return MOJO_RESULT_INVALID_ARGUMENT;
    428 
    429   return dispatcher->EndWriteData(num_elements_written);
    430 }
    431 
    432 MojoResult CoreImpl::ReadData(MojoHandle data_pipe_consumer_handle,
    433                               void* elements,
    434                               uint32_t* num_elements,
    435                               MojoReadDataFlags flags) {
    436   scoped_refptr<Dispatcher> dispatcher(
    437       GetDispatcher(data_pipe_consumer_handle));
    438   if (!dispatcher.get())
    439     return MOJO_RESULT_INVALID_ARGUMENT;
    440 
    441   return dispatcher->ReadData(elements, num_elements, flags);
    442 }
    443 
    444 MojoResult CoreImpl::BeginReadData(MojoHandle data_pipe_consumer_handle,
    445                                    const void** buffer,
    446                                    uint32_t* buffer_num_elements,
    447                                    MojoReadDataFlags flags) {
    448   scoped_refptr<Dispatcher> dispatcher(
    449       GetDispatcher(data_pipe_consumer_handle));
    450   if (!dispatcher.get())
    451     return MOJO_RESULT_INVALID_ARGUMENT;
    452 
    453   return dispatcher->BeginReadData(buffer, buffer_num_elements, flags);
    454 }
    455 
    456 MojoResult CoreImpl::EndReadData(MojoHandle data_pipe_consumer_handle,
    457                                  uint32_t num_elements_read) {
    458   scoped_refptr<Dispatcher> dispatcher(
    459       GetDispatcher(data_pipe_consumer_handle));
    460   if (!dispatcher.get())
    461     return MOJO_RESULT_INVALID_ARGUMENT;
    462 
    463   return dispatcher->EndReadData(num_elements_read);
    464 }
    465 
    466 CoreImpl::CoreImpl()
    467     : next_handle_(MOJO_HANDLE_INVALID + 1) {
    468 }
    469 
    470 CoreImpl::~CoreImpl() {
    471   // This should usually not be reached (the singleton lives forever), except in
    472   // tests.
    473 }
    474 
    475 scoped_refptr<Dispatcher> CoreImpl::GetDispatcher(MojoHandle handle) {
    476   if (handle == MOJO_HANDLE_INVALID)
    477     return NULL;
    478 
    479   base::AutoLock locker(handle_table_lock_);
    480   HandleTableMap::iterator it = handle_table_.find(handle);
    481   if (it == handle_table_.end())
    482     return NULL;
    483 
    484   return it->second.dispatcher;
    485 }
    486 
    487 MojoHandle CoreImpl::AddDispatcherNoLock(
    488     const scoped_refptr<Dispatcher>& dispatcher) {
    489   DCHECK(dispatcher.get());
    490   handle_table_lock_.AssertAcquired();
    491   DCHECK_NE(next_handle_, MOJO_HANDLE_INVALID);
    492 
    493   if (handle_table_.size() >= kMaxHandleTableSize)
    494     return MOJO_HANDLE_INVALID;
    495 
    496   // TODO(vtl): Maybe we want to do something different/smarter. (Or maybe try
    497   // assigning randomly?)
    498   while (handle_table_.find(next_handle_) != handle_table_.end()) {
    499     next_handle_++;
    500     if (next_handle_ == MOJO_HANDLE_INVALID)
    501       next_handle_++;
    502   }
    503 
    504   MojoHandle new_handle = next_handle_;
    505   handle_table_[new_handle] = HandleTableEntry(dispatcher);
    506 
    507   next_handle_++;
    508   if (next_handle_ == MOJO_HANDLE_INVALID)
    509     next_handle_++;
    510 
    511   return new_handle;
    512 }
    513 
    514 // Note: We allow |handles| to repeat the same handle multiple times, since
    515 // different flags may be specified.
    516 // TODO(vtl): This incurs a performance cost in |RemoveWaiter()|. Analyze this
    517 // more carefully and address it if necessary.
    518 MojoResult CoreImpl::WaitManyInternal(const MojoHandle* handles,
    519                                       const MojoWaitFlags* flags,
    520                                       uint32_t num_handles,
    521                                       MojoDeadline deadline) {
    522   DCHECK_GT(num_handles, 0u);
    523 
    524   std::vector<scoped_refptr<Dispatcher> > dispatchers;
    525   dispatchers.reserve(num_handles);
    526   for (uint32_t i = 0; i < num_handles; i++) {
    527     scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handles[i]);
    528     if (!dispatcher.get())
    529       return MOJO_RESULT_INVALID_ARGUMENT;
    530     dispatchers.push_back(dispatcher);
    531   }
    532 
    533   // TODO(vtl): Should make the waiter live (permanently) in TLS.
    534   Waiter waiter;
    535   waiter.Init();
    536 
    537   uint32_t i;
    538   MojoResult rv = MOJO_RESULT_OK;
    539   for (i = 0; i < num_handles; i++) {
    540     rv = dispatchers[i]->AddWaiter(&waiter,
    541                                    flags[i],
    542                                    static_cast<MojoResult>(i));
    543     if (rv != MOJO_RESULT_OK)
    544       break;
    545   }
    546   uint32_t num_added = i;
    547 
    548   if (rv == MOJO_RESULT_ALREADY_EXISTS)
    549     rv = static_cast<MojoResult>(i);  // The i-th one is already "triggered".
    550   else if (rv == MOJO_RESULT_OK)
    551     rv = waiter.Wait(deadline);
    552 
    553   // Make sure no other dispatchers try to wake |waiter| for the current
    554   // |Wait()|/|WaitMany()| call. (Only after doing this can |waiter| be
    555   // destroyed, but this would still be required if the waiter were in TLS.)
    556   for (i = 0; i < num_added; i++)
    557     dispatchers[i]->RemoveWaiter(&waiter);
    558 
    559   return rv;
    560 }
    561 
    562 }  // namespace system
    563 }  // namespace mojo
    564