tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Shmem.h (6203B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_ipc_Shmem_h
      8 #define mozilla_ipc_Shmem_h
      9 
     10 #include "base/basictypes.h"
     11 #include "base/process.h"
     12 #include "chrome/common/ipc_message_utils.h"
     13 
     14 #include "nsISupports.h"
     15 #include "nscore.h"
     16 #include "nsDebug.h"
     17 
     18 #include "mozilla/ipc/SharedMemoryMapping.h"
     19 #include "mozilla/Range.h"
     20 #include "mozilla/UniquePtr.h"
     21 
     22 /**
     23 * |Shmem| is one agent in the IPDL shared memory scheme.  The way it
     24    works is essentially
     25 *
     26 *  (1) C++ code calls, say, |parentActor->AllocShmem(size)|
     27 
     28 *  (2) IPDL-generated code creates a |mozilla::ipc::SharedMemoryMapping|
     29 *  wrapping the bare OS shmem primitives.  The code then adds the new
     30 *  SharedMemory to the set of shmem segments being managed by IPDL.
     31 *
     32 *  (3) IPDL-generated code "shares" the new SharedMemory to the child
     33 *  process, and then sends a special asynchronous IPC message to the
     34 *  child notifying it of the creation of the segment.  (What this
     35 *  means is OS specific.)
     36 *
     37 *  (4a) The child receives the special IPC message, and using the
     38 *  |MutableSharedMemoryHandle| it was passed, creates a |SharedMemoryMapping|
     39 *  in the child process.
     40 *
     41 *  (4b) After sending the "shmem-created" IPC message, IPDL-generated
     42 *  code in the parent returns a |mozilla::ipc::Shmem| back to the C++
     43 *  caller of |parentActor->AllocShmem()|.  The |Shmem| is a "weak
     44 *  reference" to the underlying |SharedMemory|, which is managed by
     45 *  IPDL-generated code.  C++ consumers of |Shmem| can't get at the
     46 *  underlying |SharedMemoryMapping|.
     47 *
     48 * If parent code wants to give access rights to the Shmem to the
     49 * child, it does so by sending its |Shmem| to the child, in an IPDL
     50 * message.  The parent's |Shmem| then "dies", i.e. becomes
     51 * inaccessible.  This process could be compared to passing a
     52 * "shmem-access baton" between parent and child.
     53 */
     54 
     55 namespace mozilla::ipc {
     56 
     57 class IProtocol;
     58 class IToplevelProtocol;
     59 
     60 class Shmem final {
     61  friend struct IPC::ParamTraits<Shmem>;
     62  friend class IProtocol;
     63  friend class IToplevelProtocol;
     64 
     65 public:
     66  using id_t = int64_t;
     67  // Low-level wrapper around platform shmem primitives.
     68  class Segment final : public SharedMemoryMapping {
     69    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Segment);
     70 
     71    explicit Segment(SharedMemoryMapping&& aMapping)
     72        : SharedMemoryMapping(std::move(aMapping)) {}
     73 
     74   private:
     75    ~Segment() = default;
     76  };
     77 
     78  class Builder {
     79   public:
     80    explicit Builder(size_t aSize);
     81 
     82    explicit operator bool() const { return mSegment && mSegment->IsValid(); }
     83 
     84    // Prepare this to be shared with another process. Return an IPC message
     85    // that contains enough information for the other process to map this
     86    // segment in OpenExisting(), and the shmem.
     87    std::tuple<UniquePtr<IPC::Message>, Shmem> Build(
     88        id_t aId, bool aUnsafe, IPC::Message::routeid_t aRoutingId);
     89 
     90   private:
     91    size_t mSize;
     92    MutableSharedMemoryHandle mHandle;
     93    RefPtr<Segment> mSegment;
     94  };
     95 
     96  Shmem() : mSegment(nullptr), mData(nullptr), mSize(0), mId(0) {}
     97  Shmem(const Shmem& aOther) = default;
     98  ~Shmem() { forget(); }
     99 
    100  Shmem& operator=(const Shmem& aRhs) = default;
    101 
    102  bool operator==(const Shmem& aRhs) const { return mSegment == aRhs.mSegment; }
    103 
    104  // Returns whether this Shmem is writable by you, and thus whether you can
    105  // transfer writability to another actor.
    106  bool IsWritable() const { return mSegment != nullptr; }
    107 
    108  // Returns whether this Shmem is readable by you, and thus whether you can
    109  // transfer readability to another actor.
    110  bool IsReadable() const { return mSegment != nullptr; }
    111 
    112  // Return a pointer to the user-visible data segment.
    113  template <typename T>
    114  T* get() const {
    115    AssertInvariants();
    116    AssertAligned<T>();
    117 
    118    return reinterpret_cast<T*>(mData);
    119  }
    120 
    121  // Return the size of the segment as requested when this shmem
    122  // segment was allocated, in units of T.  The underlying mapping may
    123  // actually be larger because of page alignment and private data,
    124  // but this isn't exposed to clients.
    125  template <typename T>
    126  size_t Size() const {
    127    AssertInvariants();
    128    AssertAligned<T>();
    129 
    130    return mSize / sizeof(T);
    131  }
    132 
    133  template <typename T>
    134  Range<T> Range() const {
    135    return {get<T>(), Size<T>()};
    136  }
    137 
    138 private:
    139  // These shouldn't be used directly, use the IPDL interface instead.
    140 
    141  Shmem(RefPtr<Segment>&& aSegment, id_t aId, size_t aSize, bool aUnsafe);
    142 
    143  id_t Id() const { return mId; }
    144 
    145  Segment* GetSegment() const { return mSegment; }
    146 
    147 #ifndef DEBUG
    148  void RevokeRights() {}
    149 #else
    150  void RevokeRights();
    151 #endif
    152 
    153  void forget() {
    154    mSegment = nullptr;
    155    mData = nullptr;
    156    mSize = 0;
    157    mId = 0;
    158 #ifdef DEBUG
    159    mUnsafe = false;
    160 #endif
    161  }
    162 
    163  // Stop sharing this with another process. Return an IPC message that
    164  // contains enough information for the other process to unmap this
    165  // segment.  Return a new message if successful (owned by the
    166  // caller), nullptr if not.
    167  UniquePtr<IPC::Message> MkDestroyedMessage(IPC::Message::routeid_t routingId);
    168 
    169  // Return a Segment instance in this process using the descriptor shared
    170  // to us by the process that created the underlying OS shmem resource.  The
    171  // contents of the descriptor depend on the type of SharedMemory that was
    172  // passed to us.
    173  static already_AddRefed<Segment> OpenExisting(const IPC::Message& aDescriptor,
    174                                                id_t* aId,
    175                                                bool aProtect = false);
    176 
    177  template <typename T>
    178  void AssertAligned() const {
    179    if (0 != (mSize % sizeof(T))) MOZ_CRASH("shmem is not T-aligned");
    180  }
    181 
    182 #if !defined(DEBUG)
    183  void AssertInvariants() const {}
    184 #else
    185  void AssertInvariants() const;
    186 #endif
    187 
    188  RefPtr<Segment> mSegment;
    189  void* mData;
    190  size_t mSize;
    191  id_t mId;
    192 #ifdef DEBUG
    193  bool mUnsafe = false;
    194 #endif
    195 };
    196 
    197 }  // namespace mozilla::ipc
    198 
    199 #endif  // ifndef mozilla_ipc_Shmem_h