gecko-dev/ipc/glue/BigBuffer.cpp
Kelsey Gilbert 7671a9d5a4 Bug 1801021 - Use BigBuffer for DispatchCommands. r=gfx-reviewers,lsalzman
Using ipc::Shmem causes unbounded shmem use growth until e.g. a Worker
yields to the event loop. If a Worker never yields, Shmems sent to WebGLParent
are never released.

Specifically the manager (PCanvasManager) for WebGLParent calls
DestroySharedMemory, which sends/enqueues for WebGLChild's manager a
matching call to ShmemDestroyed. However, while WebGLChild refuses to spin its
event loop (such as a no-return WASM Worker), the ShmemDestroyed events
will just pile up. Closing e.g. the tab frees the shmems, but they accumulate
unbounded until the Worker yields to the event loop.

This is true for other users of ipc::Shmem (or RaiiShmem) as well, but
entrypoints other than DispatchCommands are rarer and can be handled
later similarly.

Differential Revision: https://phabricator.services.mozilla.com/D162946
2022-11-25 22:20:38 +00:00

106 lines
3.5 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ipc/BigBuffer.h"
#include "mozilla/ipc/SharedMemoryBasic.h"
#include "nsDebug.h"
namespace mozilla::ipc {
BigBuffer::BigBuffer(Adopt, SharedMemory* aSharedMemory, size_t aSize)
: mSize(aSize), mData(AsVariant(RefPtr{aSharedMemory})) {
MOZ_RELEASE_ASSERT(aSharedMemory && aSharedMemory->memory(),
"shared memory must be non-null and mapped");
MOZ_RELEASE_ASSERT(mSize <= aSharedMemory->Size(),
"shared memory region isn't large enough");
}
BigBuffer::BigBuffer(Adopt, uint8_t* aData, size_t aSize)
: mSize(aSize), mData(AsVariant(UniqueFreePtr<uint8_t[]>{aData})) {}
uint8_t* BigBuffer::Data() {
return mData.is<0>() ? mData.as<0>().get()
: reinterpret_cast<uint8_t*>(mData.as<1>()->memory());
}
const uint8_t* BigBuffer::Data() const {
return mData.is<0>()
? mData.as<0>().get()
: reinterpret_cast<const uint8_t*>(mData.as<1>()->memory());
}
auto BigBuffer::TryAllocBuffer(size_t aSize) -> Maybe<Storage> {
if (aSize <= kShmemThreshold) {
auto mem = UniqueFreePtr<uint8_t[]>{
reinterpret_cast<uint8_t*>(malloc(aSize))}; // Fallible!
if (!mem) return {};
return Some(AsVariant(std::move(mem)));
}
RefPtr<SharedMemory> shmem = new SharedMemoryBasic();
size_t capacity = SharedMemory::PageAlignedSize(aSize);
if (!shmem->Create(capacity) || !shmem->Map(capacity)) {
return {};
}
return Some(AsVariant(shmem));
}
} // namespace mozilla::ipc
void IPC::ParamTraits<mozilla::ipc::BigBuffer>::Write(MessageWriter* aWriter,
paramType&& aParam) {
using namespace mozilla::ipc;
size_t size = std::exchange(aParam.mSize, 0);
auto data = std::exchange(aParam.mData, BigBuffer::NoData());
WriteParam(aWriter, size);
bool isShmem = data.is<1>();
WriteParam(aWriter, isShmem);
if (isShmem) {
if (!data.as<1>()->WriteHandle(aWriter)) {
aWriter->FatalError("Failed to write data shmem");
}
} else {
aWriter->WriteBytes(data.as<0>().get(), size);
}
}
bool IPC::ParamTraits<mozilla::ipc::BigBuffer>::Read(MessageReader* aReader,
paramType* aResult) {
using namespace mozilla::ipc;
size_t size = 0;
bool isShmem = false;
if (!ReadParam(aReader, &size) || !ReadParam(aReader, &isShmem)) {
aReader->FatalError("Failed to read data size and format");
return false;
}
if (isShmem) {
RefPtr<SharedMemory> shmem = new SharedMemoryBasic();
size_t capacity = SharedMemory::PageAlignedSize(size);
if (!shmem->ReadHandle(aReader) || !shmem->Map(capacity)) {
aReader->FatalError("Failed to read data shmem");
return false;
}
*aResult = BigBuffer(BigBuffer::Adopt{}, shmem, size);
return true;
}
mozilla::UniqueFreePtr<uint8_t[]> buf{
reinterpret_cast<uint8_t*>(malloc(size))};
if (!buf) {
aReader->FatalError("Failed to allocate data buffer");
return false;
}
if (!aReader->ReadBytesInto(buf.get(), size)) {
aReader->FatalError("Failed to read data");
return false;
}
*aResult = BigBuffer(BigBuffer::Adopt{}, buf.release(), size);
return true;
}