fune/js/xpconnect/loader/AutoMemMap.cpp
Kris Maglione 2bbae5374b Bug 1470365: Part 1 - Add a compact, read-only, shared-memory string map class. r=erahm
This class implements a shared memory key-value store that fits into a single
memory mapped segment. All of the runtime data for its instances are stored in
the shared memory region, which means that memory overhead for each instance
in each process is only a few bytes.

Importantly, the key and value strings returned by this class are also
pointers into the shared memory region, which means that once an instance is
created, its memory cannot be unmapped until process shutdown.

For the uses I intend to put it to, this is a reasonable constraint. If we
need to use it for shorter-lived maps in the future, we can add an option to
return non-literal dependent strings that will be copied if they need to be
kept alive long term.

MozReview-Commit-ID: 5BwAaDsb7HS

--HG--
extra : rebase_source : b472fe628018f88a2c4d6b3de4b7143aeca55e14
extra : absorb_source : 5cdeb568cfd2b4a5a767191402e699e61e653b3b
2018-06-29 22:50:41 -07:00

164 lines
3.6 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 "AutoMemMap.h"
#include "ScriptPreloader-inl.h"
#include "mozilla/Unused.h"
#include "nsIFile.h"
#include <private/pprio.h>
namespace mozilla {
namespace loader {
using namespace mozilla::ipc;
AutoMemMap::~AutoMemMap()
{
reset();
}
FileDescriptor
AutoMemMap::cloneFileDescriptor() const
{
if (fd.get()) {
auto handle = FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd.get()));
return FileDescriptor(handle);
}
return FileDescriptor();
}
Result<Ok, nsresult>
AutoMemMap::init(nsIFile* file, int flags, int mode, PRFileMapProtect prot)
{
MOZ_ASSERT(!fd);
MOZ_TRY(file->OpenNSPRFileDesc(flags, mode, &fd.rwget()));
return initInternal(prot);
}
Result<Ok, nsresult>
AutoMemMap::init(const FileDescriptor& file, PRFileMapProtect prot,
size_t expectedSize)
{
MOZ_ASSERT(!fd);
if (!file.IsValid()) {
return Err(NS_ERROR_INVALID_ARG);
}
auto handle = file.ClonePlatformHandle();
fd = PR_ImportFile(PROsfd(handle.get()));
if (!fd) {
return Err(NS_ERROR_FAILURE);
}
Unused << handle.release();
return initInternal(prot, expectedSize);
}
Result<Ok, nsresult>
AutoMemMap::initInternal(PRFileMapProtect prot, size_t expectedSize)
{
MOZ_ASSERT(!fileMap);
MOZ_ASSERT(!addr);
PRFileInfo64 fileInfo;
MOZ_TRY(PR_GetOpenFileInfo64(fd.get(), &fileInfo));
if (fileInfo.size > UINT32_MAX)
return Err(NS_ERROR_INVALID_ARG);
fileMap = PR_CreateFileMap(fd, 0, prot);
if (!fileMap)
return Err(NS_ERROR_FAILURE);
size_ = fileInfo.size;
// The memory region size passed in certain IPC messages isn't necessary on
// Unix-like systems, since we can always stat the file descriptor to
// determine it accurately. But since we have it, anyway, sanity check that
// it matches the size returned by the stat.
MOZ_ASSERT_IF(expectedSize > 0, size_ == expectedSize);
addr = PR_MemMap(fileMap, 0, size_);
if (!addr)
return Err(NS_ERROR_FAILURE);
return Ok();
}
#ifdef XP_WIN
Result<Ok, nsresult>
AutoMemMap::initWithHandle(const FileDescriptor& file, size_t size, PRFileMapProtect prot)
{
MOZ_ASSERT(!fd);
MOZ_ASSERT(!handle_);
if (!file.IsValid()) {
return Err(NS_ERROR_INVALID_ARG);
}
handle_ = file.ClonePlatformHandle().release();
MOZ_ASSERT(!addr);
size_ = size;
addr = MapViewOfFile(
handle_,
prot == PR_PROT_READONLY ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS,
0, 0, size);
return Ok();
}
FileDescriptor
AutoMemMap::cloneHandle() const
{
return FileDescriptor(handle_);
}
#else
Result<Ok, nsresult>
AutoMemMap::initWithHandle(const FileDescriptor& file, size_t size, PRFileMapProtect prot)
{
return init(file, prot);
}
FileDescriptor
AutoMemMap::cloneHandle() const
{
return cloneFileDescriptor();
}
#endif
void
AutoMemMap::reset()
{
if (fileMap) {
if (addr && !persistent_) {
Unused << NS_WARN_IF(PR_MemUnmap(addr, size()) != PR_SUCCESS);
addr = nullptr;
}
Unused << NS_WARN_IF(PR_CloseFileMap(fileMap) != PR_SUCCESS);
fileMap = nullptr;
}
#ifdef XP_WIN
if (handle_) {
CloseHandle(handle_);
handle_ = nullptr;
}
#endif
fd.dispose();
}
} // namespace loader
} // namespace mozilla