Files
tubestation/ipc/glue/SharedMemoryBasic_mach.mm
Nika Layzell 3db1147269 Bug 1852082 - Part 1: Allow stealing ownership of the shared memory handle from SharedMemoryBasic, r=ipc-reviewers,mccr8
This is not the ideal form of this API, but avoids unnecessary handle
cloning and the corresponding fallible calls in some situations. In the
future we still want to do something more like bug 1797039, which will
provide a proper separation of handles from mappings for shared memory
regions.

Differential Revision: https://phabricator.services.mozilla.com/D187682
2023-09-15 15:26:50 +00:00

176 lines
5.1 KiB
Plaintext

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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 <map>
#include <mach/vm_map.h>
#include <mach/mach_port.h>
#if defined(XP_IOS)
# include <mach/vm_map.h>
# define mach_vm_address_t vm_address_t
# define mach_vm_map vm_map
# define mach_vm_read vm_read
# define mach_vm_region_recurse vm_region_recurse_64
# define mach_vm_size_t vm_size_t
#else
# include <mach/mach_vm.h>
#endif
#include <pthread.h>
#include <unistd.h>
#include "SharedMemoryBasic.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Printf.h"
#include "mozilla/StaticMutex.h"
#ifdef DEBUG
# define LOG_ERROR(str, args...) \
PR_BEGIN_MACRO \
mozilla::SmprintfPointer msg = mozilla::Smprintf(str, ##args); \
NS_WARNING(msg.get()); \
PR_END_MACRO
#else
# define LOG_ERROR(str, args...) \
do { /* nothing */ \
} while (0)
#endif
namespace mozilla {
namespace ipc {
SharedMemoryBasic::SharedMemoryBasic()
: mPort(MACH_PORT_NULL), mMemory(nullptr), mOpenRights(RightsReadWrite) {}
SharedMemoryBasic::~SharedMemoryBasic() {
Unmap();
CloseHandle();
}
bool SharedMemoryBasic::SetHandle(Handle aHandle, OpenRights aRights) {
MOZ_ASSERT(mPort == MACH_PORT_NULL, "already initialized");
mPort = std::move(aHandle);
mOpenRights = aRights;
return true;
}
static inline void* toPointer(mach_vm_address_t address) {
return reinterpret_cast<void*>(static_cast<uintptr_t>(address));
}
static inline mach_vm_address_t toVMAddress(void* pointer) {
return static_cast<mach_vm_address_t>(reinterpret_cast<uintptr_t>(pointer));
}
bool SharedMemoryBasic::Create(size_t size) {
MOZ_ASSERT(mPort == MACH_PORT_NULL, "already initialized");
memory_object_size_t memoryObjectSize = round_page(size);
kern_return_t kr =
mach_make_memory_entry_64(mach_task_self(), &memoryObjectSize, 0,
MAP_MEM_NAMED_CREATE | VM_PROT_DEFAULT,
getter_Transfers(mPort), MACH_PORT_NULL);
if (kr != KERN_SUCCESS || memoryObjectSize < round_page(size)) {
LOG_ERROR("Failed to make memory entry (%zu bytes). %s (%x)\n", size,
mach_error_string(kr), kr);
CloseHandle();
return false;
}
Created(size);
return true;
}
bool SharedMemoryBasic::Map(size_t size, void* fixed_address) {
MOZ_ASSERT(mMemory == nullptr);
if (MACH_PORT_NULL == mPort) {
return false;
}
kern_return_t kr;
mach_vm_address_t address = toVMAddress(fixed_address);
vm_prot_t vmProtection = VM_PROT_READ;
if (mOpenRights == RightsReadWrite) {
vmProtection |= VM_PROT_WRITE;
}
kr = mach_vm_map(mach_task_self(), &address, round_page(size), 0,
fixed_address ? VM_FLAGS_FIXED : VM_FLAGS_ANYWHERE,
mPort.get(), 0, false, vmProtection, vmProtection,
VM_INHERIT_NONE);
if (kr != KERN_SUCCESS) {
if (!fixed_address) {
LOG_ERROR(
"Failed to map shared memory (%zu bytes) into %x, port %x. %s (%x)\n",
size, mach_task_self(), mach_port_t(mPort.get()),
mach_error_string(kr), kr);
}
return false;
}
if (fixed_address && fixed_address != toPointer(address)) {
kr = vm_deallocate(mach_task_self(), address, size);
if (kr != KERN_SUCCESS) {
LOG_ERROR("Failed to unmap shared memory at unsuitable address "
"(%zu bytes) from %x, port %x. %s (%x)\n",
size, mach_task_self(), mach_port_t(mPort.get()),
mach_error_string(kr), kr);
}
return false;
}
mMemory = toPointer(address);
Mapped(size);
return true;
}
void* SharedMemoryBasic::FindFreeAddressSpace(size_t size) {
mach_vm_address_t address = 0;
size = round_page(size);
if (mach_vm_map(mach_task_self(), &address, size, 0, VM_FLAGS_ANYWHERE,
MEMORY_OBJECT_NULL, 0, false, VM_PROT_NONE, VM_PROT_NONE,
VM_INHERIT_NONE) != KERN_SUCCESS ||
vm_deallocate(mach_task_self(), address, size) != KERN_SUCCESS) {
return nullptr;
}
return toPointer(address);
}
auto SharedMemoryBasic::CloneHandle() -> Handle {
return mozilla::RetainMachSendRight(mPort.get());
}
auto SharedMemoryBasic::TakeHandle() -> Handle {
mOpenRights = RightsReadWrite;
return std::move(mPort);
}
void SharedMemoryBasic::Unmap() {
if (!mMemory) {
return;
}
vm_address_t address = toVMAddress(mMemory);
kern_return_t kr =
vm_deallocate(mach_task_self(), address, round_page(mMappedSize));
if (kr != KERN_SUCCESS) {
LOG_ERROR("Failed to deallocate shared memory. %s (%x)\n",
mach_error_string(kr), kr);
return;
}
mMemory = nullptr;
}
bool SharedMemoryBasic::IsHandleValid(const Handle& aHandle) const {
return aHandle != nullptr;
}
} // namespace ipc
} // namespace mozilla