Skip to content

Commit

Permalink
mac: Implement Mach-primitive backed shared memory.
Browse files Browse the repository at this point in the history
No code outside of tests instantiates Mach-primitive backed shared memory, so
this should have no functional effect on official builds of Chrome.

BUG=466437

Review URL: https://codereview.chromium.org/1362193003

Cr-Commit-Position: refs/heads/master@{#352177}
  • Loading branch information
erikchen authored and Commit bot committed Oct 2, 2015
1 parent d53c29e commit 0d77970
Show file tree
Hide file tree
Showing 10 changed files with 626 additions and 111 deletions.
1 change: 1 addition & 0 deletions base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,7 @@ test("base_unittests") {
"memory/scoped_ptr_unittest.cc",
"memory/scoped_ptr_unittest.nc",
"memory/scoped_vector_unittest.cc",
"memory/shared_memory_mac_unittest.cc",
"memory/shared_memory_unittest.cc",
"memory/singleton_unittest.cc",
"memory/weak_ptr_unittest.cc",
Expand Down
1 change: 1 addition & 0 deletions base/base.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@
'memory/scoped_ptr_unittest.nc',
'memory/scoped_vector_unittest.cc',
'memory/shared_memory_unittest.cc',
'memory/shared_memory_mac_unittest.cc',
'memory/singleton_unittest.cc',
'memory/weak_ptr_unittest.cc',
'memory/weak_ptr_unittest.nc',
Expand Down
11 changes: 11 additions & 0 deletions base/memory/shared_memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,9 @@ class BASE_EXPORT SharedMemory {
private:
#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID)
bool PrepareMapFile(ScopedFILE fp, ScopedFD readonly);
#if !(defined(OS_MACOSX) && !defined(OS_IOS))
bool FilePathForMemoryName(const std::string& mem_name, FilePath* path);
#endif
#endif // defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID)
enum ShareMode {
SHARE_READONLY,
Expand All @@ -279,6 +281,15 @@ class BASE_EXPORT SharedMemory {
#if defined(OS_WIN)
std::wstring name_;
HANDLE mapped_file_;
#elif defined(OS_MACOSX) && !defined(OS_IOS)
// The OS primitive that backs the shared memory region.
SharedMemoryHandle shm_;

// The mechanism by which the memory is mapped. Only valid if |memory_| is not
// |nullptr|.
SharedMemoryHandle::Type mapped_memory_mechanism_;

int readonly_mapped_file_;
#elif defined(OS_POSIX)
int mapped_file_;
int readonly_mapped_file_;
Expand Down
59 changes: 54 additions & 5 deletions base/memory/shared_memory_handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
#include <windows.h>
#include "base/process/process_handle.h"
#elif defined(OS_MACOSX) && !defined(OS_IOS)
#include <mach/mach.h>
#include <sys/types.h>
#include "base/base_export.h"
#include "base/file_descriptor_posix.h"
#include "base/macros.h"
#include "base/process/process_handle.h"
#elif defined(OS_POSIX)
#include <sys/types.h>
#include "base/file_descriptor_posix.h"
Expand Down Expand Up @@ -74,10 +76,9 @@ class BASE_EXPORT SharedMemoryHandle {
class BASE_EXPORT SharedMemoryHandle {
public:
enum Type {
// Indicates that the SharedMemoryHandle is backed by a POSIX fd.
// The SharedMemoryHandle is backed by a POSIX fd.
POSIX,
// Indicates that the SharedMemoryHandle is backed by the Mach primitive
// "memory object".
// The SharedMemoryHandle is backed by the Mach primitive "memory object".
MACH,
};

Expand All @@ -97,6 +98,16 @@ class BASE_EXPORT SharedMemoryHandle {
explicit SharedMemoryHandle(const base::FileDescriptor& file_descriptor);
SharedMemoryHandle(int fd, bool auto_close);

// Makes a Mach-based SharedMemoryHandle of the given size. On error,
// subsequent calls to IsValid() return false.
explicit SharedMemoryHandle(mach_vm_size_t size);

// Makes a Mach-based SharedMemoryHandle from |memory_object|, a named entry
// in the task with process id |pid|. The memory region has size |size|.
SharedMemoryHandle(mach_port_t memory_object,
mach_vm_size_t size,
base::ProcessId pid);

// Standard copy constructor. The new instance shares the underlying OS
// primitives.
SharedMemoryHandle(const SharedMemoryHandle& handle);
Expand All @@ -115,7 +126,8 @@ class BASE_EXPORT SharedMemoryHandle {
// Returns the type.
Type GetType() const;

// Whether the underlying OS primitive is valid.
// Whether the underlying OS primitive is valid. Once the SharedMemoryHandle
// is backed by a valid OS primitive, it becomes immutable.
bool IsValid() const;

// Sets the POSIX fd backing the SharedMemoryHandle. Requires that the
Expand All @@ -127,9 +139,46 @@ class BASE_EXPORT SharedMemoryHandle {
// uses of this method.
const FileDescriptor GetFileDescriptor() const;

// Exposed so that the SharedMemoryHandle can be transported between
// processes.
mach_port_t GetMemoryObject() const;

// Returns false on a failure to determine the size. On success, populates the
// output variable |size|.
bool GetSize(size_t* size) const;

// The SharedMemoryHandle must be valid.
// Returns whether the SharedMemoryHandle was successfully mapped into memory.
// On success, |memory| is an output variable that contains the start of the
// mapped memory.
bool MapAt(off_t offset, size_t bytes, void** memory, bool read_only);

// Closes the underlying OS primitive.
void Close() const;

private:
// Shared code between copy constructor and operator=.
void CopyRelevantData(const SharedMemoryHandle& handle);

Type type_;
FileDescriptor file_descriptor_;

// Each instance of a SharedMemoryHandle is backed either by a POSIX fd or a
// mach port. |type_| determines the backing member.
union {
FileDescriptor file_descriptor_;

struct {
mach_port_t memory_object_;

// The size of the shared memory region when |type_| is MACH. Only
// relevant if |memory_object_| is not |MACH_PORT_NULL|.
mach_vm_size_t size_;

// The pid of the process in which |memory_object_| is usable. Only
// relevant if |memory_object_| is not |MACH_PORT_NULL|.
base::ProcessId pid_;
};
};
};
#endif

Expand Down
167 changes: 150 additions & 17 deletions base/memory/shared_memory_handle_mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include "base/memory/shared_memory_handle.h"

#include <mach/mach_vm.h>
#include <sys/mman.h>
#include <unistd.h>

#include "base/posix/eintr_wrapper.h"
Expand All @@ -15,20 +17,43 @@ static_assert(sizeof(SharedMemoryHandle::Type) <=
"Size of enum SharedMemoryHandle::Type exceeds size of type "
"transmitted over wire.");

SharedMemoryHandle::SharedMemoryHandle() : type_(POSIX), file_descriptor_() {
}
SharedMemoryHandle::SharedMemoryHandle() : type_(POSIX), file_descriptor_() {}

SharedMemoryHandle::SharedMemoryHandle(
const base::FileDescriptor& file_descriptor)
: type_(POSIX), file_descriptor_(file_descriptor) {
}
: type_(POSIX), file_descriptor_(file_descriptor) {}

SharedMemoryHandle::SharedMemoryHandle(int fd, bool auto_close)
: type_(POSIX), file_descriptor_(fd, auto_close) {
: type_(POSIX), file_descriptor_(fd, auto_close) {}

SharedMemoryHandle::SharedMemoryHandle(mach_vm_size_t size) {
type_ = MACH;
mach_port_t named_right;
kern_return_t kr = mach_make_memory_entry_64(
mach_task_self(),
&size,
0, // Address.
MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE,
&named_right,
MACH_PORT_NULL); // Parent handle.
if (kr != KERN_SUCCESS) {
memory_object_ = MACH_PORT_NULL;
return;
}

memory_object_ = named_right;
size_ = size;
pid_ = GetCurrentProcId();
}

SharedMemoryHandle::SharedMemoryHandle(mach_port_t memory_object,
mach_vm_size_t size,
base::ProcessId pid)
: type_(MACH), memory_object_(memory_object), size_(size), pid_(pid) {}

SharedMemoryHandle::SharedMemoryHandle(const SharedMemoryHandle& handle)
: type_(handle.type_), file_descriptor_(handle.file_descriptor_) {
: type_(handle.type_) {
CopyRelevantData(handle);
}

SharedMemoryHandle& SharedMemoryHandle::operator=(
Expand All @@ -37,16 +62,48 @@ SharedMemoryHandle& SharedMemoryHandle::operator=(
return *this;

type_ = handle.type_;
file_descriptor_ = handle.file_descriptor_;
CopyRelevantData(handle);
return *this;
}

SharedMemoryHandle SharedMemoryHandle::Duplicate() const {
switch (type_) {
case POSIX: {
if (!IsValid())
return SharedMemoryHandle();

int duped_fd = HANDLE_EINTR(dup(file_descriptor_.fd));
if (duped_fd < 0)
return SharedMemoryHandle();
return SharedMemoryHandle(duped_fd, true);
}
case MACH: {
if (!IsValid())
return SharedMemoryHandle(MACH_PORT_NULL, 0, 0);

// Increment the ref count.
kern_return_t kr = mach_port_mod_refs(mach_task_self(), memory_object_,
MACH_PORT_RIGHT_SEND, 1);
DCHECK_EQ(kr, KERN_SUCCESS);
return SharedMemoryHandle(*this);
}
}
}

bool SharedMemoryHandle::operator==(const SharedMemoryHandle& handle) const {
// Invalid handles are always equal, even if they have different types.
if (!IsValid() && !handle.IsValid())
return true;

return type_ == handle.type_ && file_descriptor_ == handle.file_descriptor_;
if (type_ != handle.type_)
return false;

switch (type_) {
case POSIX:
return file_descriptor_ == handle.file_descriptor_;
case MACH:
return memory_object_ == handle.memory_object_ && size_ == handle.size_ &&
pid_ == handle.pid_;
}
}

bool SharedMemoryHandle::operator!=(const SharedMemoryHandle& handle) const {
Expand All @@ -62,27 +119,103 @@ bool SharedMemoryHandle::IsValid() const {
case POSIX:
return file_descriptor_.fd >= 0;
case MACH:
return false;
return memory_object_ != MACH_PORT_NULL;
}
}

void SharedMemoryHandle::SetFileHandle(int fd, bool auto_close) {
DCHECK_EQ(type_, POSIX);
DCHECK(!IsValid());
file_descriptor_.fd = fd;
file_descriptor_.auto_close = auto_close;
type_ = POSIX;
}

const FileDescriptor SharedMemoryHandle::GetFileDescriptor() const {
DCHECK_EQ(type_, POSIX);
return file_descriptor_;
}

SharedMemoryHandle SharedMemoryHandle::Duplicate() const {
DCHECK_EQ(type_, POSIX);
int duped_handle = HANDLE_EINTR(dup(file_descriptor_.fd));
if (duped_handle < 0)
return SharedMemoryHandle();
return SharedMemoryHandle(duped_handle, true);
mach_port_t SharedMemoryHandle::GetMemoryObject() const {
DCHECK_EQ(type_, MACH);
return memory_object_;
}

bool SharedMemoryHandle::GetSize(size_t* size) const {
if (!IsValid())
return false;

switch (type_) {
case SharedMemoryHandle::POSIX:
struct stat st;
if (fstat(file_descriptor_.fd, &st) != 0)
return false;
if (st.st_size < 0)
return false;
*size = st.st_size;
return true;
case SharedMemoryHandle::MACH:
*size = size_;
return true;
}
}

bool SharedMemoryHandle::MapAt(off_t offset,
size_t bytes,
void** memory,
bool read_only) {
DCHECK(IsValid());
switch (type_) {
case SharedMemoryHandle::POSIX:
*memory = mmap(nullptr, bytes, PROT_READ | (read_only ? 0 : PROT_WRITE),
MAP_SHARED, file_descriptor_.fd, offset);

return *memory && *memory != reinterpret_cast<void*>(-1);
case SharedMemoryHandle::MACH:
DCHECK_EQ(pid_, GetCurrentProcId());
kern_return_t kr = mach_vm_map(
mach_task_self(),
reinterpret_cast<mach_vm_address_t*>(memory), // Output parameter
bytes,
0, // Alignment mask
VM_FLAGS_ANYWHERE,
memory_object_,
offset,
FALSE, // Copy
VM_PROT_READ | (read_only ? 0 : VM_PROT_WRITE), // Current protection
VM_PROT_READ | VM_PROT_WRITE, // Maximum protection
VM_INHERIT_NONE);
return kr == KERN_SUCCESS;
}
}

void SharedMemoryHandle::Close() const {
if (!IsValid())
return;

switch (type_) {
case POSIX:
if (IGNORE_EINTR(close(file_descriptor_.fd)) < 0)
DPLOG(ERROR) << "Error closing fd.";
break;
case MACH:
kern_return_t kr = mach_port_deallocate(mach_task_self(), memory_object_);
if (kr != KERN_SUCCESS)
DPLOG(ERROR) << "Error deallocating mach port: " << kr;
break;
}
}

void SharedMemoryHandle::CopyRelevantData(const SharedMemoryHandle& handle) {
switch (type_) {
case POSIX:
file_descriptor_ = handle.file_descriptor_;
break;
case MACH:
memory_object_ = handle.memory_object_;
size_ = handle.size_;
pid_ = handle.pid_;
break;
}
}

} // namespace base
Loading

0 comments on commit 0d77970

Please sign in to comment.