Chromium Code Reviews| 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) { |