1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/memory/discardable_shared_memory.h"
13 #include "base/atomicops.h"
14 #include "base/logging.h"
15 #include "base/numerics/safe_math.h"
20 // Use a machine-sized pointer as atomic type. It will use the Atomic32 or
21 // Atomic64 routines, depending on the architecture.
22 typedef intptr_t AtomicType
;
23 typedef uintptr_t UAtomicType
;
25 // Template specialization for timestamp serialization/deserialization. This
26 // is used to serialize timestamps using Unix time on systems where AtomicType
27 // does not have enough precision to contain a timestamp in the standard
30 Time
TimeFromWireFormat(int64 value
);
32 int64
TimeToWireFormat(Time time
);
34 // Serialize to Unix time when using 4-byte wire format.
35 // Note: 19 January 2038, this will cease to work.
37 Time ALLOW_UNUSED_TYPE TimeFromWireFormat
<4>(int64 value
) {
38 return value
? Time::UnixEpoch() + TimeDelta::FromSeconds(value
) : Time();
41 int64 ALLOW_UNUSED_TYPE TimeToWireFormat
<4>(Time time
) {
42 return time
> Time::UnixEpoch() ? (time
- Time::UnixEpoch()).InSeconds() : 0;
45 // Standard serialization format when using 8-byte wire format.
47 Time ALLOW_UNUSED_TYPE TimeFromWireFormat
<8>(int64 value
) {
48 return Time::FromInternalValue(value
);
51 int64 ALLOW_UNUSED_TYPE TimeToWireFormat
<8>(Time time
) {
52 return time
.ToInternalValue();
56 enum LockState
{ UNLOCKED
= 0, LOCKED
= 1 };
58 explicit SharedState(AtomicType ivalue
) { value
.i
= ivalue
; }
59 SharedState(LockState lock_state
, Time timestamp
) {
60 int64 wire_timestamp
= TimeToWireFormat
<sizeof(AtomicType
)>(timestamp
);
61 DCHECK_GE(wire_timestamp
, 0);
62 DCHECK((lock_state
& ~1) == 0);
63 value
.u
= (static_cast<UAtomicType
>(wire_timestamp
) << 1) | lock_state
;
66 LockState
GetLockState() const { return static_cast<LockState
>(value
.u
& 1); }
68 Time
GetTimestamp() const {
69 return TimeFromWireFormat
<sizeof(AtomicType
)>(value
.u
>> 1);
72 // Bit 1: Lock state. Bit is set when locked.
73 // Bit 2..sizeof(AtomicType)*8: Usage timestamp. NULL time when locked or
81 // Shared state is stored at offset 0 in shared memory segments.
82 SharedState
* SharedStateFromSharedMemory(const SharedMemory
& shared_memory
) {
83 DCHECK(shared_memory
.memory());
84 return static_cast<SharedState
*>(shared_memory
.memory());
89 DiscardableSharedMemory::DiscardableSharedMemory() {
92 DiscardableSharedMemory::DiscardableSharedMemory(
93 SharedMemoryHandle shared_memory_handle
)
94 : shared_memory_(shared_memory_handle
, false) {
97 DiscardableSharedMemory::~DiscardableSharedMemory() {
100 bool DiscardableSharedMemory::CreateAndMap(size_t size
) {
101 CheckedNumeric
<size_t> checked_size
= size
;
102 checked_size
+= sizeof(SharedState
);
103 if (!checked_size
.IsValid())
106 if (!shared_memory_
.CreateAndMapAnonymous(checked_size
.ValueOrDie()))
109 DCHECK(last_known_usage_
.is_null());
110 SharedState
new_state(SharedState::LOCKED
, Time());
111 subtle::Release_Store(&SharedStateFromSharedMemory(shared_memory_
)->value
.i
,
116 bool DiscardableSharedMemory::Map(size_t size
) {
117 return shared_memory_
.Map(sizeof(SharedState
) + size
);
120 bool DiscardableSharedMemory::Lock() {
121 DCHECK(shared_memory_
.memory());
123 // Return false when instance has been purged or not initialized properly by
124 // checking if |last_known_usage_| is NULL.
125 if (last_known_usage_
.is_null())
128 SharedState
old_state(SharedState::UNLOCKED
, last_known_usage_
);
129 SharedState
new_state(SharedState::LOCKED
, Time());
130 SharedState
result(subtle::Acquire_CompareAndSwap(
131 &SharedStateFromSharedMemory(shared_memory_
)->value
.i
,
134 if (result
.value
.u
== old_state
.value
.u
)
137 // Update |last_known_usage_| in case the above CAS failed because of
138 // an incorrect timestamp.
139 last_known_usage_
= result
.GetTimestamp();
143 void DiscardableSharedMemory::Unlock() {
144 DCHECK(shared_memory_
.memory());
146 Time current_time
= Now();
147 DCHECK(!current_time
.is_null());
149 SharedState
old_state(SharedState::LOCKED
, Time());
150 SharedState
new_state(SharedState::UNLOCKED
, current_time
);
151 // Note: timestamp cannot be NULL as that is a unique value used when
153 DCHECK(!new_state
.GetTimestamp().is_null());
154 // Timestamps precision should at least be accurate to the second.
155 DCHECK_EQ((new_state
.GetTimestamp() - Time::UnixEpoch()).InSeconds(),
156 (current_time
- Time::UnixEpoch()).InSeconds());
157 SharedState
result(subtle::Release_CompareAndSwap(
158 &SharedStateFromSharedMemory(shared_memory_
)->value
.i
,
162 DCHECK_EQ(old_state
.value
.u
, result
.value
.u
);
164 last_known_usage_
= current_time
;
167 void* DiscardableSharedMemory::memory() const {
168 return SharedStateFromSharedMemory(shared_memory_
) + 1;
171 bool DiscardableSharedMemory::Purge(Time current_time
) {
172 // Early out if not mapped. This can happen if the segment was previously
173 // unmapped using a call to Close().
174 if (!shared_memory_
.memory())
177 SharedState
old_state(SharedState::UNLOCKED
, last_known_usage_
);
178 SharedState
new_state(SharedState::UNLOCKED
, Time());
179 SharedState
result(subtle::Acquire_CompareAndSwap(
180 &SharedStateFromSharedMemory(shared_memory_
)->value
.i
,
184 // Update |last_known_usage_| to |current_time| if the memory is locked. This
185 // allows the caller to determine if purging failed because last known usage
186 // was incorrect or memory was locked. In the second case, the caller should
187 // most likely wait for some amount of time before attempting to purge the
189 if (result
.value
.u
!= old_state
.value
.u
) {
190 last_known_usage_
= result
.GetLockState() == SharedState::LOCKED
192 : result
.GetTimestamp();
196 last_known_usage_
= Time();
200 bool DiscardableSharedMemory::PurgeAndTruncate(Time current_time
) {
201 if (!Purge(current_time
))
204 #if defined(OS_POSIX)
205 // Truncate shared memory to size of SharedState.
206 SharedMemoryHandle handle
= shared_memory_
.handle();
207 if (SharedMemory::IsHandleValid(handle
)) {
208 if (HANDLE_EINTR(ftruncate(handle
.fd
, sizeof(SharedState
))) != 0)
209 DPLOG(ERROR
) << "ftruncate() failed";
216 bool DiscardableSharedMemory::IsMemoryResident() const {
217 DCHECK(shared_memory_
.memory());
219 SharedState
result(subtle::NoBarrier_Load(
220 &SharedStateFromSharedMemory(shared_memory_
)->value
.i
));
222 return result
.GetLockState() == SharedState::LOCKED
||
223 !result
.GetTimestamp().is_null();
226 void DiscardableSharedMemory::Close() {
227 shared_memory_
.Close();
230 Time
DiscardableSharedMemory::Now() const {