diff options
author | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-21 20:45:27 +0000 |
---|---|---|
committer | viettrungluu@chromium.org <viettrungluu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-21 20:45:27 +0000 |
commit | ac1df702a252fb22e4a8ba68cc80d0c67b08d6de (patch) | |
tree | 41d9ed4db4cb2c101a67aaa1778754f1ad855e49 /mojo/system | |
parent | 976966c809acb6f46aed9468aeaf0f86df1f470f (diff) | |
download | chromium_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.cc | 41 | ||||
-rw-r--r-- | mojo/system/raw_shared_buffer.h | 93 | ||||
-rw-r--r-- | mojo/system/raw_shared_buffer_posix.cc | 118 | ||||
-rw-r--r-- | mojo/system/raw_shared_buffer_unittest.cc | 120 | ||||
-rw-r--r-- | mojo/system/raw_shared_buffer_win.cc | 35 |
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 |