Home | History | Annotate | Download | only in system
      1 // Copyright 2017 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/public/cpp/system/wait.h"
      6 
      7 #include <memory>
      8 #include <vector>
      9 
     10 #include "base/memory/ptr_util.h"
     11 #include "base/memory/ref_counted.h"
     12 #include "base/synchronization/waitable_event.h"
     13 #include "mojo/public/c/system/trap.h"
     14 #include "mojo/public/cpp/system/trap.h"
     15 
     16 namespace mojo {
     17 namespace {
     18 
     19 class TriggerContext : public base::RefCountedThreadSafe<TriggerContext> {
     20  public:
     21   TriggerContext()
     22       : event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
     23                base::WaitableEvent::InitialState::NOT_SIGNALED) {}
     24 
     25   base::WaitableEvent& event() { return event_; }
     26   MojoResult wait_result() const { return wait_result_; }
     27   MojoHandleSignalsState wait_state() const { return wait_state_; }
     28   uintptr_t context_value() const { return reinterpret_cast<uintptr_t>(this); }
     29 
     30   static void OnNotification(const MojoTrapEvent* event) {
     31     auto* context = reinterpret_cast<TriggerContext*>(event->trigger_context);
     32     context->Notify(event->result, event->signals_state);
     33     if (event->result == MOJO_RESULT_CANCELLED) {
     34       // Balanced in Wait() or WaitMany().
     35       context->Release();
     36     }
     37   }
     38 
     39  private:
     40   friend class base::RefCountedThreadSafe<TriggerContext>;
     41 
     42   ~TriggerContext() {}
     43 
     44   void Notify(MojoResult result, MojoHandleSignalsState state) {
     45     if (wait_result_ == MOJO_RESULT_UNKNOWN) {
     46       wait_result_ = result;
     47       wait_state_ = state;
     48     }
     49     event_.Signal();
     50   }
     51 
     52   base::WaitableEvent event_;
     53 
     54   // NOTE: Although these are modified in Notify() which may be called from any
     55   // sequence, Notify() is guaranteed to never run concurrently with itself.
     56   // Furthermore, they are only modified once, before |event_| signals; so there
     57   // is no need for a TriggerContext user to synchronize access to these fields
     58   // apart from waiting on |event()|.
     59   MojoResult wait_result_ = MOJO_RESULT_UNKNOWN;
     60   MojoHandleSignalsState wait_state_ = {0, 0};
     61 
     62   DISALLOW_COPY_AND_ASSIGN(TriggerContext);
     63 };
     64 
     65 }  // namespace
     66 
     67 MojoResult Wait(Handle handle,
     68                 MojoHandleSignals signals,
     69                 MojoTriggerCondition condition,
     70                 MojoHandleSignalsState* signals_state) {
     71   ScopedTrapHandle trap;
     72   MojoResult rv = CreateTrap(&TriggerContext::OnNotification, &trap);
     73   DCHECK_EQ(MOJO_RESULT_OK, rv);
     74 
     75   scoped_refptr<TriggerContext> context = new TriggerContext;
     76 
     77   // Balanced in TriggerContext::OnNotification if MojoAddTrigger() is
     78   // successful. Otherwise balanced immediately below.
     79   context->AddRef();
     80 
     81   rv = MojoAddTrigger(trap.get().value(), handle.value(), signals, condition,
     82                       context->context_value(), nullptr);
     83   if (rv == MOJO_RESULT_INVALID_ARGUMENT) {
     84     // Balanced above.
     85     context->Release();
     86     return rv;
     87   }
     88   DCHECK_EQ(MOJO_RESULT_OK, rv);
     89 
     90   uint32_t num_blocking_events = 1;
     91   MojoTrapEvent blocking_event = {sizeof(blocking_event)};
     92   rv = MojoArmTrap(trap.get().value(), nullptr, &num_blocking_events,
     93                    &blocking_event);
     94   if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
     95     DCHECK_EQ(1u, num_blocking_events);
     96     if (signals_state)
     97       *signals_state = blocking_event.signals_state;
     98     return blocking_event.result;
     99   }
    100 
    101   // Wait for the first notification only.
    102   context->event().Wait();
    103 
    104   MojoResult ready_result = context->wait_result();
    105   DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result);
    106 
    107   if (signals_state)
    108     *signals_state = context->wait_state();
    109 
    110   return ready_result;
    111 }
    112 
    113 MojoResult WaitMany(const Handle* handles,
    114                     const MojoHandleSignals* signals,
    115                     size_t num_handles,
    116                     size_t* result_index,
    117                     MojoHandleSignalsState* signals_states) {
    118   if (!handles || !signals)
    119     return MOJO_RESULT_INVALID_ARGUMENT;
    120 
    121   ScopedTrapHandle trap;
    122   MojoResult rv = CreateTrap(&TriggerContext::OnNotification, &trap);
    123   DCHECK_EQ(MOJO_RESULT_OK, rv);
    124 
    125   std::vector<scoped_refptr<TriggerContext>> contexts(num_handles);
    126   std::vector<base::WaitableEvent*> events(num_handles);
    127   for (size_t i = 0; i < num_handles; ++i) {
    128     contexts[i] = new TriggerContext();
    129 
    130     // Balanced in TriggerContext::OnNotification if MojoAddTrigger() is
    131     // successful. Otherwise balanced immediately below.
    132     contexts[i]->AddRef();
    133 
    134     MojoResult rv =
    135         MojoAddTrigger(trap.get().value(), handles[i].value(), signals[i],
    136                        MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
    137                        contexts[i]->context_value(), nullptr);
    138     if (rv == MOJO_RESULT_INVALID_ARGUMENT) {
    139       if (result_index)
    140         *result_index = i;
    141 
    142       // Balanced above.
    143       contexts[i]->Release();
    144 
    145       return MOJO_RESULT_INVALID_ARGUMENT;
    146     }
    147 
    148     events[i] = &contexts[i]->event();
    149   }
    150 
    151   uint32_t num_blocking_events = 1;
    152   MojoTrapEvent blocking_event = {sizeof(blocking_event)};
    153   rv = MojoArmTrap(trap.get().value(), nullptr, &num_blocking_events,
    154                    &blocking_event);
    155 
    156   size_t index = num_handles;
    157   MojoResult ready_result = MOJO_RESULT_UNKNOWN;
    158   MojoHandleSignalsState ready_state = {};
    159   if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
    160     DCHECK_EQ(1u, num_blocking_events);
    161 
    162     // Most commonly we only watch a small number of handles. Just scan for
    163     // the right index.
    164     for (size_t i = 0; i < num_handles; ++i) {
    165       if (contexts[i]->context_value() == blocking_event.trigger_context) {
    166         index = i;
    167         ready_result = blocking_event.result;
    168         ready_state = blocking_event.signals_state;
    169         break;
    170       }
    171     }
    172   } else {
    173     DCHECK_EQ(MOJO_RESULT_OK, rv);
    174 
    175     // Wait for one of the contexts to signal. First one wins.
    176     index = base::WaitableEvent::WaitMany(events.data(), events.size());
    177     ready_result = contexts[index]->wait_result();
    178     ready_state = contexts[index]->wait_state();
    179   }
    180 
    181   DCHECK_NE(MOJO_RESULT_UNKNOWN, ready_result);
    182   DCHECK_LT(index, num_handles);
    183 
    184   if (result_index)
    185     *result_index = index;
    186 
    187   if (signals_states) {
    188     for (size_t i = 0; i < num_handles; ++i) {
    189       if (i == index) {
    190         signals_states[i] = ready_state;
    191       } else {
    192         signals_states[i] = handles[i].QuerySignalsState();
    193       }
    194     }
    195   }
    196 
    197   return ready_result;
    198 }
    199 
    200 }  // namespace mojo
    201