tor-browser

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

SourceSurfaceSharedData.cpp (8600B)


      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 #include "SourceSurfaceSharedData.h"
      8 
      9 #include "mozilla/Likely.h"
     10 #include "mozilla/StaticPrefs_image.h"
     11 #include "mozilla/ipc/SharedMemoryMapping.h"
     12 #include "mozilla/layers/SharedSurfacesChild.h"
     13 #include "mozilla/layers/SharedSurfacesParent.h"
     14 #include "nsDebug.h"  // for NS_ABORT_OOM
     15 
     16 #include "base/process_util.h"
     17 
     18 #ifdef DEBUG
     19 /**
     20 * If defined, this makes SourceSurfaceSharedData::Finalize memory protect the
     21 * underlying shared buffer in the producing process (the content or UI
     22 * process). Given flushing the page table is expensive, and its utility is
     23 * predominantly diagnostic (in case of overrun), turn it off by default.
     24 */
     25 #  define SHARED_SURFACE_PROTECT_FINALIZED
     26 #endif
     27 
     28 using namespace mozilla::layers;
     29 
     30 namespace mozilla {
     31 namespace gfx {
     32 
     33 void SourceSurfaceSharedDataWrapper::Init(
     34    const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat,
     35    ipc::ReadOnlySharedMemoryHandle aHandle, base::ProcessId aCreatorPid) {
     36  MOZ_ASSERT(!mBuf);
     37  mSize = aSize;
     38  mStride = aStride;
     39  mFormat = aFormat;
     40  mCreatorPid = aCreatorPid;
     41 
     42  size_t len = GetAlignedDataLength();
     43  mBufHandle = std::move(aHandle);
     44  if (!mBufHandle) {
     45    MOZ_CRASH("Invalid shared memory handle!");
     46  }
     47 
     48  bool mapped = EnsureMapped(len);
     49  if ((sizeof(uintptr_t) <= 4 ||
     50       StaticPrefs::image_mem_shared_unmap_force_enabled_AtStartup()) &&
     51      len / 1024 >
     52          StaticPrefs::image_mem_shared_unmap_min_threshold_kb_AtStartup()) {
     53    mHandleLock.emplace("SourceSurfaceSharedDataWrapper::mHandleLock");
     54 
     55    if (mapped) {
     56      // Tracking at the initial mapping, and not just after the first use of
     57      // the surface means we might get unmapped again before the next frame
     58      // gets rendered if a low virtual memory condition persists.
     59      SharedSurfacesParent::AddTracking(this);
     60    }
     61  } else if (!mapped) {
     62    // We don't support unmapping for this surface, and we failed to map it.
     63    NS_ABORT_OOM(len);
     64  } else {
     65    mBufHandle = nullptr;
     66  }
     67 }
     68 
     69 void SourceSurfaceSharedDataWrapper::Init(SourceSurfaceSharedData* aSurface) {
     70  MOZ_ASSERT(!mBuf);
     71  MOZ_ASSERT(aSurface);
     72  mSize = aSurface->mSize;
     73  mStride = aSurface->mStride;
     74  mFormat = aSurface->mFormat;
     75  mCreatorPid = base::GetCurrentProcId();
     76  mBuf = aSurface->mBuf;
     77 }
     78 
     79 bool SourceSurfaceSharedDataWrapper::EnsureMapped(size_t aLength) {
     80  MOZ_ASSERT(!GetData());
     81 
     82  if (mBufHandle.Size() < aLength) {
     83    return false;
     84  }
     85 
     86  auto mapping = mBufHandle.Map();
     87  while (!mapping) {
     88    nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>> expired;
     89    if (!SharedSurfacesParent::AgeOneGeneration(expired)) {
     90      return false;
     91    }
     92    MOZ_ASSERT(!expired.Contains(this));
     93    SharedSurfacesParent::ExpireMap(expired);
     94    mapping = mBufHandle.Map();
     95  }
     96 
     97  mBuf = std::make_shared<ipc::MutableOrReadOnlySharedMemoryMapping>(
     98      std::move(mapping));
     99 
    100  return true;
    101 }
    102 
    103 bool SourceSurfaceSharedDataWrapper::Map(MapType aMapType,
    104                                         MappedSurface* aMappedSurface) {
    105  uint8_t* dataPtr;
    106 
    107  if (aMapType != MapType::READ) {
    108    // The data may be write-protected
    109    return false;
    110  }
    111 
    112  if (mHandleLock) {
    113    MutexAutoLock lock(*mHandleLock);
    114    dataPtr = GetData();
    115    if (mMapCount == 0) {
    116      if (mConsumers > 0) {
    117        SharedSurfacesParent::RemoveTracking(this);
    118      }
    119      if (!dataPtr) {
    120        size_t len = GetAlignedDataLength();
    121        if (!EnsureMapped(len)) {
    122          NS_ABORT_OOM(len);
    123        }
    124        dataPtr = GetData();
    125      }
    126    }
    127    ++mMapCount;
    128  } else {
    129    dataPtr = GetData();
    130    ++mMapCount;
    131  }
    132 
    133  MOZ_ASSERT(dataPtr);
    134  aMappedSurface->mData = dataPtr;
    135  aMappedSurface->mStride = mStride;
    136  return true;
    137 }
    138 
    139 void SourceSurfaceSharedDataWrapper::Unmap() {
    140  if (mHandleLock) {
    141    MutexAutoLock lock(*mHandleLock);
    142    if (--mMapCount == 0 && mConsumers > 0) {
    143      SharedSurfacesParent::AddTracking(this);
    144    }
    145  } else {
    146    --mMapCount;
    147  }
    148  MOZ_ASSERT(mMapCount >= 0);
    149 }
    150 
    151 void SourceSurfaceSharedDataWrapper::ExpireMap() {
    152  MutexAutoLock lock(*mHandleLock);
    153  if (mMapCount == 0) {
    154    // This unmaps the stored memory mapping.
    155    *mBuf = nullptr;
    156  }
    157 }
    158 
    159 bool SourceSurfaceSharedData::Init(const IntSize& aSize, int32_t aStride,
    160                                   SurfaceFormat aFormat,
    161                                   bool aShare /* = true */) {
    162  mSize = aSize;
    163  mStride = aStride;
    164  mFormat = aFormat;
    165 
    166  size_t len = GetAlignedDataLength();
    167  mBufHandle = ipc::shared_memory::Create(len);
    168  mBuf = std::make_shared<ipc::MutableOrReadOnlySharedMemoryMapping>(
    169      mBufHandle.Map());
    170  if (NS_WARN_IF(!mBufHandle) || NS_WARN_IF(!mBuf || !*mBuf)) {
    171    return false;
    172  }
    173 
    174  if (aShare) {
    175    layers::SharedSurfacesChild::Share(this);
    176  }
    177 
    178  return true;
    179 }
    180 
    181 void SourceSurfaceSharedData::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
    182                                                  SizeOfInfo& aInfo) const {
    183  MutexAutoLock lock(mMutex);
    184  aInfo.AddType(SurfaceType::DATA_SHARED);
    185  if (mBuf) {
    186    aInfo.mNonHeapBytes = GetAlignedDataLength();
    187  }
    188  if (!mClosed) {
    189    aInfo.mExternalHandles = 1;
    190  }
    191  Maybe<wr::ExternalImageId> extId = SharedSurfacesChild::GetExternalId(this);
    192  if (extId) {
    193    aInfo.mExternalId = wr::AsUint64(extId.ref());
    194  }
    195 }
    196 
    197 uint8_t* SourceSurfaceSharedData::GetDataInternal() const {
    198  mMutex.AssertCurrentThreadOwns();
    199 
    200  // This class's mappings are always mutable, so we can safely cast away the
    201  // const in the values returned here (see the comment above the `mBuf`
    202  // declaration).
    203 
    204  // If we have an old buffer lingering, it is because we get reallocated to
    205  // get a new handle to share, but there were still active mappings.
    206  if (MOZ_UNLIKELY(mOldBuf)) {
    207    MOZ_ASSERT(mMapCount > 0);
    208    MOZ_ASSERT(mFinalized);
    209    return const_cast<uint8_t*>(mOldBuf->DataAs<uint8_t>());
    210  }
    211  // Const cast to match `GetData()`.
    212  return const_cast<uint8_t*>(mBuf->DataAs<uint8_t>());
    213 }
    214 
    215 nsresult SourceSurfaceSharedData::CloneHandle(
    216    ipc::ReadOnlySharedMemoryHandle& aHandle) {
    217  MutexAutoLock lock(mMutex);
    218  MOZ_ASSERT(mHandleCount > 0);
    219 
    220  if (mClosed) {
    221    return NS_ERROR_NOT_AVAILABLE;
    222  }
    223 
    224  aHandle = mBufHandle.Clone().ToReadOnly();
    225  if (MOZ_UNLIKELY(!aHandle)) {
    226    return NS_ERROR_FAILURE;
    227  }
    228 
    229  return NS_OK;
    230 }
    231 
    232 void SourceSurfaceSharedData::CloseHandleInternal() {
    233  mMutex.AssertCurrentThreadOwns();
    234 
    235  if (mClosed) {
    236    MOZ_ASSERT(mHandleCount == 0);
    237    MOZ_ASSERT(mShared);
    238    return;
    239  }
    240 
    241  if (mShared) {
    242    mBufHandle = nullptr;
    243    mClosed = true;
    244  }
    245 }
    246 
    247 bool SourceSurfaceSharedData::ReallocHandle() {
    248  MutexAutoLock lock(mMutex);
    249  MOZ_ASSERT(mHandleCount > 0);
    250  MOZ_ASSERT(mClosed);
    251 
    252  if (NS_WARN_IF(!mFinalized)) {
    253    // We haven't finished populating the surface data yet, which means we are
    254    // out of luck, as we have no means of synchronizing with the producer to
    255    // write new data to a new buffer. This should be fairly rare, caused by a
    256    // crash in the GPU process, while we were decoding an image.
    257    return false;
    258  }
    259 
    260  size_t len = GetAlignedDataLength();
    261  auto handle = ipc::shared_memory::Create(len);
    262  auto mapping = handle.Map();
    263  if (NS_WARN_IF(!handle) || NS_WARN_IF(!mapping)) {
    264    return false;
    265  }
    266 
    267  size_t copyLen = GetDataLength();
    268  memcpy(mapping.Address(), mBuf->Address(), copyLen);
    269 #ifdef SHARED_SURFACE_PROTECT_FINALIZED
    270  ipc::shared_memory::LocalProtect(mapping.DataAs<char>(), len,
    271                                   ipc::shared_memory::AccessRead);
    272 #endif
    273 
    274  if (mMapCount > 0 && !mOldBuf) {
    275    mOldBuf = std::move(mBuf);
    276  }
    277  mBufHandle = std::move(handle);
    278  mBuf = std::make_shared<ipc::MutableOrReadOnlySharedMemoryMapping>(
    279      std::move(mapping));
    280  mClosed = false;
    281  mShared = false;
    282  return true;
    283 }
    284 
    285 void SourceSurfaceSharedData::Finalize() {
    286  MutexAutoLock lock(mMutex);
    287  MOZ_ASSERT(!mFinalized);
    288 
    289 #ifdef SHARED_SURFACE_PROTECT_FINALIZED
    290  size_t len = GetAlignedDataLength();
    291  // This class's mappings are always mutable, so we can safely cast away the
    292  // const (see the comment above the `mBuf` declaration).
    293  ipc::shared_memory::LocalProtect(const_cast<char*>(mBuf->DataAs<char>()), len,
    294                                   ipc::shared_memory::AccessRead);
    295 #endif
    296 
    297  mFinalized = true;
    298 }
    299 
    300 }  // namespace gfx
    301 }  // namespace mozilla