This adds a new type, `SharedMemoryCursor`, as well as platform support for mapping subregions of shared memory handles. This type will attempt to map the entire shared memory region, and will back off on the size of the region until it can successfully map a portion of the region to read data from. Ideally, this should help reduce the chances of encountering memory fragmentation issues when sending large JS structured clone buffers over IPC. Differential Revision: https://phabricator.services.mozilla.com/D233116
106 lines
3.4 KiB
C++
106 lines
3.4 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 "SharedMemoryCursor.h"
|
|
|
|
namespace mozilla::ipc::shared_memory {
|
|
|
|
bool Cursor::Read(void* aBuffer, size_t aCount) {
|
|
return Consume(aBuffer, aCount, /* aWriteToShmem */ false);
|
|
}
|
|
|
|
bool Cursor::Write(const void* aBuffer, size_t aCount) {
|
|
return Consume(const_cast<void*>(aBuffer), aCount, /* aWriteToShmem */ true);
|
|
}
|
|
|
|
void Cursor::Seek(uint64_t aOffset) {
|
|
MOZ_ASSERT(aOffset <= Size());
|
|
|
|
// Update our offset, and invalidate `mMapping` if our current chunk changed.
|
|
uint64_t oldChunkStart = ChunkStart();
|
|
mOffset = aOffset;
|
|
if (mMapping && oldChunkStart != ChunkStart()) {
|
|
mMapping = nullptr;
|
|
}
|
|
}
|
|
|
|
Handle Cursor::TakeHandle() {
|
|
mMapping = nullptr;
|
|
return std::move(mHandle);
|
|
}
|
|
|
|
void Cursor::SetChunkSize(size_t aChunkSize) {
|
|
MOZ_ASSERT(IsPowerOfTwo(aChunkSize),
|
|
"Cannot specify non power-of-two maximum chunk size");
|
|
MOZ_ASSERT(aChunkSize >= SystemAllocationGranularity(),
|
|
"Cannot specify a chunk size which is smaller than the system "
|
|
"allocation granularity");
|
|
mChunkSize = aChunkSize;
|
|
mMapping = nullptr; // Invalidate any existing mappings.
|
|
}
|
|
|
|
bool Cursor::Consume(void* aBuffer, size_t aCount, bool aWriteToShmem) {
|
|
if (aCount > Remaining()) {
|
|
NS_WARNING("count too large");
|
|
return false;
|
|
}
|
|
|
|
size_t consumed = 0;
|
|
while (consumed < aCount) {
|
|
// Ensure we have a valid mapping each trip through the loop. This will
|
|
// automatically back off on chunk size to avoid mapping failure.
|
|
if (!EnsureMapping()) {
|
|
return false;
|
|
}
|
|
|
|
// Determine how many of the requested bytes are available in mMapping, and
|
|
// perform the operation on them.
|
|
size_t mappingOffset = ChunkOffset();
|
|
size_t mappingRemaining = mMapping.Size() - mappingOffset;
|
|
size_t toCopy = std::min<size_t>(mappingRemaining, aCount - consumed);
|
|
|
|
void* shmemPtr = static_cast<char*>(mMapping.Data()) + mappingOffset;
|
|
void* bufferPtr = static_cast<char*>(aBuffer) + consumed;
|
|
if (aWriteToShmem) {
|
|
memcpy(shmemPtr, bufferPtr, toCopy);
|
|
} else {
|
|
memcpy(bufferPtr, shmemPtr, toCopy);
|
|
}
|
|
|
|
// Seek and advance offsets. This will invalidate our mapping if it no
|
|
// longer applies to the current chunk.
|
|
Seek(mOffset + toCopy);
|
|
consumed += toCopy;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Cursor::EnsureMapping() {
|
|
MOZ_ASSERT(mHandle.IsValid());
|
|
|
|
while (!mMapping) {
|
|
// Attempt to map at the current chunk size.
|
|
uint64_t chunkStart = ChunkStart();
|
|
size_t chunkSize = std::min<uint64_t>(ChunkSize(), Size() - chunkStart);
|
|
mMapping = mHandle.MapSubregion(chunkStart, chunkSize);
|
|
if (MOZ_UNLIKELY(!mMapping)) {
|
|
// If we failed to map a single allocation granularity, we can't go
|
|
// smaller, so give up.
|
|
if (chunkSize <= SystemAllocationGranularity()) {
|
|
NS_WARNING(
|
|
"Failed to map the smallest allocation granularity of shared "
|
|
"memory region!");
|
|
return false;
|
|
}
|
|
// Try to allocate a smaller chunk next time.
|
|
mChunkSize = RoundUpPow2(chunkSize) >> 1;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace mozilla::ipc::shared_memory
|