Bug 1331944 - Part 1. Add SourceSurfaceSharedDataWrapper and SourceSurfaceSharedData::HandleLock. r=jrmuizel
authorAndrew Osmond <aosmond@mozilla.com>
Mon, 30 Oct 2017 13:10:28 -0400
changeset 389047 ed385db8511241a5178506ef9a015fd1df098b15
parent 389046 68e85782bbcab3c06e729551643bdc602cf8de71
child 389048 8bc9bb88a4d1b8e6bf6a8ea84a66499bb8561df3
push id96781
push useraosmond@gmail.com
push dateMon, 30 Oct 2017 17:10:40 +0000
treeherdermozilla-inbound@d903cabfc500 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1331944
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1331944 - Part 1. Add SourceSurfaceSharedDataWrapper and SourceSurfaceSharedData::HandleLock. r=jrmuizel
gfx/layers/SourceSurfaceSharedData.cpp
gfx/layers/SourceSurfaceSharedData.h
--- a/gfx/layers/SourceSurfaceSharedData.cpp
+++ b/gfx/layers/SourceSurfaceSharedData.cpp
@@ -7,16 +7,53 @@
 
 #include "mozilla/Likely.h"
 #include "mozilla/Types.h" // for decltype
 
 namespace mozilla {
 namespace gfx {
 
 bool
+SourceSurfaceSharedDataWrapper::Init(const IntSize& aSize,
+                                     int32_t aStride,
+                                     SurfaceFormat aFormat,
+                                     const SharedMemoryBasic::Handle& aHandle,
+                                     base::ProcessId aCreatorPid)
+{
+  MOZ_ASSERT(!mBuf);
+  mSize = aSize;
+  mStride = aStride;
+  mFormat = aFormat;
+  mCreatorPid = aCreatorPid;
+
+  size_t len = GetAlignedDataLength();
+  mBuf = MakeAndAddRef<SharedMemoryBasic>();
+  if (NS_WARN_IF(!mBuf->SetHandle(aHandle, ipc::SharedMemory::RightsReadOnly)) ||
+      NS_WARN_IF(!mBuf->Map(len))) {
+    mBuf = nullptr;
+    return false;
+  }
+
+  mBuf->CloseHandle();
+  return true;
+}
+
+void
+SourceSurfaceSharedDataWrapper::Init(SourceSurfaceSharedData* aSurface)
+{
+  MOZ_ASSERT(!mBuf);
+  MOZ_ASSERT(aSurface);
+  mSize = aSurface->mSize;
+  mStride = aSurface->mStride;
+  mFormat = aSurface->mFormat;
+  mCreatorPid = base::GetCurrentProcId();
+  mBuf = aSurface->mBuf;
+}
+
+bool
 SourceSurfaceSharedData::Init(const IntSize &aSize,
                               int32_t aStride,
                               SurfaceFormat aFormat)
 {
   mSize = aSize;
   mStride = aStride;
   mFormat = aFormat;
 
@@ -62,16 +99,17 @@ SourceSurfaceSharedData::GetDataInternal
   return static_cast<uint8_t*>(mBuf->memory());
 }
 
 nsresult
 SourceSurfaceSharedData::ShareToProcess(base::ProcessId aPid,
                                         SharedMemoryBasic::Handle& aHandle)
 {
   MutexAutoLock lock(mMutex);
+  MOZ_ASSERT(mHandleCount > 0);
 
   if (mClosed) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   bool shared = mBuf->ShareToProcess(aPid, &aHandle);
   if (MOZ_UNLIKELY(!shared)) {
     return NS_ERROR_FAILURE;
@@ -81,29 +119,33 @@ SourceSurfaceSharedData::ShareToProcess(
 }
 
 void
 SourceSurfaceSharedData::CloseHandleInternal()
 {
   mMutex.AssertCurrentThreadOwns();
 
   if (mClosed) {
+    MOZ_ASSERT(mHandleCount == 0);
+    MOZ_ASSERT(mFinalized);
+    MOZ_ASSERT(mShared);
     return;
   }
 
   if (mFinalized && mShared) {
     mBuf->CloseHandle();
     mClosed = true;
   }
 }
 
 bool
 SourceSurfaceSharedData::ReallocHandle()
 {
   MutexAutoLock lock(mMutex);
+  MOZ_ASSERT(mHandleCount > 0);
   MOZ_ASSERT(mClosed);
   MOZ_ASSERT(mFinalized);
 
   size_t len = GetAlignedDataLength();
   RefPtr<SharedMemoryBasic> buf = new SharedMemoryBasic();
   if (NS_WARN_IF(!buf->Create(len)) ||
       NS_WARN_IF(!buf->Map(len))) {
     return false;
--- a/gfx/layers/SourceSurfaceSharedData.h
+++ b/gfx/layers/SourceSurfaceSharedData.h
@@ -8,39 +8,138 @@
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/ipc/SharedMemoryBasic.h"
 
 namespace mozilla {
 namespace gfx {
 
+class SourceSurfaceSharedData;
+
+/**
+ * This class is used to wrap shared (as in process) data buffers allocated by
+ * a SourceSurfaceSharedData object. It may live in the same process or a
+ * different process from the actual SourceSurfaceSharedData object.
+ *
+ * If it is in the same process, mBuf is the same object as that in the surface.
+ * It is a useful abstraction over just using the surface directly, because it
+ * can have a different lifetime from the surface; if the surface gets freed,
+ * consumers may continue accessing the data in the buffer. Releasing the
+ * original surface is a signal which feeds into SharedSurfacesParent to decide
+ * to release the SourceSurfaceSharedDataWrapper.
+ *
+ * If it is in a different process, mBuf is a new SharedMemoryBasic object which
+ * mapped in the given shared memory handle as read only memory.
+ */
+class SourceSurfaceSharedDataWrapper final : public DataSourceSurface
+{
+  typedef mozilla::ipc::SharedMemoryBasic SharedMemoryBasic;
+
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedDataWrapper, override)
+
+  SourceSurfaceSharedDataWrapper()
+    : mStride(0)
+    , mFormat(SurfaceFormat::UNKNOWN)
+  { }
+
+  bool Init(const IntSize& aSize,
+            int32_t aStride,
+            SurfaceFormat aFormat,
+            const SharedMemoryBasic::Handle& aHandle,
+            base::ProcessId aCreatorPid);
+
+  void Init(SourceSurfaceSharedData *aSurface);
+
+  base::ProcessId GetCreatorPid() const
+  {
+    return mCreatorPid;
+  }
+
+  int32_t Stride() override { return mStride; }
+
+  SurfaceType GetType() const override { return SurfaceType::DATA; }
+  IntSize GetSize() const override { return mSize; }
+  SurfaceFormat GetFormat() const override { return mFormat; }
+
+  uint8_t* GetData() override
+  {
+    return static_cast<uint8_t*>(mBuf->memory());
+  }
+
+  bool OnHeap() const override
+  {
+    return false;
+  }
+
+  bool Map(MapType, MappedSurface *aMappedSurface) override
+  {
+    aMappedSurface->mData = GetData();
+    aMappedSurface->mStride = mStride;
+    return true;
+  }
+
+  void Unmap() override
+  { }
+
+  bool AddConsumer()
+  {
+    return ++mConsumers == 1;
+  }
+
+  bool RemoveConsumer()
+  {
+    MOZ_ASSERT(mConsumers > 0);
+    return --mConsumers == 0;
+  }
+
+private:
+  size_t GetDataLength() const
+  {
+    return static_cast<size_t>(mStride) * mSize.height;
+  }
+
+  size_t GetAlignedDataLength() const
+  {
+    return mozilla::ipc::SharedMemory::PageAlignedSize(GetDataLength());
+  }
+
+  int32_t mStride;
+  uint32_t mConsumers;
+  IntSize mSize;
+  RefPtr<SharedMemoryBasic> mBuf;
+  SurfaceFormat mFormat;
+  base::ProcessId mCreatorPid;
+};
+
 /**
  * This class is used to wrap shared (as in process) data buffers used by a
  * source surface.
  */
 class SourceSurfaceSharedData final : public DataSourceSurface
 {
   typedef mozilla::ipc::SharedMemoryBasic SharedMemoryBasic;
 
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedData, override)
 
   SourceSurfaceSharedData()
     : mMutex("SourceSurfaceSharedData")
     , mStride(0)
     , mMapCount(0)
+    , mHandleCount(0)
     , mFormat(SurfaceFormat::UNKNOWN)
     , mClosed(false)
     , mFinalized(false)
     , mShared(false)
   {
   }
 
-  bool Init(const IntSize &aSize,
+  bool Init(const IntSize& aSize,
             int32_t aStride,
             SurfaceFormat aFormat);
 
   uint8_t* GetData() override
   {
     MutexAutoLock lock(mMutex);
     return GetDataInternal();
   }
@@ -128,27 +227,75 @@ public:
    * Allocate a new shared memory buffer so that we can get a new handle for
    * sharing to new processes. ShareToProcess must have failed with
    * NS_ERROR_NOT_AVAILABLE in order for this to be safe to call. Returns true
    * if the operation succeeds. If it fails, there is no state change.
    */
   bool ReallocHandle();
 
   /**
-   * Indicates we have finished writing to the buffer and it may be marked as
+   * Signals we have finished writing to the buffer and it may be marked as
    * read only. May release the handle if possible (see CloseHandleInternal).
    */
   void Finalize();
 
+  /**
+   * Indicates whether or not the buffer can change. If this returns true, it is
+   * guaranteed to continue to do so for the remainder of the surface's life.
+   */
+  bool IsFinalized() const
+  {
+    MutexAutoLock lock(mMutex);
+    return mFinalized;
+  }
+
+  /**
+   * While a HandleLock exists for the given surface, the shared memory handle
+   * cannot be released.
+   */
+  class MOZ_STACK_CLASS HandleLock final {
+  public:
+    explicit HandleLock(SourceSurfaceSharedData* aSurface)
+      : mSurface(aSurface)
+    {
+      mSurface->LockHandle();
+    }
+
+    ~HandleLock()
+    {
+      mSurface->UnlockHandle();
+    }
+
+  private:
+    RefPtr<SourceSurfaceSharedData> mSurface;
+  };
+
 private:
+  friend class SourceSurfaceSharedDataWrapper;
+
   ~SourceSurfaceSharedData() override
   {
     MOZ_ASSERT(mMapCount == 0);
   }
 
+  void LockHandle()
+  {
+    MutexAutoLock lock(mMutex);
+    ++mHandleCount;
+  }
+
+  void UnlockHandle()
+  {
+    MutexAutoLock lock(mMutex);
+    MOZ_ASSERT(mHandleCount > 0);
+    --mHandleCount;
+    mShared = true;
+    CloseHandleInternal();
+  }
+
   uint8_t* GetDataInternal() const;
 
   size_t GetDataLength() const
   {
     return static_cast<size_t>(mStride) * mSize.height;
   }
 
   size_t GetAlignedDataLength() const
@@ -160,16 +307,17 @@ private:
    * Attempt to close the handle. Only if the buffer has been both finalized
    * and we have completed sharing will it be released.
    */
   void CloseHandleInternal();
 
   mutable Mutex mMutex;
   int32_t mStride;
   int32_t mMapCount;
+  int32_t mHandleCount;
   IntSize mSize;
   RefPtr<SharedMemoryBasic> mBuf;
   RefPtr<SharedMemoryBasic> mOldBuf;
   SurfaceFormat mFormat;
   bool mClosed : 1;
   bool mFinalized : 1;
   bool mShared : 1;
 };