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

Side by Side Diff: base/memory/discardable_shared_memory.cc

Issue 1647803004: Move base to DEPS (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 4 years, 10 months 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 unified diff | Download patch
OLDNEW
(Empty)
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 #include "base/process/process_metrics.h"
17
18 #if defined(OS_ANDROID)
19 #include "third_party/ashmem/ashmem.h"
20 #endif
21
22 namespace base {
23 namespace {
24
25 // Use a machine-sized pointer as atomic type. It will use the Atomic32 or
26 // Atomic64 routines, depending on the architecture.
27 typedef intptr_t AtomicType;
28 typedef uintptr_t UAtomicType;
29
30 // Template specialization for timestamp serialization/deserialization. This
31 // is used to serialize timestamps using Unix time on systems where AtomicType
32 // does not have enough precision to contain a timestamp in the standard
33 // serialized format.
34 template <int>
35 Time TimeFromWireFormat(int64 value);
36 template <int>
37 int64 TimeToWireFormat(Time time);
38
39 // Serialize to Unix time when using 4-byte wire format.
40 // Note: 19 January 2038, this will cease to work.
41 template <>
42 Time ALLOW_UNUSED_TYPE TimeFromWireFormat<4>(int64 value) {
43 return value ? Time::UnixEpoch() + TimeDelta::FromSeconds(value) : Time();
44 }
45 template <>
46 int64 ALLOW_UNUSED_TYPE TimeToWireFormat<4>(Time time) {
47 return time > Time::UnixEpoch() ? (time - Time::UnixEpoch()).InSeconds() : 0;
48 }
49
50 // Standard serialization format when using 8-byte wire format.
51 template <>
52 Time ALLOW_UNUSED_TYPE TimeFromWireFormat<8>(int64 value) {
53 return Time::FromInternalValue(value);
54 }
55 template <>
56 int64 ALLOW_UNUSED_TYPE TimeToWireFormat<8>(Time time) {
57 return time.ToInternalValue();
58 }
59
60 struct SharedState {
61 enum LockState { UNLOCKED = 0, LOCKED = 1 };
62
63 explicit SharedState(AtomicType ivalue) { value.i = ivalue; }
64 SharedState(LockState lock_state, Time timestamp) {
65 int64 wire_timestamp = TimeToWireFormat<sizeof(AtomicType)>(timestamp);
66 DCHECK_GE(wire_timestamp, 0);
67 DCHECK_EQ(lock_state & ~1, 0);
68 value.u = (static_cast<UAtomicType>(wire_timestamp) << 1) | lock_state;
69 }
70
71 LockState GetLockState() const { return static_cast<LockState>(value.u & 1); }
72
73 Time GetTimestamp() const {
74 return TimeFromWireFormat<sizeof(AtomicType)>(value.u >> 1);
75 }
76
77 // Bit 1: Lock state. Bit is set when locked.
78 // Bit 2..sizeof(AtomicType)*8: Usage timestamp. NULL time when locked or
79 // purged.
80 union {
81 AtomicType i;
82 UAtomicType u;
83 } value;
84 };
85
86 // Shared state is stored at offset 0 in shared memory segments.
87 SharedState* SharedStateFromSharedMemory(const SharedMemory& shared_memory) {
88 DCHECK(shared_memory.memory());
89 return static_cast<SharedState*>(shared_memory.memory());
90 }
91
92 // Round up |size| to a multiple of alignment, which must be a power of two.
93 size_t Align(size_t alignment, size_t size) {
94 DCHECK_EQ(alignment & (alignment - 1), 0u);
95 return (size + alignment - 1) & ~(alignment - 1);
96 }
97
98 // Round up |size| to a multiple of page size.
99 size_t AlignToPageSize(size_t size) {
100 return Align(base::GetPageSize(), size);
101 }
102
103 } // namespace
104
105 DiscardableSharedMemory::DiscardableSharedMemory()
106 : mapped_size_(0), locked_page_count_(0) {
107 }
108
109 DiscardableSharedMemory::DiscardableSharedMemory(
110 SharedMemoryHandle shared_memory_handle)
111 : shared_memory_(shared_memory_handle, false),
112 mapped_size_(0),
113 locked_page_count_(0) {
114 }
115
116 DiscardableSharedMemory::~DiscardableSharedMemory() {
117 }
118
119 bool DiscardableSharedMemory::CreateAndMap(size_t size) {
120 CheckedNumeric<size_t> checked_size = size;
121 checked_size += AlignToPageSize(sizeof(SharedState));
122 if (!checked_size.IsValid())
123 return false;
124
125 if (!shared_memory_.CreateAndMapAnonymous(checked_size.ValueOrDie()))
126 return false;
127
128 mapped_size_ =
129 shared_memory_.mapped_size() - AlignToPageSize(sizeof(SharedState));
130
131 locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize();
132 #if DCHECK_IS_ON()
133 for (size_t page = 0; page < locked_page_count_; ++page)
134 locked_pages_.insert(page);
135 #endif
136
137 DCHECK(last_known_usage_.is_null());
138 SharedState new_state(SharedState::LOCKED, Time());
139 subtle::Release_Store(&SharedStateFromSharedMemory(shared_memory_)->value.i,
140 new_state.value.i);
141 return true;
142 }
143
144 bool DiscardableSharedMemory::Map(size_t size) {
145 if (!shared_memory_.Map(AlignToPageSize(sizeof(SharedState)) + size))
146 return false;
147
148 mapped_size_ =
149 shared_memory_.mapped_size() - AlignToPageSize(sizeof(SharedState));
150
151 locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize();
152 #if DCHECK_IS_ON()
153 for (size_t page = 0; page < locked_page_count_; ++page)
154 locked_pages_.insert(page);
155 #endif
156
157 return true;
158 }
159
160 bool DiscardableSharedMemory::Unmap() {
161 if (!shared_memory_.Unmap())
162 return false;
163
164 mapped_size_ = 0;
165 return true;
166 }
167
168 DiscardableSharedMemory::LockResult DiscardableSharedMemory::Lock(
169 size_t offset, size_t length) {
170 DCHECK_EQ(AlignToPageSize(offset), offset);
171 DCHECK_EQ(AlignToPageSize(length), length);
172
173 // Calls to this function must be synchronized properly.
174 DFAKE_SCOPED_LOCK(thread_collision_warner_);
175
176 DCHECK(shared_memory_.memory());
177
178 // We need to successfully acquire the platform independent lock before
179 // individual pages can be locked.
180 if (!locked_page_count_) {
181 // Return false when instance has been purged or not initialized properly
182 // by checking if |last_known_usage_| is NULL.
183 if (last_known_usage_.is_null())
184 return FAILED;
185
186 SharedState old_state(SharedState::UNLOCKED, last_known_usage_);
187 SharedState new_state(SharedState::LOCKED, Time());
188 SharedState result(subtle::Acquire_CompareAndSwap(
189 &SharedStateFromSharedMemory(shared_memory_)->value.i,
190 old_state.value.i,
191 new_state.value.i));
192 if (result.value.u != old_state.value.u) {
193 // Update |last_known_usage_| in case the above CAS failed because of
194 // an incorrect timestamp.
195 last_known_usage_ = result.GetTimestamp();
196 return FAILED;
197 }
198 }
199
200 // Zero for length means "everything onward".
201 if (!length)
202 length = AlignToPageSize(mapped_size_) - offset;
203
204 size_t start = offset / base::GetPageSize();
205 size_t end = start + length / base::GetPageSize();
206 DCHECK_LT(start, end);
207 DCHECK_LE(end, AlignToPageSize(mapped_size_) / base::GetPageSize());
208
209 // Add pages to |locked_page_count_|.
210 // Note: Locking a page that is already locked is an error.
211 locked_page_count_ += end - start;
212 #if DCHECK_IS_ON()
213 // Detect incorrect usage by keeping track of exactly what pages are locked.
214 for (auto page = start; page < end; ++page) {
215 auto result = locked_pages_.insert(page);
216 DCHECK(result.second);
217 }
218 DCHECK_EQ(locked_pages_.size(), locked_page_count_);
219 #endif
220
221 #if defined(OS_ANDROID)
222 SharedMemoryHandle handle = shared_memory_.handle();
223 if (SharedMemory::IsHandleValid(handle)) {
224 if (ashmem_pin_region(
225 handle.fd, AlignToPageSize(sizeof(SharedState)) + offset, length)) {
226 return PURGED;
227 }
228 }
229 #endif
230
231 return SUCCESS;
232 }
233
234 void DiscardableSharedMemory::Unlock(size_t offset, size_t length) {
235 DCHECK_EQ(AlignToPageSize(offset), offset);
236 DCHECK_EQ(AlignToPageSize(length), length);
237
238 // Calls to this function must be synchronized properly.
239 DFAKE_SCOPED_LOCK(thread_collision_warner_);
240
241 // Zero for length means "everything onward".
242 if (!length)
243 length = AlignToPageSize(mapped_size_) - offset;
244
245 DCHECK(shared_memory_.memory());
246
247 #if defined(OS_ANDROID)
248 SharedMemoryHandle handle = shared_memory_.handle();
249 if (SharedMemory::IsHandleValid(handle)) {
250 if (ashmem_unpin_region(
251 handle.fd, AlignToPageSize(sizeof(SharedState)) + offset, length)) {
252 DPLOG(ERROR) << "ashmem_unpin_region() failed";
253 }
254 }
255 #endif
256
257 size_t start = offset / base::GetPageSize();
258 size_t end = start + length / base::GetPageSize();
259 DCHECK_LT(start, end);
260 DCHECK_LE(end, AlignToPageSize(mapped_size_) / base::GetPageSize());
261
262 // Remove pages from |locked_page_count_|.
263 // Note: Unlocking a page that is not locked is an error.
264 DCHECK_GE(locked_page_count_, end - start);
265 locked_page_count_ -= end - start;
266 #if DCHECK_IS_ON()
267 // Detect incorrect usage by keeping track of exactly what pages are locked.
268 for (auto page = start; page < end; ++page) {
269 auto erased_count = locked_pages_.erase(page);
270 DCHECK_EQ(1u, erased_count);
271 }
272 DCHECK_EQ(locked_pages_.size(), locked_page_count_);
273 #endif
274
275 // Early out and avoid releasing the platform independent lock if some pages
276 // are still locked.
277 if (locked_page_count_)
278 return;
279
280 Time current_time = Now();
281 DCHECK(!current_time.is_null());
282
283 SharedState old_state(SharedState::LOCKED, Time());
284 SharedState new_state(SharedState::UNLOCKED, current_time);
285 // Note: timestamp cannot be NULL as that is a unique value used when
286 // locked or purged.
287 DCHECK(!new_state.GetTimestamp().is_null());
288 // Timestamp precision should at least be accurate to the second.
289 DCHECK_EQ((new_state.GetTimestamp() - Time::UnixEpoch()).InSeconds(),
290 (current_time - Time::UnixEpoch()).InSeconds());
291 SharedState result(subtle::Release_CompareAndSwap(
292 &SharedStateFromSharedMemory(shared_memory_)->value.i,
293 old_state.value.i,
294 new_state.value.i));
295
296 DCHECK_EQ(old_state.value.u, result.value.u);
297
298 last_known_usage_ = current_time;
299 }
300
301 void* DiscardableSharedMemory::memory() const {
302 return reinterpret_cast<uint8*>(shared_memory_.memory()) +
303 AlignToPageSize(sizeof(SharedState));
304 }
305
306 bool DiscardableSharedMemory::Purge(Time current_time) {
307 // Calls to this function must be synchronized properly.
308 DFAKE_SCOPED_LOCK(thread_collision_warner_);
309
310 // Early out if not mapped. This can happen if the segment was previously
311 // unmapped using a call to Close().
312 if (!shared_memory_.memory())
313 return true;
314
315 SharedState old_state(SharedState::UNLOCKED, last_known_usage_);
316 SharedState new_state(SharedState::UNLOCKED, Time());
317 SharedState result(subtle::Acquire_CompareAndSwap(
318 &SharedStateFromSharedMemory(shared_memory_)->value.i,
319 old_state.value.i,
320 new_state.value.i));
321
322 // Update |last_known_usage_| to |current_time| if the memory is locked. This
323 // allows the caller to determine if purging failed because last known usage
324 // was incorrect or memory was locked. In the second case, the caller should
325 // most likely wait for some amount of time before attempting to purge the
326 // the memory again.
327 if (result.value.u != old_state.value.u) {
328 last_known_usage_ = result.GetLockState() == SharedState::LOCKED
329 ? current_time
330 : result.GetTimestamp();
331 return false;
332 }
333
334 last_known_usage_ = Time();
335 return true;
336 }
337
338 bool DiscardableSharedMemory::IsMemoryResident() const {
339 DCHECK(shared_memory_.memory());
340
341 SharedState result(subtle::NoBarrier_Load(
342 &SharedStateFromSharedMemory(shared_memory_)->value.i));
343
344 return result.GetLockState() == SharedState::LOCKED ||
345 !result.GetTimestamp().is_null();
346 }
347
348 void DiscardableSharedMemory::Close() {
349 shared_memory_.Close();
350 }
351
352 #if defined(DISCARDABLE_SHARED_MEMORY_SHRINKING)
353 void DiscardableSharedMemory::Shrink() {
354 #if defined(OS_POSIX)
355 SharedMemoryHandle handle = shared_memory_.handle();
356 if (!SharedMemory::IsHandleValid(handle))
357 return;
358
359 // Truncate shared memory to size of SharedState.
360 if (HANDLE_EINTR(ftruncate(SharedMemory::GetFdFromSharedMemoryHandle(handle),
361 AlignToPageSize(sizeof(SharedState)))) != 0) {
362 DPLOG(ERROR) << "ftruncate() failed";
363 return;
364 }
365 mapped_size_ = 0;
366 #else
367 NOTIMPLEMENTED();
368 #endif
369 }
370 #endif
371
372 Time DiscardableSharedMemory::Now() const {
373 return Time::Now();
374 }
375
376 } // namespace base
OLDNEW
« no previous file with comments | « base/memory/discardable_shared_memory.h ('k') | base/memory/discardable_shared_memory_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698