summaryrefslogtreecommitdiffstats
path: root/mojo/system
diff options
context:
space:
mode:
authorviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-21 20:45:27 +0000
committerviettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-21 20:45:27 +0000
commitac1df702a252fb22e4a8ba68cc80d0c67b08d6de (patch)
tree41d9ed4db4cb2c101a67aaa1778754f1ad855e49 /mojo/system
parent976966c809acb6f46aed9468aeaf0f86df1f470f (diff)
downloadchromium_src-ac1df702a252fb22e4a8ba68cc80d0c67b08d6de.zip
chromium_src-ac1df702a252fb22e4a8ba68cc80d0c67b08d6de.tar.gz
chromium_src-ac1df702a252fb22e4a8ba68cc80d0c67b08d6de.tar.bz2
Mojo: Add a simple platform-level shared memory implementation.
(Unfortunately, base::SharedMemory doesn't quite meet our needs right now, and it'll be a while -- if ever -- before I manage to refactor it so that it does.) Not yet implemented on Windows. (This should be simple, but I'll do it separately.) R=darin@chromium.org Review URL: https://codereview.chromium.org/205583008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@258667 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo/system')
-rw-r--r--mojo/system/raw_shared_buffer.cc41
-rw-r--r--mojo/system/raw_shared_buffer.h93
-rw-r--r--mojo/system/raw_shared_buffer_posix.cc118
-rw-r--r--mojo/system/raw_shared_buffer_unittest.cc120
-rw-r--r--mojo/system/raw_shared_buffer_win.cc35
5 files changed, 407 insertions, 0 deletions
diff --git a/mojo/system/raw_shared_buffer.cc b/mojo/system/raw_shared_buffer.cc
new file mode 100644
index 0000000..669879e
--- /dev/null
+++ b/mojo/system/raw_shared_buffer.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/system/raw_shared_buffer.h"
+
+namespace mojo {
+namespace system {
+
+RawSharedBuffer::~RawSharedBuffer() {
+}
+
+// static
+scoped_ptr<RawSharedBuffer> RawSharedBuffer::Create(size_t num_bytes) {
+ if (!num_bytes)
+ return scoped_ptr<RawSharedBuffer>();
+
+ scoped_ptr<RawSharedBuffer> rv(new RawSharedBuffer(num_bytes));
+ if (!rv->Init())
+ return scoped_ptr<RawSharedBuffer>();
+ return rv.Pass();
+}
+
+scoped_ptr<RawSharedBuffer::Mapping> RawSharedBuffer::Map(size_t offset,
+ size_t length) {
+ if (offset > num_bytes_ || length == 0)
+ return scoped_ptr<Mapping>();
+
+ // Note: This is an overflow-safe check of |offset + length > num_bytes_|
+ // (that |num_bytes >= offset| is verified above).
+ if (length > num_bytes_ - offset)
+ return scoped_ptr<Mapping>();
+
+ return MapImpl(offset, length);
+}
+
+RawSharedBuffer::RawSharedBuffer(size_t num_bytes) : num_bytes_(num_bytes) {
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/mojo/system/raw_shared_buffer.h b/mojo/system/raw_shared_buffer.h
new file mode 100644
index 0000000..562abbe
--- /dev/null
+++ b/mojo/system/raw_shared_buffer.h
@@ -0,0 +1,93 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SYSTEM_RAW_SHARED_BUFFER_H_
+#define MOJO_SYSTEM_RAW_SHARED_BUFFER_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// |RawSharedBuffer| is a wrapper around OS-specific shared memory. It has the
+// following features:
+// - A |RawSharedBuffer| simply represents a piece of shared memory that *may*
+// be mapped and *may* be shared to another process.
+// - A single |RawSharedBuffer| may be mapped multiple times. The lifetime of
+// the mapping (owned by |RawSharedBuffer::Mapping|) is separate from the
+// lifetime of the |RawSharedBuffer|.
+// - Sizes/offsets (of the shared memory and mappings) are arbitrary, and not
+// restricted by page size. However, more memory may actually be mapped than
+// requested.
+//
+// It currently does NOT support the following:
+// - Sharing read-only. (This will probably eventually be supported.)
+//
+// A |RawSharedBuffer| is not thread-safe (but is thread-friendly).
+//
+// TODO(vtl): Rectify this with |base::SharedMemory|.
+class MOJO_SYSTEM_IMPL_EXPORT RawSharedBuffer {
+ public:
+ // A mapping of a |RawSharedBuffer| (compararable to a "file view" in
+ // Windows); see above. Created by |RawSharedBuffer::Map()|. Automatically
+ // unmaps memory on destruction.
+ class MOJO_SYSTEM_IMPL_EXPORT Mapping {
+ public:
+ ~Mapping() { Unmap(); }
+
+ void* base() const { return base_; }
+ size_t length() const { return length_; }
+
+ private:
+ friend class RawSharedBuffer;
+
+ Mapping(void* base, size_t length, void* real_base, size_t real_length)
+ : base_(base), length_(length),
+ real_base_(real_base), real_length_(real_length) {}
+ void Unmap();
+
+ void* const base_;
+ const size_t length_;
+
+ void* const real_base_;
+ const size_t real_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(Mapping);
+ };
+
+ ~RawSharedBuffer();
+
+ // Creates a shared buffer of size |num_bytes| bytes (initially zero-filled).
+ // Returns null on failure.
+ static scoped_ptr<RawSharedBuffer> Create(size_t num_bytes);
+
+ // Maps (some) of the shared buffer into memory; [|offset|, |offset + length|]
+ // must be contained in [0, |num_bytes|], and |length| must be at least 1.
+ // Returns null on failure.
+ scoped_ptr<Mapping> Map(size_t offset, size_t length);
+
+ size_t num_bytes() const { return num_bytes_; }
+
+ private:
+ explicit RawSharedBuffer(size_t num_bytes);
+ bool Init();
+ // The platform-dependent part of |Map()|; doesn't check arguments.
+ // TODO(vtl)
+ scoped_ptr<Mapping> MapImpl(size_t offset, size_t length);
+
+ size_t num_bytes_;
+ embedder::ScopedPlatformHandle handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawSharedBuffer);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_RAW_SHARED_BUFFER_H_
diff --git a/mojo/system/raw_shared_buffer_posix.cc b/mojo/system/raw_shared_buffer_posix.cc
new file mode 100644
index 0000000..3fc6191
--- /dev/null
+++ b/mojo/system/raw_shared_buffer_posix.cc
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/system/raw_shared_buffer.h"
+
+#include <stdint.h>
+#include <stdio.h> // For |fileno()|.
+#include <sys/mman.h> // For |mmap()|/|munmap()|.
+#include <sys/types.h> // For |off_t|.
+#include <unistd.h>
+
+#include <limits>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_restrictions.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+
+// We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a
+// |uint64_t|.
+COMPILE_ASSERT(sizeof(size_t) <= sizeof(uint64_t), size_t_too_big);
+COMPILE_ASSERT(sizeof(off_t) <= sizeof(uint64_t), off_t_too_big);
+
+namespace mojo {
+namespace system {
+
+// RawSharedBuffer::Mapping ----------------------------------------------------
+
+void RawSharedBuffer::Mapping::Unmap() {
+ int result = munmap(real_base_, real_length_);
+ PLOG_IF(ERROR, result != 0) << "munmap";
+}
+
+// RawSharedBuffer -------------------------------------------------------------
+
+bool RawSharedBuffer::Init() {
+ DCHECK(!handle_.is_valid());
+
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ if (static_cast<uint64_t>(num_bytes_) >
+ static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
+ return false;
+ }
+
+ // TODO(vtl): This is stupid. The implementation of
+ // |CreateAndOpenTemporaryFileInDir()| starts with an FD, |fdopen()|s to get a
+ // |FILE*|, and then we have to |dup(fileno(fp))| to get back to an FD that we
+ // can own. (base/memory/shared_memory_posix.cc does this too, with more
+ // |fstat()|s thrown in for good measure.)
+ base::FilePath shared_buffer_dir;
+ if (!base::GetShmemTempDir(false, &shared_buffer_dir)) {
+ LOG(ERROR) << "Failed to get temporary directory for shared memory";
+ return false;
+ }
+ base::FilePath shared_buffer_file;
+ base::ScopedFILE fp(base::CreateAndOpenTemporaryFileInDir(
+ shared_buffer_dir, &shared_buffer_file));
+ if (!fp) {
+ LOG(ERROR) << "Failed to create/open temporary file for shared memory";
+ return false;
+ }
+ // Note: |unlink()| is not interruptible.
+ if (unlink(shared_buffer_file.value().c_str()) != 0) {
+ PLOG(WARNING) << "unlink";
+ // This isn't "fatal" (e.g., someone else may have unlinked the file first),
+ // so we may as well continue.
+ }
+
+ // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are).
+ base::ScopedFD fd(dup(fileno(fp.get())));
+ if (!fd.is_valid()) {
+ PLOG(ERROR) << "dup";
+ return false;
+ }
+
+ if (HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(num_bytes_))) != 0) {
+ PLOG(ERROR) << "ftruncate";
+ return false;
+ }
+
+ handle_.reset(embedder::PlatformHandle(fd.release()));
+ return true;
+}
+
+scoped_ptr<RawSharedBuffer::Mapping> RawSharedBuffer::MapImpl(size_t offset,
+ size_t length) {
+ size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity();
+ size_t real_offset = offset - offset_rounding;
+ size_t real_length = length + offset_rounding;
+
+ // This should hold (since we checked |num_bytes| versus the maximum value of
+ // |off_t| on creation, but it never hurts to be paranoid.
+ DCHECK_LE(static_cast<uint64_t>(real_offset),
+ static_cast<uint64_t>(std::numeric_limits<off_t>::max()));
+
+ void* real_base = mmap(NULL, real_length, PROT_READ | PROT_WRITE, MAP_SHARED,
+ handle_.get().fd, static_cast<off_t>(real_offset));
+ // |mmap()| should return |MAP_FAILED| (a.k.a. -1) on error. But it shouldn't
+ // return null either.
+ if (real_base == MAP_FAILED || !real_base) {
+ PLOG(ERROR) << "mmap";
+ return scoped_ptr<Mapping>();
+ }
+
+ void* base = static_cast<char*>(real_base) + offset_rounding;
+ return make_scoped_ptr(new Mapping(base, length, real_base, real_length));
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/mojo/system/raw_shared_buffer_unittest.cc b/mojo/system/raw_shared_buffer_unittest.cc
new file mode 100644
index 0000000..06c6c81
--- /dev/null
+++ b/mojo/system/raw_shared_buffer_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/system/raw_shared_buffer.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h" // TODO(vtl): Remove once enabled on Windows.
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+// TODO(vtl): Implement and enable on Windows.
+#if !defined(OS_WIN)
+TEST(RawSharedBufferTest, Basic) {
+ const size_t kNumInts = 100;
+ const size_t kNumBytes = kNumInts * sizeof(int);
+ // A fudge so that we're not just writing zero bytes 75% of the time.
+ const int kFudge = 1234567890;
+
+ // Make some memory.
+ scoped_ptr<RawSharedBuffer> buffer(RawSharedBuffer::Create(kNumBytes));
+ ASSERT_TRUE(buffer);
+
+ // Map it all, scribble some stuff, and then unmap it.
+ {
+ scoped_ptr<RawSharedBuffer::Mapping> mapping(buffer->Map(0, kNumBytes));
+ ASSERT_TRUE(mapping);
+ ASSERT_TRUE(mapping->base());
+ int* stuff = static_cast<int*>(mapping->base());
+ for (size_t i = 0; i < kNumInts; i++)
+ stuff[i] = static_cast<int>(i) + kFudge;
+ }
+
+ // Map it all again, check that our scribbling is still there, then do a
+ // partial mapping and scribble on that, check that everything is coherent,
+ // unmap the first mapping, scribble on some of the second mapping, and then
+ // unmap it.
+ {
+ scoped_ptr<RawSharedBuffer::Mapping> mapping1(buffer->Map(0, kNumBytes));
+ ASSERT_TRUE(mapping1);
+ ASSERT_TRUE(mapping1->base());
+ int* stuff1 = static_cast<int*>(mapping1->base());
+ for (size_t i = 0; i < kNumInts; i++)
+ EXPECT_EQ(static_cast<int>(i) + kFudge, stuff1[i]) << i;
+
+ scoped_ptr<RawSharedBuffer::Mapping> mapping2(
+ buffer->Map((kNumInts / 2) * sizeof(int), 2 * sizeof(int)));
+ ASSERT_TRUE(mapping2);
+ ASSERT_TRUE(mapping2->base());
+ int* stuff2 = static_cast<int*>(mapping2->base());
+ EXPECT_EQ(static_cast<int>(kNumInts / 2) + kFudge, stuff2[0]);
+ EXPECT_EQ(static_cast<int>(kNumInts / 2) + 1 + kFudge, stuff2[1]);
+
+ stuff2[0] = 123;
+ stuff2[1] = 456;
+ EXPECT_EQ(123, stuff1[kNumInts / 2]);
+ EXPECT_EQ(456, stuff1[kNumInts / 2 + 1]);
+
+ mapping1.reset();
+
+ EXPECT_EQ(123, stuff2[0]);
+ EXPECT_EQ(456, stuff2[1]);
+ stuff2[1] = 789;
+ }
+
+ // Do another partial mapping and check that everything is the way we expect
+ // it to be.
+ {
+ scoped_ptr<RawSharedBuffer::Mapping> mapping(
+ buffer->Map(sizeof(int), kNumBytes - sizeof(int)));
+ ASSERT_TRUE(mapping);
+ ASSERT_TRUE(mapping->base());
+ int* stuff = static_cast<int*>(mapping->base());
+
+ for (size_t j = 0; j < kNumInts - 1; j++) {
+ int i = static_cast<int>(j) + 1;
+ if (i == kNumInts / 2) {
+ EXPECT_EQ(123, stuff[j]);
+ } else if (i == kNumInts / 2 + 1) {
+ EXPECT_EQ(789, stuff[j]);
+ } else {
+ EXPECT_EQ(i + kFudge, stuff[j]) << i;
+ }
+ }
+ }
+}
+
+// TODO(vtl): Bigger buffers.
+
+TEST(RawSharedBufferTest, InvalidArguments) {
+ // Zero length not allowed.
+ EXPECT_FALSE(RawSharedBuffer::Create(0));
+
+ // Invalid mappings:
+ scoped_ptr<RawSharedBuffer> buffer(RawSharedBuffer::Create(100));
+ ASSERT_TRUE(buffer);
+
+ // Zero length not allowed.
+ EXPECT_FALSE(buffer->Map(0, 0));
+
+ // Okay:
+ EXPECT_TRUE(buffer->Map(0, 100));
+ // Offset + length too big.
+ EXPECT_FALSE(buffer->Map(0, 101));
+ EXPECT_FALSE(buffer->Map(1, 100));
+
+ // Okay:
+ EXPECT_TRUE(buffer->Map(50, 50));
+ // Offset + length too big.
+ EXPECT_FALSE(buffer->Map(50, 51));
+ EXPECT_FALSE(buffer->Map(51, 50));
+}
+#endif // !defined(OS_WIN)
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/mojo/system/raw_shared_buffer_win.cc b/mojo/system/raw_shared_buffer_win.cc
new file mode 100644
index 0000000..c18f01f
--- /dev/null
+++ b/mojo/system/raw_shared_buffer_win.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/system/raw_shared_buffer.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace system {
+
+// RawSharedBuffer::Mapping ----------------------------------------------------
+
+void RawSharedBuffer::Mapping::Unmap() {
+ // TODO(vtl)
+ NOTIMPLEMENTED();
+}
+
+// RawSharedBuffer -------------------------------------------------------------
+
+bool RawSharedBuffer::Init() {
+ // TODO(vtl)
+ NOTIMPLEMENTED();
+ return false;
+}
+
+scoped_ptr<RawSharedBuffer::Mapping> RawSharedBuffer::MapImpl(size_t offset,
+ size_t length) {
+ // TODO(vtl)
+ NOTIMPLEMENTED();
+ return scoped_ptr<Mapping>();
+}
+
+} // namespace system
+} // namespace mojo