tor-browser

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

SourceSurfaceSharedData.h (10780B)


      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_GFX_SOURCESURFACESHAREDDATA_H_
      8 #define MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_
      9 
     10 #include "base/process.h"
     11 #include "mozilla/gfx/2D.h"
     12 #include "mozilla/Mutex.h"
     13 #include "mozilla/ipc/SharedMemoryHandle.h"
     14 #include "mozilla/ipc/SharedMemoryMapping.h"
     15 #include "nsExpirationTracker.h"
     16 
     17 namespace mozilla {
     18 namespace gfx {
     19 
     20 class SourceSurfaceSharedData;
     21 
     22 /**
     23 * This class is used to wrap shared (as in process) data buffers allocated by
     24 * a SourceSurfaceSharedData object. It may live in the same process or a
     25 * different process from the actual SourceSurfaceSharedData object.
     26 *
     27 * If it is in the same process, mBuf is the same object as that in the surface.
     28 * It is a useful abstraction over just using the surface directly, because it
     29 * can have a different lifetime from the surface; if the surface gets freed,
     30 * consumers may continue accessing the data in the buffer. Releasing the
     31 * original surface is a signal which feeds into SharedSurfacesParent to decide
     32 * to release the SourceSurfaceSharedDataWrapper.
     33 *
     34 * If it is in a different process, mBuf is a new SharedMemory object which
     35 * mapped in the given shared memory handle as read only memory.
     36 */
     37 class SourceSurfaceSharedDataWrapper final : public DataSourceSurface {
     38 public:
     39  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedDataWrapper,
     40                                          override)
     41 
     42  SourceSurfaceSharedDataWrapper() = default;
     43 
     44  void Init(const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat,
     45            mozilla::ipc::ReadOnlySharedMemoryHandle aHandle,
     46            base::ProcessId aCreatorPid);
     47 
     48  void Init(SourceSurfaceSharedData* aSurface);
     49 
     50  base::ProcessId GetCreatorPid() const { return mCreatorPid; }
     51 
     52  int32_t Stride() override { return mStride; }
     53 
     54  SurfaceType GetType() const override {
     55    return SurfaceType::DATA_SHARED_WRAPPER;
     56  }
     57  IntSize GetSize() const override { return mSize; }
     58  SurfaceFormat GetFormat() const override { return mFormat; }
     59 
     60  uint8_t* GetData() override {
     61    // Cast away const-ness of shared memory to match the
     62    // `DataSourceSurface::GetData` interface. The data will not be written to.
     63    return mBuf ? const_cast<uint8_t*>(mBuf->DataAs<uint8_t>()) : nullptr;
     64  }
     65 
     66  bool OnHeap() const override { return false; }
     67 
     68  bool Map(MapType aMapType, MappedSurface* aMappedSurface) final;
     69 
     70  void Unmap() final;
     71 
     72  void ExpireMap();
     73 
     74  bool AddConsumer() { return ++mConsumers == 1; }
     75 
     76  bool RemoveConsumer(bool aForCreator) {
     77    MOZ_ASSERT(mConsumers > 0);
     78    if (aForCreator) {
     79      if (!mCreatorRef) {
     80        MOZ_ASSERT_UNREACHABLE("Already released creator reference!");
     81        return false;
     82      }
     83      mCreatorRef = false;
     84    }
     85    return --mConsumers == 0;
     86  }
     87 
     88  uint32_t GetConsumers() const { return mConsumers; }
     89 
     90  bool HasCreatorRef() const { return mCreatorRef; }
     91 
     92  nsExpirationState* GetExpirationState() { return &mExpirationState; }
     93 
     94 private:
     95  ~SourceSurfaceSharedDataWrapper() override {
     96    MOZ_RELEASE_ASSERT(!mExpirationState.IsTracked());
     97  }
     98 
     99  size_t GetDataLength() const {
    100    return static_cast<size_t>(mStride) * mSize.height;
    101  }
    102 
    103  size_t GetAlignedDataLength() const {
    104    return mozilla::ipc::shared_memory::PageAlignedSize(GetDataLength());
    105  }
    106 
    107  bool EnsureMapped(size_t aLength);
    108 
    109  // Protects mapping and unmapping of mBuf.
    110  Maybe<Mutex> mHandleLock;
    111  nsExpirationState mExpirationState;
    112  int32_t mStride = 0;
    113  uint32_t mConsumers = 1;
    114  IntSize mSize;
    115  // This is only used to support EnsureMapped if we fail initially.
    116  mozilla::ipc::ReadOnlySharedMemoryHandle mBufHandle;
    117  std::shared_ptr<mozilla::ipc::MutableOrReadOnlySharedMemoryMapping> mBuf;
    118  SurfaceFormat mFormat = SurfaceFormat::UNKNOWN;
    119  base::ProcessId mCreatorPid = 0;
    120  bool mCreatorRef = true;
    121 };
    122 
    123 /**
    124 * This class is used to wrap shared (as in process) data buffers used by a
    125 * source surface.
    126 */
    127 class SourceSurfaceSharedData : public DataSourceSurface {
    128 public:
    129  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedData, override)
    130 
    131  SourceSurfaceSharedData()
    132      : mMutex("SourceSurfaceSharedData"),
    133        mStride(0),
    134        mHandleCount(0),
    135        mFormat(SurfaceFormat::UNKNOWN),
    136        mClosed(false),
    137        mFinalized(false),
    138        mShared(false) {}
    139 
    140  /**
    141   * Initialize the surface by creating a shared memory buffer with a size
    142   * determined by aSize, aStride and aFormat. If aShare is true, it will also
    143   * immediately attempt to share the surface with the GPU process via
    144   * SharedSurfacesChild.
    145   */
    146  bool Init(const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat,
    147            bool aShare = true);
    148 
    149  uint8_t* GetData() final {
    150    MutexAutoLock lock(mMutex);
    151    return GetDataInternal();
    152  }
    153 
    154  int32_t Stride() final { return mStride; }
    155 
    156  SurfaceType GetType() const override { return SurfaceType::DATA_SHARED; }
    157  IntSize GetSize() const final { return mSize; }
    158  SurfaceFormat GetFormat() const final { return mFormat; }
    159 
    160  void SizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
    161                           SizeOfInfo& aInfo) const final;
    162 
    163  bool OnHeap() const final { return false; }
    164 
    165  /**
    166   * Although Map (and Moz2D in general) isn't normally threadsafe,
    167   * we want to allow it for SourceSurfaceSharedData since it should
    168   * always be fine (for reading at least).
    169   *
    170   * This is the same as the base class implementation except using
    171   * mMapCount instead of mIsMapped since that breaks for multithread.
    172   *
    173   * Additionally if a reallocation happened while there were active
    174   * mappings, then we guarantee that GetData will continue to return
    175   * the same data pointer by retaining the old shared buffer until
    176   * the last mapping is freed via Unmap.
    177   */
    178  bool Map(MapType aMapType, MappedSurface* aMappedSurface) final {
    179    MutexAutoLock lock(mMutex);
    180    if (mFinalized && aMapType != MapType::READ) {
    181      // Once finalized the data may be write-protected
    182      return false;
    183    }
    184    ++mMapCount;
    185    aMappedSurface->mData = GetDataInternal();
    186    aMappedSurface->mStride = mStride;
    187    return true;
    188  }
    189 
    190  void Unmap() final {
    191    MutexAutoLock lock(mMutex);
    192    MOZ_ASSERT(mMapCount > 0);
    193    if (--mMapCount == 0) {
    194      mOldBuf = nullptr;
    195    }
    196  }
    197 
    198  /**
    199   * Get a handle to share to another process for this buffer. Returns:
    200   *   NS_OK -- success, aHandle is valid.
    201   *   NS_ERROR_NOT_AVAILABLE -- handle was closed, need to reallocate.
    202   *   NS_ERROR_FAILURE -- failed to create a handle to share.
    203   */
    204  nsresult CloneHandle(mozilla::ipc::ReadOnlySharedMemoryHandle& aHandle);
    205 
    206  /**
    207   * Indicates the buffer is not expected to be shared with any more processes.
    208   * May release the handle if possible (see CloseHandleInternal).
    209   */
    210  void FinishedSharing() {
    211    MutexAutoLock lock(mMutex);
    212    mShared = true;
    213    CloseHandleInternal();
    214  }
    215 
    216  /**
    217   * Indicates whether or not the buffer can be shared with another process
    218   * without reallocating. Note that this is racy and should only be used for
    219   * informational/reporting purposes.
    220   */
    221  bool CanShare() const {
    222    MutexAutoLock lock(mMutex);
    223    return !mClosed;
    224  }
    225 
    226  /**
    227   * Allocate a new shared memory buffer so that we can get a new handle for
    228   * sharing to new processes. CloneHandle must have failed with
    229   * NS_ERROR_NOT_AVAILABLE in order for this to be safe to call. Returns true
    230   * if the operation succeeds. If it fails, there is no state change.
    231   */
    232  bool ReallocHandle();
    233 
    234  /**
    235   * Signals we have finished writing to the buffer and it may be marked as
    236   * read only.
    237   */
    238  void Finalize();
    239 
    240  /**
    241   * Indicates whether or not the buffer can change. If this returns true, it is
    242   * guaranteed to continue to do so for the remainder of the surface's life.
    243   */
    244  bool IsFinalized() const {
    245    MutexAutoLock lock(mMutex);
    246    return mFinalized;
    247  }
    248 
    249  /**
    250   * Yields a dirty rect of what has changed since it was last called.
    251   */
    252  Maybe<IntRect> TakeDirtyRect() final {
    253    MutexAutoLock lock(mMutex);
    254    if (mDirtyRect) {
    255      Maybe<IntRect> ret = std::move(mDirtyRect);
    256      return ret;
    257    }
    258    return Nothing();
    259  }
    260 
    261  /**
    262   * Increment the invalidation counter.
    263   */
    264  void Invalidate(const IntRect& aDirtyRect) final {
    265    MutexAutoLock lock(mMutex);
    266    if (!aDirtyRect.IsEmpty()) {
    267      if (mDirtyRect) {
    268        mDirtyRect->UnionRect(mDirtyRect.ref(), aDirtyRect);
    269      } else {
    270        mDirtyRect = Some(aDirtyRect);
    271      }
    272    } else {
    273      mDirtyRect = Some(IntRect(IntPoint(0, 0), mSize));
    274    }
    275    MOZ_ASSERT_IF(mDirtyRect, !mDirtyRect->IsEmpty());
    276  }
    277 
    278  /**
    279   * While a HandleLock exists for the given surface, the shared memory handle
    280   * cannot be released.
    281   */
    282  class MOZ_STACK_CLASS HandleLock final {
    283   public:
    284    explicit HandleLock(SourceSurfaceSharedData* aSurface)
    285        : mSurface(aSurface) {
    286      mSurface->LockHandle();
    287    }
    288 
    289    ~HandleLock() { mSurface->UnlockHandle(); }
    290 
    291   private:
    292    RefPtr<SourceSurfaceSharedData> mSurface;
    293  };
    294 
    295 protected:
    296  virtual ~SourceSurfaceSharedData() = default;
    297 
    298 private:
    299  friend class SourceSurfaceSharedDataWrapper;
    300 
    301  void LockHandle() {
    302    MutexAutoLock lock(mMutex);
    303    ++mHandleCount;
    304  }
    305 
    306  void UnlockHandle() {
    307    MutexAutoLock lock(mMutex);
    308    MOZ_ASSERT(mHandleCount > 0);
    309    --mHandleCount;
    310    mShared = true;
    311    CloseHandleInternal();
    312  }
    313 
    314  uint8_t* GetDataInternal() const;
    315 
    316  size_t GetDataLength() const {
    317    return static_cast<size_t>(mStride) * mSize.height;
    318  }
    319 
    320  size_t GetAlignedDataLength() const {
    321    return mozilla::ipc::shared_memory::PageAlignedSize(GetDataLength());
    322  }
    323 
    324  /**
    325   * Attempt to close the handle. Only if the buffer has been both finalized
    326   * and we have completed sharing will it be released.
    327   */
    328  void CloseHandleInternal();
    329 
    330  mutable Mutex mMutex MOZ_UNANNOTATED;
    331  int32_t mStride;
    332  int32_t mHandleCount;
    333  Maybe<IntRect> mDirtyRect;
    334  IntSize mSize;
    335  mozilla::ipc::MutableSharedMemoryHandle mBufHandle;
    336  // This class always has mutable mappings, however we need to share them with
    337  // the wrapper which may be read-only, so we use MutableOrReadOnly.
    338  std::shared_ptr<mozilla::ipc::MutableOrReadOnlySharedMemoryMapping> mBuf;
    339  std::shared_ptr<mozilla::ipc::MutableOrReadOnlySharedMemoryMapping> mOldBuf;
    340  SurfaceFormat mFormat;
    341  bool mClosed : 1;
    342  bool mFinalized : 1;
    343  bool mShared : 1;
    344 };
    345 
    346 }  // namespace gfx
    347 }  // namespace mozilla
    348 
    349 #endif /* MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_ */