Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1049)

Unified Diff: base/memory/discardable_shared_memory.cc

Issue 809603004: base: Add ashmem support to base::DiscardableSharedMemory implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add missing GetPageSize on iOS Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/memory/discardable_shared_memory.h ('k') | base/memory/discardable_shared_memory_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/memory/discardable_shared_memory.cc
diff --git a/base/memory/discardable_shared_memory.cc b/base/memory/discardable_shared_memory.cc
index 851f1add97f0326bd1ce28bb43c228a5b5aafe77..6dc651c729e830b7aad98b4e60d6447fd5954745 100644
--- a/base/memory/discardable_shared_memory.cc
+++ b/base/memory/discardable_shared_memory.cc
@@ -13,6 +13,11 @@
#include "base/atomicops.h"
#include "base/logging.h"
#include "base/numerics/safe_math.h"
+#include "base/process/process_metrics.h"
+
+#if defined(OS_ANDROID)
+#include "third_party/ashmem/ashmem.h"
+#endif
Avi (use Gerrit) 2014/12/16 23:20:30 We need to deal with one of the implementations in
reveman 2014/12/17 16:34:05 We can introduce discardable_shared_memory_posix/a
Avi (use Gerrit) 2014/12/17 16:58:17 Acknowledged.
namespace base {
namespace {
@@ -84,14 +89,28 @@ SharedState* SharedStateFromSharedMemory(const SharedMemory& shared_memory) {
return static_cast<SharedState*>(shared_memory.memory());
}
+// Round up |size| to a multiple of alignment, which must be a power of two.
+size_t Align(size_t alignment, size_t size) {
+ DCHECK_EQ(alignment & (alignment - 1), 0u);
Avi (use Gerrit) 2014/12/16 23:20:30 DCHECK_EQ(0u, ...)
reveman 2014/12/17 16:34:06 Done. But out curiosity, why? I used to write code
Avi (use Gerrit) 2014/12/17 16:58:17 The printout, mostly. When it fails, it explicitly
reveman 2014/12/17 18:38:49 I don't think it does for DCHECK_*. It will just s
+ return (size + alignment - 1) & ~(alignment - 1);
+}
+
+// Round up |size| to a multiple of page size.
+size_t AlignToPageSize(size_t size) {
+ return Align(base::GetPageSize(), size);
+}
+
} // namespace
-DiscardableSharedMemory::DiscardableSharedMemory() {
+DiscardableSharedMemory::DiscardableSharedMemory()
+ : mapped_size_(0), locked_page_count_(0) {
}
DiscardableSharedMemory::DiscardableSharedMemory(
SharedMemoryHandle shared_memory_handle)
- : shared_memory_(shared_memory_handle, false) {
+ : shared_memory_(shared_memory_handle, false),
+ mapped_size_(0),
+ locked_page_count_(0) {
}
DiscardableSharedMemory::~DiscardableSharedMemory() {
@@ -99,13 +118,22 @@ DiscardableSharedMemory::~DiscardableSharedMemory() {
bool DiscardableSharedMemory::CreateAndMap(size_t size) {
CheckedNumeric<size_t> checked_size = size;
- checked_size += sizeof(SharedState);
+ checked_size += AlignToPageSize(sizeof(SharedState));
if (!checked_size.IsValid())
return false;
if (!shared_memory_.CreateAndMapAnonymous(checked_size.ValueOrDie()))
return false;
+ mapped_size_ =
+ shared_memory_.mapped_size() - AlignToPageSize(sizeof(SharedState));
+
+ locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize();
+#ifndef NDEBUG
+ for (size_t page = 0; page < locked_page_count_; ++page)
+ locked_pages_.insert(page);
+#endif
+
DCHECK(last_known_usage_.is_null());
SharedState new_state(SharedState::LOCKED, Time());
subtle::Release_Store(&SharedStateFromSharedMemory(shared_memory_)->value.i,
@@ -114,35 +142,124 @@ bool DiscardableSharedMemory::CreateAndMap(size_t size) {
}
bool DiscardableSharedMemory::Map(size_t size) {
- return shared_memory_.Map(sizeof(SharedState) + size);
+ if (!shared_memory_.Map(AlignToPageSize(sizeof(SharedState)) + size))
+ return false;
+
+ mapped_size_ =
+ shared_memory_.mapped_size() - AlignToPageSize(sizeof(SharedState));
+
+ locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize();
+#ifndef NDEBUG
+ for (size_t page = 0; page < locked_page_count_; ++page)
+ locked_pages_.insert(page);
+#endif
+
+ return true;
}
-bool DiscardableSharedMemory::Lock() {
- DCHECK(shared_memory_.memory());
+bool DiscardableSharedMemory::Lock(size_t offset, size_t length) {
+ DCHECK_EQ(AlignToPageSize(offset), offset);
+ DCHECK_EQ(AlignToPageSize(length), length);
// Return false when instance has been purged or not initialized properly by
// checking if |last_known_usage_| is NULL.
if (last_known_usage_.is_null())
return false;
- SharedState old_state(SharedState::UNLOCKED, last_known_usage_);
- SharedState new_state(SharedState::LOCKED, Time());
- SharedState result(subtle::Acquire_CompareAndSwap(
- &SharedStateFromSharedMemory(shared_memory_)->value.i,
- old_state.value.i,
- new_state.value.i));
- if (result.value.u == old_state.value.u)
- return true;
+ DCHECK(shared_memory_.memory());
+
+ // We need to successfully acquire the platform independent lock before
+ // individual pages can be locked.
+ if (!locked_page_count_) {
Avi (use Gerrit) 2014/12/16 23:20:29 I have a bad gut feeling about this. It used to b
reveman 2014/12/17 16:34:05 Multiple calls on the same instance? This class is
Avi (use Gerrit) 2014/12/17 16:58:17 If this isn't thread-safe, can you at least do a t
reveman 2014/12/17 18:38:48 Added a ThreadCollisionWarner.
+ SharedState old_state(SharedState::UNLOCKED, last_known_usage_);
+ SharedState new_state(SharedState::LOCKED, Time());
+ SharedState result(subtle::Acquire_CompareAndSwap(
+ &SharedStateFromSharedMemory(shared_memory_)->value.i,
+ old_state.value.i,
+ new_state.value.i));
+ if (result.value.u != old_state.value.u) {
+ // Update |last_known_usage_| in case the above CAS failed because of
+ // an incorrect timestamp.
+ last_known_usage_ = result.GetTimestamp();
+ return false;
+ }
+ }
+
+ // Zero for length means "everything onward".
Avi (use Gerrit) 2014/12/16 23:20:30 What? Why is this not in the .h file? That's too i
reveman 2014/12/17 16:34:05 Sorry, I meant to also add a comment about this to
Avi (use Gerrit) 2014/12/17 16:58:17 Acknowledged.
+ if (!length)
+ length = AlignToPageSize(mapped_size_) - offset;
+
+ size_t start = offset / base::GetPageSize();
+ size_t end = start + length / base::GetPageSize();
+ DCHECK_LT(start, AlignToPageSize(mapped_size_));
+ DCHECK_LE(end, AlignToPageSize(mapped_size_));
+
+ // Add pages to |locked_page_count_|.
+ // Note: Locking a page that is already locked is an error.
+ locked_page_count_ += end - start;
+#ifndef NDEBUG
+ // Detect incorrect usage by keeping track of exactly what pages are locked.
+ for (size_t page = start; page < end; ++page) {
Avi (use Gerrit) 2014/12/16 23:20:29 auto page
reveman 2014/12/17 16:34:05 Done.
+ DCHECK(locked_pages_.find(page) == locked_pages_.end());
+ locked_pages_.insert(page);
Avi (use Gerrit) 2014/12/16 23:20:30 A smidge faster to sub in the two lines: auto res
reveman 2014/12/17 16:34:05 Done.
+ }
+ DCHECK_EQ(locked_pages_.size(), locked_page_count_);
+#endif
+
+#if defined(OS_ANDROID)
+ SharedMemoryHandle handle = shared_memory_.handle();
+ DCHECK(SharedMemory::IsHandleValid(handle));
+ if (ashmem_pin_region(
+ handle.fd, AlignToPageSize(sizeof(SharedState)) + offset, length)) {
+ return false;
+ }
Avi (use Gerrit) 2014/12/16 23:20:30 Is there any way we can not deal with ashmem here
reveman 2014/12/17 16:34:05 I think it's better to include android/posix speci
+#endif
- // Update |last_known_usage_| in case the above CAS failed because of
- // an incorrect timestamp.
- last_known_usage_ = result.GetTimestamp();
- return false;
+ return true;
}
-void DiscardableSharedMemory::Unlock() {
+void DiscardableSharedMemory::Unlock(size_t offset, size_t length) {
+ DCHECK_EQ(AlignToPageSize(offset), offset);
+ DCHECK_EQ(AlignToPageSize(length), length);
+
+ // Zero for length means "everything onward".
+ if (!length)
+ length = AlignToPageSize(mapped_size_) - offset;
+
DCHECK(shared_memory_.memory());
+#if defined(OS_ANDROID)
+ SharedMemoryHandle handle = shared_memory_.handle();
+ DCHECK(SharedMemory::IsHandleValid(handle));
+ if (ashmem_unpin_region(
+ handle.fd, AlignToPageSize(sizeof(SharedState)) + offset, length)) {
+ DPLOG(ERROR) << "ashmem_unpin_region() failed";
+ }
+#endif
+
+ size_t start = offset / base::GetPageSize();
+ size_t end = start + length / base::GetPageSize();
+ DCHECK_LT(start, AlignToPageSize(mapped_size_));
+ DCHECK_LE(end, AlignToPageSize(mapped_size_));
+
+ // Remove pages from |locked_page_count_|.
+ // Note: Unlocking a page that is not locked is an error.
+ DCHECK_GE(locked_page_count_, end - start);
+ locked_page_count_ -= end - start;
+#ifndef NDEBUG
+ // Detect incorrect usage by keeping track of exactly what pages are locked.
+ for (uintptr_t page = start; page < end; ++page) {
Avi (use Gerrit) 2014/12/16 23:20:30 auto page Might as well use auto in for loop decl
reveman 2014/12/17 16:34:06 Done.
+ DCHECK(locked_pages_.find(page) != locked_pages_.end());
+ locked_pages_.erase(page);
Avi (use Gerrit) 2014/12/16 23:20:30 A smidge faster to sub in the two lines: auto era
reveman 2014/12/17 16:34:05 Done.
+ }
+ DCHECK_EQ(locked_pages_.size(), locked_page_count_);
+#endif
+
+ // Early out and avoid releasing the platform independent lock if some pages
+ // are still locked.
+ if (locked_page_count_)
Avi (use Gerrit) 2014/12/16 23:20:30 Same freakout over concurrency. Unlock used to use
reveman 2014/12/17 16:34:06 Same reply. Instances of this class are not thread
+ return;
+
Time current_time = Now();
DCHECK(!current_time.is_null());
@@ -151,7 +268,7 @@ void DiscardableSharedMemory::Unlock() {
// Note: timestamp cannot be NULL as that is a unique value used when
// locked or purged.
DCHECK(!new_state.GetTimestamp().is_null());
- // Timestamps precision should at least be accurate to the second.
+ // Timestamp precision should at least be accurate to the second.
DCHECK_EQ((new_state.GetTimestamp() - Time::UnixEpoch()).InSeconds(),
(current_time - Time::UnixEpoch()).InSeconds());
SharedState result(subtle::Release_CompareAndSwap(
@@ -165,7 +282,8 @@ void DiscardableSharedMemory::Unlock() {
}
void* DiscardableSharedMemory::memory() const {
- return SharedStateFromSharedMemory(shared_memory_) + 1;
+ return reinterpret_cast<uint8*>(shared_memory_.memory()) +
+ AlignToPageSize(sizeof(SharedState));
}
bool DiscardableSharedMemory::Purge(Time current_time) {
« no previous file with comments | « base/memory/discardable_shared_memory.h ('k') | base/memory/discardable_shared_memory_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698