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

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

Issue 809603004: base: Add ashmem support to base::DiscardableSharedMemory implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add missing GetPageSize on iOS Created 6 years 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/memory/discardable_shared_memory.h" 5 #include "base/memory/discardable_shared_memory.h"
6 6
7 #if defined(OS_POSIX) 7 #if defined(OS_POSIX)
8 #include <unistd.h> 8 #include <unistd.h>
9 #endif 9 #endif
10 10
11 #include <algorithm> 11 #include <algorithm>
12 12
13 #include "base/atomicops.h" 13 #include "base/atomicops.h"
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/numerics/safe_math.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
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.
16 21
17 namespace base { 22 namespace base {
18 namespace { 23 namespace {
19 24
20 // Use a machine-sized pointer as atomic type. It will use the Atomic32 or 25 // Use a machine-sized pointer as atomic type. It will use the Atomic32 or
21 // Atomic64 routines, depending on the architecture. 26 // Atomic64 routines, depending on the architecture.
22 typedef intptr_t AtomicType; 27 typedef intptr_t AtomicType;
23 typedef uintptr_t UAtomicType; 28 typedef uintptr_t UAtomicType;
24 29
25 // Template specialization for timestamp serialization/deserialization. This 30 // Template specialization for timestamp serialization/deserialization. This
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
77 UAtomicType u; 82 UAtomicType u;
78 } value; 83 } value;
79 }; 84 };
80 85
81 // Shared state is stored at offset 0 in shared memory segments. 86 // Shared state is stored at offset 0 in shared memory segments.
82 SharedState* SharedStateFromSharedMemory(const SharedMemory& shared_memory) { 87 SharedState* SharedStateFromSharedMemory(const SharedMemory& shared_memory) {
83 DCHECK(shared_memory.memory()); 88 DCHECK(shared_memory.memory());
84 return static_cast<SharedState*>(shared_memory.memory()); 89 return static_cast<SharedState*>(shared_memory.memory());
85 } 90 }
86 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);
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
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
87 } // namespace 103 } // namespace
88 104
89 DiscardableSharedMemory::DiscardableSharedMemory() { 105 DiscardableSharedMemory::DiscardableSharedMemory()
106 : mapped_size_(0), locked_page_count_(0) {
90 } 107 }
91 108
92 DiscardableSharedMemory::DiscardableSharedMemory( 109 DiscardableSharedMemory::DiscardableSharedMemory(
93 SharedMemoryHandle shared_memory_handle) 110 SharedMemoryHandle shared_memory_handle)
94 : shared_memory_(shared_memory_handle, false) { 111 : shared_memory_(shared_memory_handle, false),
112 mapped_size_(0),
113 locked_page_count_(0) {
95 } 114 }
96 115
97 DiscardableSharedMemory::~DiscardableSharedMemory() { 116 DiscardableSharedMemory::~DiscardableSharedMemory() {
98 } 117 }
99 118
100 bool DiscardableSharedMemory::CreateAndMap(size_t size) { 119 bool DiscardableSharedMemory::CreateAndMap(size_t size) {
101 CheckedNumeric<size_t> checked_size = size; 120 CheckedNumeric<size_t> checked_size = size;
102 checked_size += sizeof(SharedState); 121 checked_size += AlignToPageSize(sizeof(SharedState));
103 if (!checked_size.IsValid()) 122 if (!checked_size.IsValid())
104 return false; 123 return false;
105 124
106 if (!shared_memory_.CreateAndMapAnonymous(checked_size.ValueOrDie())) 125 if (!shared_memory_.CreateAndMapAnonymous(checked_size.ValueOrDie()))
107 return false; 126 return false;
108 127
128 mapped_size_ =
129 shared_memory_.mapped_size() - AlignToPageSize(sizeof(SharedState));
130
131 locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize();
132 #ifndef NDEBUG
133 for (size_t page = 0; page < locked_page_count_; ++page)
134 locked_pages_.insert(page);
135 #endif
136
109 DCHECK(last_known_usage_.is_null()); 137 DCHECK(last_known_usage_.is_null());
110 SharedState new_state(SharedState::LOCKED, Time()); 138 SharedState new_state(SharedState::LOCKED, Time());
111 subtle::Release_Store(&SharedStateFromSharedMemory(shared_memory_)->value.i, 139 subtle::Release_Store(&SharedStateFromSharedMemory(shared_memory_)->value.i,
112 new_state.value.i); 140 new_state.value.i);
113 return true; 141 return true;
114 } 142 }
115 143
116 bool DiscardableSharedMemory::Map(size_t size) { 144 bool DiscardableSharedMemory::Map(size_t size) {
117 return shared_memory_.Map(sizeof(SharedState) + 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 #ifndef NDEBUG
153 for (size_t page = 0; page < locked_page_count_; ++page)
154 locked_pages_.insert(page);
155 #endif
156
157 return true;
118 } 158 }
119 159
120 bool DiscardableSharedMemory::Lock() { 160 bool DiscardableSharedMemory::Lock(size_t offset, size_t length) {
121 DCHECK(shared_memory_.memory()); 161 DCHECK_EQ(AlignToPageSize(offset), offset);
162 DCHECK_EQ(AlignToPageSize(length), length);
122 163
123 // Return false when instance has been purged or not initialized properly by 164 // Return false when instance has been purged or not initialized properly by
124 // checking if |last_known_usage_| is NULL. 165 // checking if |last_known_usage_| is NULL.
125 if (last_known_usage_.is_null()) 166 if (last_known_usage_.is_null())
126 return false; 167 return false;
127 168
128 SharedState old_state(SharedState::UNLOCKED, last_known_usage_); 169 DCHECK(shared_memory_.memory());
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 170
137 // Update |last_known_usage_| in case the above CAS failed because of 171 // We need to successfully acquire the platform independent lock before
138 // an incorrect timestamp. 172 // individual pages can be locked.
139 last_known_usage_ = result.GetTimestamp(); 173 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.
140 return false; 174 SharedState old_state(SharedState::UNLOCKED, last_known_usage_);
175 SharedState new_state(SharedState::LOCKED, Time());
176 SharedState result(subtle::Acquire_CompareAndSwap(
177 &SharedStateFromSharedMemory(shared_memory_)->value.i,
178 old_state.value.i,
179 new_state.value.i));
180 if (result.value.u != old_state.value.u) {
181 // Update |last_known_usage_| in case the above CAS failed because of
182 // an incorrect timestamp.
183 last_known_usage_ = result.GetTimestamp();
184 return false;
185 }
186 }
187
188 // 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.
189 if (!length)
190 length = AlignToPageSize(mapped_size_) - offset;
191
192 size_t start = offset / base::GetPageSize();
193 size_t end = start + length / base::GetPageSize();
194 DCHECK_LT(start, AlignToPageSize(mapped_size_));
195 DCHECK_LE(end, AlignToPageSize(mapped_size_));
196
197 // Add pages to |locked_page_count_|.
198 // Note: Locking a page that is already locked is an error.
199 locked_page_count_ += end - start;
200 #ifndef NDEBUG
201 // Detect incorrect usage by keeping track of exactly what pages are locked.
202 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.
203 DCHECK(locked_pages_.find(page) == locked_pages_.end());
204 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.
205 }
206 DCHECK_EQ(locked_pages_.size(), locked_page_count_);
207 #endif
208
209 #if defined(OS_ANDROID)
210 SharedMemoryHandle handle = shared_memory_.handle();
211 DCHECK(SharedMemory::IsHandleValid(handle));
212 if (ashmem_pin_region(
213 handle.fd, AlignToPageSize(sizeof(SharedState)) + offset, length)) {
214 return false;
215 }
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
216 #endif
217
218 return true;
141 } 219 }
142 220
143 void DiscardableSharedMemory::Unlock() { 221 void DiscardableSharedMemory::Unlock(size_t offset, size_t length) {
222 DCHECK_EQ(AlignToPageSize(offset), offset);
223 DCHECK_EQ(AlignToPageSize(length), length);
224
225 // Zero for length means "everything onward".
226 if (!length)
227 length = AlignToPageSize(mapped_size_) - offset;
228
144 DCHECK(shared_memory_.memory()); 229 DCHECK(shared_memory_.memory());
145 230
231 #if defined(OS_ANDROID)
232 SharedMemoryHandle handle = shared_memory_.handle();
233 DCHECK(SharedMemory::IsHandleValid(handle));
234 if (ashmem_unpin_region(
235 handle.fd, AlignToPageSize(sizeof(SharedState)) + offset, length)) {
236 DPLOG(ERROR) << "ashmem_unpin_region() failed";
237 }
238 #endif
239
240 size_t start = offset / base::GetPageSize();
241 size_t end = start + length / base::GetPageSize();
242 DCHECK_LT(start, AlignToPageSize(mapped_size_));
243 DCHECK_LE(end, AlignToPageSize(mapped_size_));
244
245 // Remove pages from |locked_page_count_|.
246 // Note: Unlocking a page that is not locked is an error.
247 DCHECK_GE(locked_page_count_, end - start);
248 locked_page_count_ -= end - start;
249 #ifndef NDEBUG
250 // Detect incorrect usage by keeping track of exactly what pages are locked.
251 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.
252 DCHECK(locked_pages_.find(page) != locked_pages_.end());
253 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.
254 }
255 DCHECK_EQ(locked_pages_.size(), locked_page_count_);
256 #endif
257
258 // Early out and avoid releasing the platform independent lock if some pages
259 // are still locked.
260 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
261 return;
262
146 Time current_time = Now(); 263 Time current_time = Now();
147 DCHECK(!current_time.is_null()); 264 DCHECK(!current_time.is_null());
148 265
149 SharedState old_state(SharedState::LOCKED, Time()); 266 SharedState old_state(SharedState::LOCKED, Time());
150 SharedState new_state(SharedState::UNLOCKED, current_time); 267 SharedState new_state(SharedState::UNLOCKED, current_time);
151 // Note: timestamp cannot be NULL as that is a unique value used when 268 // Note: timestamp cannot be NULL as that is a unique value used when
152 // locked or purged. 269 // locked or purged.
153 DCHECK(!new_state.GetTimestamp().is_null()); 270 DCHECK(!new_state.GetTimestamp().is_null());
154 // Timestamps precision should at least be accurate to the second. 271 // Timestamp precision should at least be accurate to the second.
155 DCHECK_EQ((new_state.GetTimestamp() - Time::UnixEpoch()).InSeconds(), 272 DCHECK_EQ((new_state.GetTimestamp() - Time::UnixEpoch()).InSeconds(),
156 (current_time - Time::UnixEpoch()).InSeconds()); 273 (current_time - Time::UnixEpoch()).InSeconds());
157 SharedState result(subtle::Release_CompareAndSwap( 274 SharedState result(subtle::Release_CompareAndSwap(
158 &SharedStateFromSharedMemory(shared_memory_)->value.i, 275 &SharedStateFromSharedMemory(shared_memory_)->value.i,
159 old_state.value.i, 276 old_state.value.i,
160 new_state.value.i)); 277 new_state.value.i));
161 278
162 DCHECK_EQ(old_state.value.u, result.value.u); 279 DCHECK_EQ(old_state.value.u, result.value.u);
163 280
164 last_known_usage_ = current_time; 281 last_known_usage_ = current_time;
165 } 282 }
166 283
167 void* DiscardableSharedMemory::memory() const { 284 void* DiscardableSharedMemory::memory() const {
168 return SharedStateFromSharedMemory(shared_memory_) + 1; 285 return reinterpret_cast<uint8*>(shared_memory_.memory()) +
286 AlignToPageSize(sizeof(SharedState));
169 } 287 }
170 288
171 bool DiscardableSharedMemory::Purge(Time current_time) { 289 bool DiscardableSharedMemory::Purge(Time current_time) {
172 // Early out if not mapped. This can happen if the segment was previously 290 // Early out if not mapped. This can happen if the segment was previously
173 // unmapped using a call to Close(). 291 // unmapped using a call to Close().
174 if (!shared_memory_.memory()) 292 if (!shared_memory_.memory())
175 return true; 293 return true;
176 294
177 SharedState old_state(SharedState::UNLOCKED, last_known_usage_); 295 SharedState old_state(SharedState::UNLOCKED, last_known_usage_);
178 SharedState new_state(SharedState::UNLOCKED, Time()); 296 SharedState new_state(SharedState::UNLOCKED, Time());
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 void DiscardableSharedMemory::Close() { 344 void DiscardableSharedMemory::Close() {
227 shared_memory_.Unmap(); 345 shared_memory_.Unmap();
228 shared_memory_.Close(); 346 shared_memory_.Close();
229 } 347 }
230 348
231 Time DiscardableSharedMemory::Now() const { 349 Time DiscardableSharedMemory::Now() const {
232 return Time::Now(); 350 return Time::Now();
233 } 351 }
234 352
235 } // namespace base 353 } // 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