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

Side by Side Diff: courgette/memory_allocator.h

Issue 6677141: Switch out use of std::string and std::vector for large allocations for a buffer class that doesn... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 9 years, 8 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 #ifndef COURGETTE_MEMORY_ALLOCATOR_H_ 5 #ifndef COURGETTE_MEMORY_ALLOCATOR_H_
6 #define COURGETTE_MEMORY_ALLOCATOR_H_ 6 #define COURGETTE_MEMORY_ALLOCATOR_H_
7 7
8 #include <memory> 8 #include <memory>
9 9
10 #include "base/basictypes.h" 10 #include "base/basictypes.h"
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
49 mutable bool checked_; 49 mutable bool checked_;
50 }; 50 };
51 typedef CheckReturnValue<bool> CheckBool; 51 typedef CheckReturnValue<bool> CheckBool;
52 #else 52 #else
53 typedef bool CheckBool; 53 typedef bool CheckBool;
54 #endif 54 #endif
55 55
56 namespace courgette { 56 namespace courgette {
57 57
58 #ifdef OS_WIN 58 #ifdef OS_WIN
59 // Prototype for the allocation handler function.
60 // See ThrowExceptionOnAllocationFailure below.
61 typedef void (__stdcall* AllocationFailureHandler)(uint32 err);
62
63 // A helper function to help diagnose failures we've seen in the field.
64 // The function constructs a string containing the error and throws a runtime
65 // exception. The calling convention is set to stdcall to force argument
66 // passing via the stack.
67 __declspec(noinline)
68 void __stdcall ThrowExceptionOnAllocationFailure(uint32 err);
59 69
60 // Manages a temporary file. The file is created in the %TEMP% folder and 70 // Manages a temporary file. The file is created in the %TEMP% folder and
61 // is deleted when the file handle is closed. 71 // is deleted when the file handle is closed.
62 // NOTE: Since the file will be used as backing for a memory allocation, 72 // NOTE: Since the file will be used as backing for a memory allocation,
63 // it will never be so big that size_t cannot represent its size. 73 // it will never be so big that size_t cannot represent its size.
64 class TempFile { 74 class TempFile {
65 public: 75 public:
66 TempFile(); 76 TempFile();
67 ~TempFile(); 77 ~TempFile();
68 78
69 __declspec(noinline) void Create(); 79 __declspec(noinline) bool Create(AllocationFailureHandler failure_handler);
70 void Close(); 80 void Close();
71 __declspec(noinline) void SetSize(size_t size); 81 __declspec(noinline) bool SetSize(size_t size,
82 AllocationFailureHandler failure_handler);
72 83
73 // Returns true iff the temp file is currently open. 84 // Returns true iff the temp file is currently open.
74 bool valid() const; 85 bool valid() const;
75 86
76 // Returns the handle of the temporary file or INVALID_HANDLE_VALUE if 87 // Returns the handle of the temporary file or INVALID_HANDLE_VALUE if
77 // a temp file has not been created. 88 // a temp file has not been created.
78 base::PlatformFile handle() const; 89 base::PlatformFile handle() const;
79 90
80 // Returns the size of the temp file. If the temp file doesn't exist, 91 // Returns the size of the temp file. If the temp file doesn't exist,
81 // the return value is 0. 92 // the return value is 0.
82 size_t size() const; 93 size_t size() const;
83 94
84 protected: 95 protected:
85 __declspec(noinline) FilePath PrepareTempFile(); 96 __declspec(noinline)
97 bool PrepareTempFile(FilePath* path,
grt (UTC plus 2) 2011/04/05 17:10:08 move |path| after |failure_handler|: out params co
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
98 AllocationFailureHandler failure_handler);
86 99
87 base::PlatformFile file_; 100 base::PlatformFile file_;
88 size_t size_; 101 size_t size_;
89 }; 102 };
90 103
91 // Manages a read/write virtual mapping of a physical file. 104 // Manages a read/write virtual mapping of a physical file.
92 class FileMapping { 105 class FileMapping {
93 public: 106 public:
94 FileMapping(); 107 FileMapping();
95 ~FileMapping(); 108 ~FileMapping();
96 109
97 // Map a file from beginning to |size|. 110 // Map a file from beginning to |size|.
98 __declspec(noinline) void Create(HANDLE file, size_t size); 111 __declspec(noinline) bool Create(HANDLE file, size_t size,
112 AllocationFailureHandler failure_handler);
99 void Close(); 113 void Close();
100 114
101 // Returns true iff a mapping has been created. 115 // Returns true iff a mapping has been created.
102 bool valid() const; 116 bool valid() const;
103 117
104 // Returns a writable pointer to the beginning of the memory mapped file. 118 // Returns a writable pointer to the beginning of the memory mapped file.
105 // If Create has not been called successfully, return value is NULL. 119 // If Create has not been called successfully, return value is NULL.
106 void* view() const; 120 void* view() const;
107 121
108 protected: 122 protected:
109 __declspec(noinline) void InitializeView(size_t size); 123 __declspec(noinline)
124 bool InitializeView(size_t size, AllocationFailureHandler failure_handler);
110 125
111 HANDLE mapping_; 126 HANDLE mapping_;
112 void* view_; 127 void* view_;
113 }; 128 };
114 129
115 // Manages a temporary file and a memory mapping of the temporary file. 130 // Manages a temporary file and a memory mapping of the temporary file.
116 // The memory that this class manages holds a pointer back to the TempMapping 131 // The memory that this class manages holds a pointer back to the TempMapping
117 // object itself, so that given a memory pointer allocated by this class, 132 // object itself, so that given a memory pointer allocated by this class,
118 // you can get a pointer to the TempMapping instance that owns that memory. 133 // you can get a pointer to the TempMapping instance that owns that memory.
119 class TempMapping { 134 class TempMapping {
120 public: 135 public:
121 TempMapping(); 136 TempMapping();
122 ~TempMapping(); 137 ~TempMapping();
123 138
124 // Creates a temporary file of size |size| and maps it into the current 139 // Creates a temporary file of size |size| and maps it into the current
125 // process' address space. 140 // process' address space.
126 __declspec(noinline) void Initialize(size_t size); 141 __declspec(noinline) bool Initialize(size_t size,
142 AllocationFailureHandler failure_handler);
127 143
128 // Returns a writable pointer to the reserved memory. 144 // Returns a writable pointer to the reserved memory.
129 void* memory() const; 145 void* memory() const;
130 146
147 // Returns true if the mapping is valid and memory is available.
148 bool valid() const;
149
131 // Returns a pointer to the TempMapping instance that allocated the |mem| 150 // Returns a pointer to the TempMapping instance that allocated the |mem|
132 // block of memory. It's the callers responsibility to make sure that 151 // block of memory. It's the callers responsibility to make sure that
133 // the memory block was allocated by the TempMapping class. 152 // the memory block was allocated by the TempMapping class.
134 static TempMapping* GetMappingFromPtr(void* mem); 153 static TempMapping* GetMappingFromPtr(void* mem);
135 154
136 protected: 155 protected:
137 TempFile file_; 156 TempFile file_;
138 FileMapping mapping_; 157 FileMapping mapping_;
139 }; 158 };
140 159
141 // An STL compatible memory allocator class that allocates memory either 160 // An STL compatible memory allocator class that allocates memory either
grt (UTC plus 2) 2011/04/05 17:10:08 Since the allocate() method now returns NULL on er
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
142 // from the heap or via a temporary file. A file allocation will be made 161 // from the heap or via a temporary file. A file allocation will be made
143 // if either the requested memory size exceeds |kMaxHeapAllocationSize| 162 // if either the requested memory size exceeds |kMaxHeapAllocationSize|
144 // or if a heap allocation fails. 163 // or if a heap allocation fails.
145 // Allocating the memory as a mapping of a temporary file solves the problem 164 // Allocating the memory as a mapping of a temporary file solves the problem
146 // that there might not be enough physical memory and pagefile to support the 165 // that there might not be enough physical memory and pagefile to support the
147 // allocation. This can happen because these resources are too small, or 166 // allocation. This can happen because these resources are too small, or
148 // already committed to other processes. Provided there is enough disk, the 167 // already committed to other processes. Provided there is enough disk, the
149 // temporary file acts like a pagefile that other processes can't access. 168 // temporary file acts like a pagefile that other processes can't access.
150 template<class T> 169 template<class T, AllocationFailureHandler mem_failures_handler>
grt (UTC plus 2) 2011/04/05 17:10:08 If you make this template<class T, AllocationFailu
tommi (sloooow) - chröme 2011/04/05 19:06:53 Yes, but afaik the style guide doesn't allow that,
151 class MemoryAllocator { 170 class MemoryAllocator {
152 public: 171 public:
153 typedef T value_type; 172 typedef T value_type;
154 typedef value_type* pointer; 173 typedef value_type* pointer;
155 typedef value_type& reference; 174 typedef value_type& reference;
156 typedef const value_type* const_pointer; 175 typedef const value_type* const_pointer;
157 typedef const value_type& const_reference; 176 typedef const value_type& const_reference;
158 typedef size_t size_type; 177 typedef size_t size_type;
159 typedef ptrdiff_t difference_type; 178 typedef ptrdiff_t difference_type;
160 179
161 // Each allocation is tagged with a single byte so that we know how to 180 // Each allocation is tagged with a single byte so that we know how to
162 // deallocate it. 181 // deallocate it.
163 enum AllocationType { 182 enum AllocationType {
164 HEAP_ALLOCATION, 183 HEAP_ALLOCATION,
165 FILE_ALLOCATION, 184 FILE_ALLOCATION,
166 }; 185 };
167 186
168 // 5MB is the maximum heap allocation size that we'll attempt. 187 // 5MB is the maximum heap allocation size that we'll attempt.
169 // When applying a patch for Chrome 10.X we found that at this 188 // When applying a patch for Chrome 10.X we found that at this
170 // threshold there were 17 allocations higher than this threshold 189 // threshold there were 17 allocations higher than this threshold
171 // (largest at 136MB) 10 allocations just below the threshold and 6362 190 // (largest at 136MB) 10 allocations just below the threshold and 6362
172 // smaller allocations. 191 // smaller allocations.
173 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5; 192 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5;
174 193
175 template<class OtherT> 194 template<class OtherT>
176 struct rebind { 195 struct rebind {
177 // convert an MemoryAllocator<T> to a MemoryAllocator<OtherT> 196 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
178 typedef MemoryAllocator<OtherT> other; 197 typedef MemoryAllocator<OtherT, mem_failures_handler> other;
179 }; 198 };
180 199
181 MemoryAllocator() _THROW0() { 200 MemoryAllocator() _THROW0() {
182 } 201 }
183 202
184 // We can't use an explicit constructor here, as dictated by our style guide. 203 // We can't use an explicit constructor here, as dictated by our style guide.
185 // The implementation of basic_string in Visual Studio 2010 prevents this. 204 // The implementation of basic_string in Visual Studio 2010 prevents this.
186 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { 205 MemoryAllocator( // NOLINT
206 const MemoryAllocator<T, mem_failures_handler>& other) _THROW0() {
187 } 207 }
188 208
189 template<class OtherT> 209 template<class OtherT>
190 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { 210 MemoryAllocator( // NOLINT
211 const MemoryAllocator<OtherT, mem_failures_handler>& other) _THROW0() {
191 } 212 }
192 213
193 ~MemoryAllocator() { 214 ~MemoryAllocator() {
194 } 215 }
195 216
196 void deallocate(pointer ptr, size_type size) { 217 void deallocate(pointer ptr, size_type size) {
197 uint8* mem = reinterpret_cast<uint8*>(ptr); 218 uint8* mem = reinterpret_cast<uint8*>(ptr);
198 mem -= sizeof(T); 219 mem -= sizeof(T);
199 if (mem[0] == HEAP_ALLOCATION) { 220 if (mem[0] == HEAP_ALLOCATION) {
200 delete [] mem; 221 delete [] mem;
201 } else { 222 } else {
202 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]); 223 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]);
203 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem); 224 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem);
204 delete mapping; 225 delete mapping;
205 } 226 }
206 } 227 }
207 228
208 pointer allocate(size_type count) { 229 pointer allocate(size_type count) {
209 // We use the first byte of each allocation to mark the allocation type. 230 // We use the first byte of each allocation to mark the allocation type.
210 // However, so that the allocation is properly aligned, we allocate an 231 // However, so that the allocation is properly aligned, we allocate an
211 // extra element and then use the first byte of the first element 232 // extra element and then use the first byte of the first element
212 // to mark the allocation type. 233 // to mark the allocation type.
213 count++; 234 count++;
214 235
215 if (count > max_size()) 236 if (count > max_size()) {
216 throw std::length_error("overflow"); 237 if (mem_failures_handler)
238 mem_failures_handler(ERROR_INVALID_PARAMETER);
239 return NULL;
240 }
217 241
218 size_type bytes = count * sizeof(T); 242 size_type bytes = count * sizeof(T);
219 uint8* mem = NULL; 243 uint8* mem = NULL;
220 244
221 // First see if we can do this allocation on the heap. 245 // First see if we can do this allocation on the heap.
222 if (count < kMaxHeapAllocationSize) 246 if (count < kMaxHeapAllocationSize)
223 mem = new(std::nothrow) uint8[bytes]; 247 mem = new(std::nothrow) uint8[bytes];
224 if (mem != NULL) { 248 if (mem != NULL) {
225 mem[0] = static_cast<uint8>(HEAP_ALLOCATION); 249 mem[0] = static_cast<uint8>(HEAP_ALLOCATION);
226 } else { 250 } else {
227 // If either the heap allocation failed or the request exceeds the 251 // If either the heap allocation failed or the request exceeds the
228 // max heap allocation threshold, we back the allocation with a temp file. 252 // max heap allocation threshold, we back the allocation with a temp file.
229 TempMapping* mapping = new TempMapping(); 253 TempMapping* mapping = new(std::nothrow) TempMapping();
230 mapping->Initialize(bytes); 254 if (!mapping && mem_failures_handler)
231 mem = reinterpret_cast<uint8*>(mapping->memory()); 255 mem_failures_handler(ERROR_OUTOFMEMORY);
232 mem[0] = static_cast<uint8>(FILE_ALLOCATION); 256
257 if (mapping && mapping->Initialize(bytes, mem_failures_handler)) {
258 mem = reinterpret_cast<uint8*>(mapping->memory());
259 mem[0] = static_cast<uint8>(FILE_ALLOCATION);
260 }
233 } 261 }
234 return reinterpret_cast<pointer>(mem + sizeof(T)); 262 return mem ? reinterpret_cast<pointer>(mem + sizeof(T)) : NULL;
235 } 263 }
236 264
237 pointer allocate(size_type count, const void* hint) { 265 pointer allocate(size_type count, const void* hint) {
238 return allocate(count); 266 return allocate(count);
239 } 267 }
240 268
241 void construct(pointer ptr, const T& value) { 269 void construct(pointer ptr, const T& value) {
242 ::new(ptr) T(value); 270 ::new(ptr) T(value);
243 } 271 }
244 272
245 void destroy(pointer ptr) { 273 void destroy(pointer ptr) {
246 ptr->~T(); 274 ptr->~T();
247 } 275 }
248 276
249 size_t max_size() const _THROW0() { 277 size_t max_size() const _THROW0() {
250 size_type count = static_cast<size_type>(-1) / sizeof(T); 278 size_type count = static_cast<size_type>(-1) / sizeof(T);
251 return (0 < count ? count : 1); 279 return (0 < count ? count : 1);
252 } 280 }
253 }; 281 };
254 282
255 #else // OS_WIN 283 #else // OS_WIN
256 284
257 // On Mac, Linux, we just use the default STL allocator. 285 // On Mac, Linux, we just use the default STL allocator.
258 template<class T> 286 template<class T>
259 class MemoryAllocator : public std::allocator<T> { 287 class MemoryAllocator : public std::allocator<T> {
grt (UTC plus 2) 2011/04/05 17:10:08 This class template has different semantics than t
tommi (sloooow) - chröme 2011/04/05 19:06:53 That's intentional. The problem I'm addressing is
260 public: 288 public:
261 }; 289 };
262 290
263 #endif // OS_WIN 291 #endif // OS_WIN
264 292
grt (UTC plus 2) 2011/04/05 17:10:08 Add some documentation for this class and for its
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
293 template<typename T>
grt (UTC plus 2) 2011/04/05 17:10:08 Consider: template<typename T, class Allocator = M
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
294 class NoThrowBuffer {
295 public:
296 typedef T value_type;
297 const static size_t kAllocationFailure = 0xffffffff;
grt (UTC plus 2) 2011/04/05 17:10:08 "static const" here and on the next line.
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
298 const static size_t kStartSize = sizeof(T) > 0xff ? 1 : 0xff / sizeof(T);
grt (UTC plus 2) 2011/04/05 17:10:08 Why 0xff rather than 0x100?
tommi (sloooow) - chröme 2011/04/05 19:06:53 Cause it's closer to fffffuuuuuuuu... :) no real r
299
300 NoThrowBuffer() : buffer_(NULL), size_(0), alloc_size_(0) {
301 }
302
303 ~NoThrowBuffer() {
304 clear();
305 }
306
307 void clear() {
308 if (buffer_) {
309 alloc_.deallocate(buffer_, alloc_size_);
310 buffer_ = NULL;
311 size_ = 0;
312 alloc_size_ = 0;
313 }
314 }
315
316 bool empty() const {
317 return size_ == 0;
318 }
319
320 CheckBool reserve(size_t size) {
grt (UTC plus 2) 2011/04/05 17:10:08 Use WARN_UNUSED_RESULT (from base/compiler_specifi
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
321 if (failed())
322 return false;
323
324 if (size <= alloc_size_)
325 return true;
326
327 if (size < kStartSize) {
grt (UTC plus 2) 2011/04/05 17:10:08 nit: remove braces
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
328 size = kStartSize;
329 }
330
331 T* new_buffer = alloc_.allocate(size);
332 if (!new_buffer) {
333 clear();
334 alloc_size_ = kAllocationFailure;
335 } else {
336 if (buffer_) {
337 memcpy(new_buffer, buffer_, size_ * sizeof(T));
338 alloc_.deallocate(buffer_, alloc_size_);
339 }
340 buffer_ = new_buffer;
341 alloc_size_ = size;
342 }
343
344 return !failed();
345 }
346
347 CheckBool append(const T* data, size_t size) {
348 if (failed())
349 return false;
350
grt (UTC plus 2) 2011/04/05 17:10:08 if (size > alloc_.max_size() - size_) return fal
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
351 if (!size)
352 return true;
353
354 if ((alloc_size_ - size_) < size) {
grt (UTC plus 2) 2011/04/05 17:10:08 this code has a few problems with overflow. the f
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
355 size_t new_size = alloc_size_ ? (alloc_size_ * 2) : kStartSize;
356 while (new_size < size)
357 new_size *= 2;
358 if (!reserve(new_size))
359 return false;
360 }
361
362 memcpy(buffer_ + size_, data, size * sizeof(T));
363 size_ += size;
364
365 return true;
366 }
367
368 CheckBool resize(size_t size, const T& init_value) {
369 if (size > size_) {
370 if (!reserve(size))
371 return false;
372 for (size_t i = size_; i < size; ++i)
373 buffer_[i] = init_value;
374 } else if (size < size_) {
375 // TODO(tommi): Should we allocate a new, smaller buffer?
376 // It might simply be faster for us to don't reallocate but only
grt (UTC plus 2) 2011/04/05 17:10:08 nit: "It might be faster for us to simply change t
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
377 // change the size.
378 }
379
380 size_ = size;
381
382 return true;
383 }
384
385 CheckBool push_back(const T& item) {
386 return append(&item, 1);
387 }
388
389 const T& back() const {
390 return buffer_[size_ - 1];
391 }
392
393 T& back() {
394 return buffer_[size_ - 1];
395 }
396
397 const T* begin() const {
398 if (!size_)
399 return NULL;
400 return &buffer_[0];
401 }
402
403 T* begin() {
404 if (!size_)
405 return NULL;
406 return &buffer_[0];
407 }
408
409 const T* end() const {
410 if (!size_)
411 return NULL;
412 return &buffer_[size_ - 1];
413 }
414
415 T* end() {
416 if (!size_)
417 return NULL;
418 return &buffer_[size_ - 1];
419 }
420
421 const T& operator[](size_t index) const {
422 return buffer_[index];
grt (UTC plus 2) 2011/04/05 17:10:08 DCHECK(index < size_)?
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
423 }
424
425 T& operator[](size_t index) {
426 return buffer_[index];
grt (UTC plus 2) 2011/04/05 17:10:08 DCHECK(index < size_)?
tommi (sloooow) - chröme 2011/04/05 19:06:53 Done.
427 }
428
429 size_t size() const {
430 return size_;
431 }
432
433 T* data() const {
434 return buffer_;
435 }
436
437 // Returns true if an allocation failure has ever occurred for this object.
438 bool failed() const {
439 return alloc_size_ == kAllocationFailure;
440 }
441
442 protected:
443 T* buffer_;
444 size_t size_; // how much of the buffer we're using.
445 size_t alloc_size_; // how much space we have allocated.
446 #ifdef OS_WIN
447 MemoryAllocator<T, NULL> alloc_;
448 #else
449 MemoryAllocator<T> alloc_;
450 #endif
451 };
452
265 } // namespace courgette 453 } // namespace courgette
266 454
267 #endif // COURGETTE_MEMORY_ALLOCATOR_H_ 455 #endif // COURGETTE_MEMORY_ALLOCATOR_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698