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

Unified Diff: base/memory/discardable_shared_memory.cc

Issue 822713002: Update from https://crrev.com/309415 (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: 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
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..eb9e55236218a4af9861753a8af93445aedc6ef4 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
namespace base {
namespace {
@@ -59,7 +64,7 @@ struct SharedState {
SharedState(LockState lock_state, Time timestamp) {
int64 wire_timestamp = TimeToWireFormat<sizeof(AtomicType)>(timestamp);
DCHECK_GE(wire_timestamp, 0);
- DCHECK((lock_state & ~1) == 0);
+ DCHECK_EQ(lock_state & ~1, 0);
value.u = (static_cast<UAtomicType>(wire_timestamp) << 1) | lock_state;
}
@@ -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);
+ 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();
+#if DCHECK_IS_ON
+ 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,130 @@ 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();
+#if DCHECK_IS_ON
+ 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);
+
+ // Calls to this function must synchronized properly.
+ DFAKE_SCOPED_LOCK(thread_collision_warner_);
// 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_) {
+ 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".
+ if (!length)
+ length = AlignToPageSize(mapped_size_) - offset;
+
+ size_t start = offset / base::GetPageSize();
+ size_t end = start + length / base::GetPageSize();
+ DCHECK_LT(start, end);
+ DCHECK_LE(end, AlignToPageSize(mapped_size_) / base::GetPageSize());
+
+ // Add pages to |locked_page_count_|.
+ // Note: Locking a page that is already locked is an error.
+ locked_page_count_ += end - start;
+#if DCHECK_IS_ON
+ // Detect incorrect usage by keeping track of exactly what pages are locked.
+ for (auto page = start; page < end; ++page) {
+ auto result = locked_pages_.insert(page);
+ DCHECK(result.second);
+ }
+ DCHECK_EQ(locked_pages_.size(), locked_page_count_);
+#endif
- // Update |last_known_usage_| in case the above CAS failed because of
- // an incorrect timestamp.
- last_known_usage_ = result.GetTimestamp();
- return false;
+#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;
+ }
+#endif
+
+ return true;
}
-void DiscardableSharedMemory::Unlock() {
+void DiscardableSharedMemory::Unlock(size_t offset, size_t length) {
+ DCHECK_EQ(AlignToPageSize(offset), offset);
+ DCHECK_EQ(AlignToPageSize(length), length);
+
+ // Calls to this function must synchronized properly.
+ DFAKE_SCOPED_LOCK(thread_collision_warner_);
+
+ // 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, end);
+ DCHECK_LE(end, AlignToPageSize(mapped_size_) / base::GetPageSize());
+
+ // 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;
+#if DCHECK_IS_ON
+ // Detect incorrect usage by keeping track of exactly what pages are locked.
+ for (auto page = start; page < end; ++page) {
+ auto erased_count = locked_pages_.erase(page);
+ DCHECK_EQ(1u, erased_count);
+ }
+ 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_)
+ return;
+
Time current_time = Now();
DCHECK(!current_time.is_null());
@@ -151,7 +274,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,10 +288,14 @@ 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) {
+ // Calls to this function must synchronized properly.
+ DFAKE_SCOPED_LOCK(thread_collision_warner_);
+
// Early out if not mapped. This can happen if the segment was previously
// unmapped using a call to Close().
if (!shared_memory_.memory())

Powered by Google App Engine
This is Rietveld 408576698