| Index: base/memory/discardable_shared_memory.cc
|
| diff --git a/base/memory/discardable_shared_memory.cc b/base/memory/discardable_shared_memory.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..653304806ec98d1a49a4300640720332fc00fba3
|
| --- /dev/null
|
| +++ b/base/memory/discardable_shared_memory.cc
|
| @@ -0,0 +1,234 @@
|
| +// 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 "base/memory/discardable_shared_memory.h"
|
| +
|
| +#if defined(OS_POSIX)
|
| +#include <unistd.h>
|
| +#endif
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/atomicops.h"
|
| +#include "base/logging.h"
|
| +#include "base/numerics/safe_math.h"
|
| +
|
| +namespace base {
|
| +namespace {
|
| +
|
| +// Use a machine-sized pointer as atomic type. It will use the Atomic32 or
|
| +// Atomic64 routines, depending on the architecture.
|
| +typedef intptr_t AtomicType;
|
| +typedef uintptr_t UAtomicType;
|
| +
|
| +// Template specialization for timestamp serialization/deserialization. This
|
| +// is used to serialize timestamps using Unix time on systems where AtomicType
|
| +// does not have enough precision to contain a timestamp in the standard
|
| +// serialized format.
|
| +template <int>
|
| +Time TimeFromWireFormat(int64 value);
|
| +template <int>
|
| +int64 TimeToWireFormat(Time time);
|
| +
|
| +// Serialize to Unix time when using 4-byte wire format.
|
| +// Note: 19 January 2038, this will cease to work.
|
| +template <>
|
| +Time ALLOW_UNUSED_TYPE TimeFromWireFormat<4>(int64 value) {
|
| + return value ? Time::UnixEpoch() + TimeDelta::FromSeconds(value) : Time();
|
| +}
|
| +template <>
|
| +int64 ALLOW_UNUSED_TYPE TimeToWireFormat<4>(Time time) {
|
| + return time > Time::UnixEpoch() ? (time - Time::UnixEpoch()).InSeconds() : 0;
|
| +}
|
| +
|
| +// Standard serialization format when using 8-byte wire format.
|
| +template <>
|
| +Time ALLOW_UNUSED_TYPE TimeFromWireFormat<8>(int64 value) {
|
| + return Time::FromInternalValue(value);
|
| +}
|
| +template <>
|
| +int64 ALLOW_UNUSED_TYPE TimeToWireFormat<8>(Time time) {
|
| + return time.ToInternalValue();
|
| +}
|
| +
|
| +struct SharedState {
|
| + enum LockState { UNLOCKED = 0, LOCKED = 1 };
|
| +
|
| + explicit SharedState(AtomicType ivalue) { value.i = ivalue; }
|
| + SharedState(LockState lock_state, Time timestamp) {
|
| + int64 wire_timestamp = TimeToWireFormat<sizeof(AtomicType)>(timestamp);
|
| + DCHECK_GE(wire_timestamp, 0);
|
| + DCHECK((lock_state & ~1) == 0);
|
| + value.u = (static_cast<UAtomicType>(wire_timestamp) << 1) | lock_state;
|
| + }
|
| +
|
| + LockState GetLockState() const { return static_cast<LockState>(value.u & 1); }
|
| +
|
| + Time GetTimestamp() const {
|
| + return TimeFromWireFormat<sizeof(AtomicType)>(value.u >> 1);
|
| + }
|
| +
|
| + // Bit 1: Lock state. Bit is set when locked.
|
| + // Bit 2..sizeof(AtomicType)*8: Usage timestamp. NULL time when locked or
|
| + // purged.
|
| + union {
|
| + AtomicType i;
|
| + UAtomicType u;
|
| + } value;
|
| +};
|
| +
|
| +// Shared state is stored at offset 0 in shared memory segments.
|
| +SharedState* SharedStateFromSharedMemory(const SharedMemory& shared_memory) {
|
| + DCHECK(shared_memory.memory());
|
| + return static_cast<SharedState*>(shared_memory.memory());
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +DiscardableSharedMemory::DiscardableSharedMemory() {
|
| +}
|
| +
|
| +DiscardableSharedMemory::DiscardableSharedMemory(
|
| + SharedMemoryHandle shared_memory_handle)
|
| + : shared_memory_(shared_memory_handle, false) {
|
| +}
|
| +
|
| +DiscardableSharedMemory::~DiscardableSharedMemory() {
|
| +}
|
| +
|
| +bool DiscardableSharedMemory::CreateAndMap(size_t size) {
|
| + CheckedNumeric<size_t> checked_size = size;
|
| + checked_size += sizeof(SharedState);
|
| + if (!checked_size.IsValid())
|
| + return false;
|
| +
|
| + if (!shared_memory_.CreateAndMapAnonymous(checked_size.ValueOrDie()))
|
| + return false;
|
| +
|
| + DCHECK(last_known_usage_.is_null());
|
| + SharedState new_state(SharedState::LOCKED, Time());
|
| + subtle::Release_Store(&SharedStateFromSharedMemory(shared_memory_)->value.i,
|
| + new_state.value.i);
|
| + return true;
|
| +}
|
| +
|
| +bool DiscardableSharedMemory::Map(size_t size) {
|
| + return shared_memory_.Map(sizeof(SharedState) + size);
|
| +}
|
| +
|
| +bool DiscardableSharedMemory::Lock() {
|
| + DCHECK(shared_memory_.memory());
|
| +
|
| + // 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;
|
| +
|
| + // Update |last_known_usage_| in case the above CAS failed because of
|
| + // an incorrect timestamp.
|
| + last_known_usage_ = result.GetTimestamp();
|
| + return false;
|
| +}
|
| +
|
| +void DiscardableSharedMemory::Unlock() {
|
| + DCHECK(shared_memory_.memory());
|
| +
|
| + Time current_time = Now();
|
| + DCHECK(!current_time.is_null());
|
| +
|
| + SharedState old_state(SharedState::LOCKED, Time());
|
| + SharedState new_state(SharedState::UNLOCKED, current_time);
|
| + // 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.
|
| + DCHECK_EQ((new_state.GetTimestamp() - Time::UnixEpoch()).InSeconds(),
|
| + (current_time - Time::UnixEpoch()).InSeconds());
|
| + SharedState result(subtle::Release_CompareAndSwap(
|
| + &SharedStateFromSharedMemory(shared_memory_)->value.i,
|
| + old_state.value.i,
|
| + new_state.value.i));
|
| +
|
| + DCHECK_EQ(old_state.value.u, result.value.u);
|
| +
|
| + last_known_usage_ = current_time;
|
| +}
|
| +
|
| +void* DiscardableSharedMemory::memory() const {
|
| + return SharedStateFromSharedMemory(shared_memory_) + 1;
|
| +}
|
| +
|
| +bool DiscardableSharedMemory::Purge(Time current_time) {
|
| + // Early out if not mapped. This can happen if the segment was previously
|
| + // unmapped using a call to Close().
|
| + if (!shared_memory_.memory())
|
| + return true;
|
| +
|
| + SharedState old_state(SharedState::UNLOCKED, last_known_usage_);
|
| + SharedState new_state(SharedState::UNLOCKED, Time());
|
| + SharedState result(subtle::Acquire_CompareAndSwap(
|
| + &SharedStateFromSharedMemory(shared_memory_)->value.i,
|
| + old_state.value.i,
|
| + new_state.value.i));
|
| +
|
| + // Update |last_known_usage_| to |current_time| if the memory is locked. This
|
| + // allows the caller to determine if purging failed because last known usage
|
| + // was incorrect or memory was locked. In the second case, the caller should
|
| + // most likely wait for some amount of time before attempting to purge the
|
| + // the memory again.
|
| + if (result.value.u != old_state.value.u) {
|
| + last_known_usage_ = result.GetLockState() == SharedState::LOCKED
|
| + ? current_time
|
| + : result.GetTimestamp();
|
| + return false;
|
| + }
|
| +
|
| + last_known_usage_ = Time();
|
| + return true;
|
| +}
|
| +
|
| +bool DiscardableSharedMemory::PurgeAndTruncate(Time current_time) {
|
| + if (!Purge(current_time))
|
| + return false;
|
| +
|
| +#if defined(OS_POSIX)
|
| + // Truncate shared memory to size of SharedState.
|
| + SharedMemoryHandle handle = shared_memory_.handle();
|
| + if (SharedMemory::IsHandleValid(handle)) {
|
| + if (HANDLE_EINTR(ftruncate(handle.fd, sizeof(SharedState))) != 0)
|
| + DPLOG(ERROR) << "ftruncate() failed";
|
| + }
|
| +#endif
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool DiscardableSharedMemory::IsMemoryResident() const {
|
| + DCHECK(shared_memory_.memory());
|
| +
|
| + SharedState result(subtle::NoBarrier_Load(
|
| + &SharedStateFromSharedMemory(shared_memory_)->value.i));
|
| +
|
| + return result.GetLockState() == SharedState::LOCKED ||
|
| + !result.GetTimestamp().is_null();
|
| +}
|
| +
|
| +void DiscardableSharedMemory::Close() {
|
| + shared_memory_.Close();
|
| +}
|
| +
|
| +Time DiscardableSharedMemory::Now() const {
|
| + return Time::Now();
|
| +}
|
| +
|
| +} // namespace base
|
|
|