1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126

mojo / core / ipcz_driver / mojo_trap.h [blame]

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MOJO_CORE_IPCZ_DRIVER_MOJO_TRAP_H_
#define MOJO_CORE_IPCZ_DRIVER_MOJO_TRAP_H_

#include <cstdint>
#include <optional>

#include "base/containers/flat_map.h"
#include "base/containers/span.h"
#include "base/memory/scoped_refptr.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/threading/platform_thread_ref.h"
#include "mojo/core/ipcz_driver/object.h"
#include "mojo/public/c/system/trap.h"
#include "mojo/public/c/system/types.h"
#include "third_party/abseil-cpp/absl/container/inlined_vector.h"
#include "third_party/ipcz/include/ipcz/ipcz.h"

namespace mojo::core::ipcz_driver {

// Mojo traps are more complex than ipcz traps. A Mojo trap is approximately
// equivalent to a *collection* of ipcz traps (which Mojo would call "triggers"
// within a trap) sharing a common event handler.
//
// A Mojo trap can only be armed while all of its triggers' conditions are
// simultaneously unsatisfied. This object emulates that behavior well enough to
// suit Chromium's needs.
class MojoTrap : public Object<MojoTrap> {
 public:
  explicit MojoTrap(MojoTrapEventHandler handler);

  static Type object_type() { return kMojoTrap; }

  // Registers a new trigger on this trap. Each trigger corresponds to an active
  // ipcz trap when this Mojo trap is armed.
  MojoResult AddTrigger(MojoHandle handle,
                        MojoHandleSignals signals,
                        MojoTriggerCondition condition,
                        uintptr_t trigger_context);

  // Unregisters a trigger from the trap. If the trigger still has an ipcz trap
  // installed on `handle`, any event it may eventually fire will be ignored.
  MojoResult RemoveTrigger(uintptr_t trigger_context);

  // Attempts to arm this Mojo trap. Successful arming means that for every
  // trigger added, we can install a corresponding ipcz trap.
  MojoResult Arm(MojoTrapEvent* blocking_events, uint32_t* num_blocking_events);

  // ObjectBase:
  void Close() override;

 private:
  struct Trigger;

  ~MojoTrap() override;

  static void TrapEventHandler(const IpczTrapEvent* event);
  static void TrapRemovalEventHandler(const IpczTrapEvent* event);

  void HandleEvent(const IpczTrapEvent& event);
  void HandleTrapRemoved(const IpczTrapEvent& event);

  // Attempts to arm a single trigger by creating an ipcz trap for it. If this
  // fails because trapped conditions are already met, a corresponding event
  // is stored in `event`.
  IpczResult ArmTrigger(Trigger& trigger, MojoTrapEvent& event);

  void DispatchOrQueueTriggerRemoval(Trigger& trigger)
      EXCLUSIVE_LOCKS_REQUIRED(lock_);
  void DispatchOrQueueEvent(Trigger& trigger, const MojoTrapEvent& event)
      EXCLUSIVE_LOCKS_REQUIRED(lock_);
  void DispatchEvent(const MojoTrapEvent& event)
      EXCLUSIVE_LOCKS_REQUIRED(lock_);

  const MojoTrapEventHandler handler_;

  base::Lock lock_;

  // Condition variable used to wait for any other thread to finish dispatching
  // events so that another thread may dipatch its own.
  base::ConditionVariable dispatching_condition_ GUARDED_BY(lock_){&lock_};

  // The current number of waiters on |dispatching_condition_|.
  uint32_t waiters_ GUARDED_BY(lock_) = 0;

  // A ref identifying the thread which is currently dispatching an event for
  // this trap, if any.
  std::optional<base::PlatformThreadRef> dispatching_thread_ GUARDED_BY(lock_);

  using TriggerMap = base::flat_map<uintptr_t, scoped_refptr<Trigger>>;
  TriggerMap triggers_ GUARDED_BY(lock_);

  // Trigger prioritization proceeds in a round-robin fashion across consecutive
  // Arm() invocations. This iterator caches the most recently prioritized
  // entry.
  //
  // SUBTLE: Because it is invalidated by mutations to `triggers_`, this MUST
  // be reset any time a trigger is inserted or removed.
  TriggerMap::iterator next_trigger_ GUARDED_BY(lock_) = triggers_.end();

  // A Mojo trap must ensure that all its event dispatches are mutually
  // exclusive. While one thread is dispatching an event, other threads must
  // wait to acquire `dispatching_condition_` before dispatching anything; but
  // if the in-progress dispatch itself elicits new events on the trap, those
  // events are accumulated here and flushed (FIFO) after the in-progress
  // dispatch is done.
  struct PendingEvent {
    PendingEvent();
    PendingEvent(scoped_refptr<Trigger> trigger, const MojoTrapEvent& event);
    PendingEvent(PendingEvent&&);
    ~PendingEvent();
    scoped_refptr<Trigger> trigger;
    MojoTrapEvent event;
  };
  absl::InlinedVector<PendingEvent, 4> pending_mojo_events_ GUARDED_BY(lock_);

  bool armed_ GUARDED_BY(lock_) = false;
};

}  // namespace mojo::core::ipcz_driver

#endif  // MOJO_CORE_IPCZ_DRIVER_MOJO_TRAP_H_