Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / base / memory / discardable_shared_memory.cc
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.
4
5 #include "base/memory/discardable_shared_memory.h"
6
7 #if defined(OS_POSIX)
8 #include <unistd.h>
9 #endif
10
11 #include <algorithm>
12
13 #include "base/atomicops.h"
14 #include "base/logging.h"
15 #include "base/numerics/safe_math.h"
16
17 namespace base {
18 namespace {
19
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;
24
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
28 // serialized format.
29 template <int>
30 Time TimeFromWireFormat(int64 value);
31 template <int>
32 int64 TimeToWireFormat(Time time);
33
34 // Serialize to Unix time when using 4-byte wire format.
35 // Note: 19 January 2038, this will cease to work.
36 template <>
37 Time ALLOW_UNUSED_TYPE TimeFromWireFormat<4>(int64 value) {
38   return value ? Time::UnixEpoch() + TimeDelta::FromSeconds(value) : Time();
39 }
40 template <>
41 int64 ALLOW_UNUSED_TYPE TimeToWireFormat<4>(Time time) {
42   return time > Time::UnixEpoch() ? (time - Time::UnixEpoch()).InSeconds() : 0;
43 }
44
45 // Standard serialization format when using 8-byte wire format.
46 template <>
47 Time ALLOW_UNUSED_TYPE TimeFromWireFormat<8>(int64 value) {
48   return Time::FromInternalValue(value);
49 }
50 template <>
51 int64 ALLOW_UNUSED_TYPE TimeToWireFormat<8>(Time time) {
52   return time.ToInternalValue();
53 }
54
55 struct SharedState {
56   enum LockState { UNLOCKED = 0, LOCKED = 1 };
57
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;
64   }
65
66   LockState GetLockState() const { return static_cast<LockState>(value.u & 1); }
67
68   Time GetTimestamp() const {
69     return TimeFromWireFormat<sizeof(AtomicType)>(value.u >> 1);
70   }
71
72   // Bit 1: Lock state. Bit is set when locked.
73   // Bit 2..sizeof(AtomicType)*8: Usage timestamp. NULL time when locked or
74   // purged.
75   union {
76     AtomicType i;
77     UAtomicType u;
78   } value;
79 };
80
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());
85 }
86
87 }  // namespace
88
89 DiscardableSharedMemory::DiscardableSharedMemory() {
90 }
91
92 DiscardableSharedMemory::DiscardableSharedMemory(
93     SharedMemoryHandle shared_memory_handle)
94     : shared_memory_(shared_memory_handle, false) {
95 }
96
97 DiscardableSharedMemory::~DiscardableSharedMemory() {
98 }
99
100 bool DiscardableSharedMemory::CreateAndMap(size_t size) {
101   CheckedNumeric<size_t> checked_size = size;
102   checked_size += sizeof(SharedState);
103   if (!checked_size.IsValid())
104     return false;
105
106   if (!shared_memory_.CreateAndMapAnonymous(checked_size.ValueOrDie()))
107     return false;
108
109   DCHECK(last_known_usage_.is_null());
110   SharedState new_state(SharedState::LOCKED, Time());
111   subtle::Release_Store(&SharedStateFromSharedMemory(shared_memory_)->value.i,
112                         new_state.value.i);
113   return true;
114 }
115
116 bool DiscardableSharedMemory::Map(size_t size) {
117   return shared_memory_.Map(sizeof(SharedState) + size);
118 }
119
120 bool DiscardableSharedMemory::Lock() {
121   DCHECK(shared_memory_.memory());
122
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())
126     return false;
127
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,
132       old_state.value.i,
133       new_state.value.i));
134   if (result.value.u == old_state.value.u)
135     return true;
136
137   // Update |last_known_usage_| in case the above CAS failed because of
138   // an incorrect timestamp.
139   last_known_usage_ = result.GetTimestamp();
140   return false;
141 }
142
143 void DiscardableSharedMemory::Unlock() {
144   DCHECK(shared_memory_.memory());
145
146   Time current_time = Now();
147   DCHECK(!current_time.is_null());
148
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
152   // locked or purged.
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,
159       old_state.value.i,
160       new_state.value.i));
161
162   DCHECK_EQ(old_state.value.u, result.value.u);
163
164   last_known_usage_ = current_time;
165 }
166
167 void* DiscardableSharedMemory::memory() const {
168   return SharedStateFromSharedMemory(shared_memory_) + 1;
169 }
170
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())
175     return true;
176
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,
181       old_state.value.i,
182       new_state.value.i));
183
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
188   // the memory again.
189   if (result.value.u != old_state.value.u) {
190     last_known_usage_ = result.GetLockState() == SharedState::LOCKED
191                             ? current_time
192                             : result.GetTimestamp();
193     return false;
194   }
195
196   last_known_usage_ = Time();
197   return true;
198 }
199
200 bool DiscardableSharedMemory::PurgeAndTruncate(Time current_time) {
201   if (!Purge(current_time))
202     return false;
203
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";
210   }
211 #endif
212
213   return true;
214 }
215
216 bool DiscardableSharedMemory::IsMemoryResident() const {
217   DCHECK(shared_memory_.memory());
218
219   SharedState result(subtle::NoBarrier_Load(
220       &SharedStateFromSharedMemory(shared_memory_)->value.i));
221
222   return result.GetLockState() == SharedState::LOCKED ||
223          !result.GetTimestamp().is_null();
224 }
225
226 void DiscardableSharedMemory::Close() {
227   shared_memory_.Close();
228 }
229
230 Time DiscardableSharedMemory::Now() const {
231   return Time::Now();
232 }
233
234 }  // namespace base