The test fails due to a console messages arriving sometimes before the console evalua...
[chromium-blink-merge.git] / courgette / memory_allocator.h
blob08defe46e7f78e36e90043a3cbfadf51e7ffaa60
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
3 // found in the LICENSE file.
5 #ifndef COURGETTE_MEMORY_ALLOCATOR_H_
6 #define COURGETTE_MEMORY_ALLOCATOR_H_
8 #include <memory>
10 #include "base/basictypes.h"
11 #include "base/file_path.h"
12 #include "base/logging.h"
13 #include "base/platform_file.h"
15 #ifndef NDEBUG
17 // A helper class to track down call sites that are not handling error cases.
18 template<class T>
19 class CheckReturnValue {
20 public:
21 // Not marked explicit on purpose.
22 CheckReturnValue(T value) : value_(value), checked_(false) { // NOLINT
24 CheckReturnValue(const CheckReturnValue& other)
25 : value_(other.value_), checked_(other.checked_) {
26 other.checked_ = true;
29 CheckReturnValue& operator=(const CheckReturnValue& other) {
30 if (this != &other) {
31 DCHECK(checked_);
32 value_ = other.value_;
33 checked_ = other.checked_;
34 other.checked_ = true;
38 ~CheckReturnValue() {
39 DCHECK(checked_);
42 operator const T&() const {
43 checked_ = true;
44 return value_;
47 private:
48 T value_;
49 mutable bool checked_;
51 typedef CheckReturnValue<bool> CheckBool;
52 #else
53 typedef bool CheckBool;
54 #endif
56 namespace courgette {
58 #ifdef OS_WIN
60 // Manages a temporary file. The file is created in the %TEMP% folder and
61 // is deleted when the file handle is closed.
62 // 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.
64 class TempFile {
65 public:
66 TempFile();
67 ~TempFile();
69 bool Create();
70 void Close();
71 bool SetSize(size_t size);
73 // Returns true iff the temp file is currently open.
74 bool valid() const;
76 // Returns the handle of the temporary file or INVALID_HANDLE_VALUE if
77 // a temp file has not been created.
78 base::PlatformFile handle() const;
80 protected:
81 base::PlatformFile file_;
84 // Manages a read/write virtual mapping of a physical file.
85 class FileMapping {
86 public:
87 FileMapping();
88 ~FileMapping();
90 // Map a file from beginning to |size|.
91 bool Create(HANDLE file, size_t size);
92 void Close();
94 // Returns true iff a mapping has been created.
95 bool valid() const;
97 // Returns a writable pointer to the beginning of the memory mapped file.
98 // If Create has not been called successfully, return value is NULL.
99 void* view() const;
101 protected:
102 bool InitializeView(size_t size);
104 HANDLE mapping_;
105 void* view_;
108 // Manages a temporary file and a memory mapping of the temporary file.
109 // The memory that this class manages holds a pointer back to the TempMapping
110 // object itself, so that given a memory pointer allocated by this class,
111 // you can get a pointer to the TempMapping instance that owns that memory.
112 class TempMapping {
113 public:
114 TempMapping();
115 ~TempMapping();
117 // Creates a temporary file of size |size| and maps it into the current
118 // process's address space.
119 bool Initialize(size_t size);
121 // Returns a writable pointer to the reserved memory.
122 void* memory() const;
124 // Returns true if the mapping is valid and memory is available.
125 bool valid() const;
127 // Returns a pointer to the TempMapping instance that allocated the |mem|
128 // block of memory. It's the callers responsibility to make sure that
129 // the memory block was allocated by the TempMapping class.
130 static TempMapping* GetMappingFromPtr(void* mem);
132 protected:
133 TempFile file_;
134 FileMapping mapping_;
137 // A memory allocator class that allocates memory either from the heap or via a
138 // temporary file. The interface is STL inspired but the class does not throw
139 // STL exceptions on allocation failure. Instead it returns NULL.
140 // A file allocation will be made if either the requested memory size exceeds
141 // |kMaxHeapAllocationSize| or if a heap allocation fails.
142 // Allocating the memory as a mapping of a temporary file solves the problem
143 // that there might not be enough physical memory and pagefile to support the
144 // allocation. This can happen because these resources are too small, or
145 // already committed to other processes. Provided there is enough disk, the
146 // temporary file acts like a pagefile that other processes can't access.
147 template<class T>
148 class MemoryAllocator {
149 public:
150 typedef T value_type;
151 typedef value_type* pointer;
152 typedef value_type& reference;
153 typedef const value_type* const_pointer;
154 typedef const value_type& const_reference;
155 typedef size_t size_type;
156 typedef ptrdiff_t difference_type;
158 // Each allocation is tagged with a single byte so that we know how to
159 // deallocate it.
160 enum AllocationType {
161 HEAP_ALLOCATION,
162 FILE_ALLOCATION,
165 // 5MB is the maximum heap allocation size that we'll attempt.
166 // When applying a patch for Chrome 10.X we found that at this
167 // threshold there were 17 allocations higher than this threshold
168 // (largest at 136MB) 10 allocations just below the threshold and 6362
169 // smaller allocations.
170 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5;
172 template<class OtherT>
173 struct rebind {
174 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
175 typedef MemoryAllocator<OtherT> other;
178 MemoryAllocator() _THROW0() {
181 // We can't use an explicit constructor here, as dictated by our style guide.
182 // The implementation of basic_string in Visual Studio 2010 prevents this.
183 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { // NOLINT
186 template<class OtherT>
187 MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { // NOLINT
190 ~MemoryAllocator() {
193 void deallocate(pointer ptr, size_type size) {
194 uint8* mem = reinterpret_cast<uint8*>(ptr);
195 mem -= sizeof(T);
196 if (mem[0] == HEAP_ALLOCATION) {
197 delete [] mem;
198 } else {
199 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]);
200 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem);
201 delete mapping;
205 pointer allocate(size_type count) {
206 // We use the first byte of each allocation to mark the allocation type.
207 // However, so that the allocation is properly aligned, we allocate an
208 // extra element and then use the first byte of the first element
209 // to mark the allocation type.
210 count++;
212 if (count > max_size())
213 return NULL;
215 size_type bytes = count * sizeof(T);
216 uint8* mem = NULL;
218 // First see if we can do this allocation on the heap.
219 if (count < kMaxHeapAllocationSize)
220 mem = new(std::nothrow) uint8[bytes];
221 if (mem != NULL) {
222 mem[0] = static_cast<uint8>(HEAP_ALLOCATION);
223 } else {
224 // If either the heap allocation failed or the request exceeds the
225 // max heap allocation threshold, we back the allocation with a temp file.
226 TempMapping* mapping = new(std::nothrow) TempMapping();
227 if (mapping && mapping->Initialize(bytes)) {
228 mem = reinterpret_cast<uint8*>(mapping->memory());
229 mem[0] = static_cast<uint8>(FILE_ALLOCATION);
232 return mem ? reinterpret_cast<pointer>(mem + sizeof(T)) : NULL;
235 pointer allocate(size_type count, const void* hint) {
236 return allocate(count);
239 void construct(pointer ptr, const T& value) {
240 ::new(ptr) T(value);
243 void destroy(pointer ptr) {
244 ptr->~T();
247 size_type max_size() const _THROW0() {
248 size_type count = static_cast<size_type>(-1) / sizeof(T);
249 return (0 < count ? count : 1);
253 #else // OS_WIN
255 // On Mac, Linux, we use a bare bones implementation that only does
256 // heap allocations.
257 template<class T>
258 class MemoryAllocator {
259 public:
260 typedef T value_type;
261 typedef value_type* pointer;
262 typedef value_type& reference;
263 typedef const value_type* const_pointer;
264 typedef const value_type& const_reference;
265 typedef size_t size_type;
266 typedef ptrdiff_t difference_type;
268 template<class OtherT>
269 struct rebind {
270 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
271 typedef MemoryAllocator<OtherT> other;
274 MemoryAllocator() {
277 explicit MemoryAllocator(const MemoryAllocator<T>& other) {
280 template<class OtherT>
281 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) {
284 ~MemoryAllocator() {
287 void deallocate(pointer ptr, size_type size) {
288 delete [] ptr;
291 pointer allocate(size_type count) {
292 if (count > max_size())
293 return NULL;
294 return reinterpret_cast<pointer>(
295 new(std::nothrow) uint8[count * sizeof(T)]);
298 pointer allocate(size_type count, const void* hint) {
299 return allocate(count);
302 void construct(pointer ptr, const T& value) {
303 ::new(ptr) T(value);
306 void destroy(pointer ptr) {
307 ptr->~T();
310 size_type max_size() const {
311 size_type count = static_cast<size_type>(-1) / sizeof(T);
312 return (0 < count ? count : 1);
316 #endif // OS_WIN
318 // Manages a growable buffer. The buffer allocation is done by the
319 // MemoryAllocator class. This class will not throw exceptions so call sites
320 // must be prepared to handle memory allocation failures.
321 // The interface is STL inspired to avoid having to make too many changes
322 // to code that previously was using STL.
323 template<typename T, class Allocator = MemoryAllocator<T> >
324 class NoThrowBuffer {
325 public:
326 typedef T value_type;
327 static const size_t kAllocationFailure = 0xffffffff;
328 static const size_t kStartSize = sizeof(T) > 0x100 ? 1 : 0x100 / sizeof(T);
330 NoThrowBuffer() : buffer_(NULL), size_(0), alloc_size_(0) {
333 ~NoThrowBuffer() {
334 clear();
337 void clear() {
338 if (buffer_) {
339 alloc_.deallocate(buffer_, alloc_size_);
340 buffer_ = NULL;
341 size_ = 0;
342 alloc_size_ = 0;
346 bool empty() const {
347 return size_ == 0;
350 CheckBool reserve(size_t size) WARN_UNUSED_RESULT {
351 if (failed())
352 return false;
354 if (size <= alloc_size_)
355 return true;
357 if (size < kStartSize)
358 size = kStartSize;
360 T* new_buffer = alloc_.allocate(size);
361 if (!new_buffer) {
362 clear();
363 alloc_size_ = kAllocationFailure;
364 } else {
365 if (buffer_) {
366 memcpy(new_buffer, buffer_, size_ * sizeof(T));
367 alloc_.deallocate(buffer_, alloc_size_);
369 buffer_ = new_buffer;
370 alloc_size_ = size;
373 return !failed();
376 CheckBool append(const T* data, size_t size) WARN_UNUSED_RESULT {
377 if (failed())
378 return false;
380 if (size > alloc_.max_size() - size_)
381 return false;
383 if (!size)
384 return true;
386 if ((alloc_size_ - size_) < size) {
387 const size_t max_size = alloc_.max_size();
388 size_t new_size = alloc_size_ ? alloc_size_ : kStartSize;
389 while (new_size < size_ + size) {
390 if (new_size < max_size - new_size) {
391 new_size *= 2;
392 } else {
393 new_size = max_size;
396 if (!reserve(new_size))
397 return false;
400 memcpy(buffer_ + size_, data, size * sizeof(T));
401 size_ += size;
403 return true;
406 CheckBool resize(size_t size, const T& init_value) WARN_UNUSED_RESULT {
407 if (size > size_) {
408 if (!reserve(size))
409 return false;
410 for (size_t i = size_; i < size; ++i)
411 buffer_[i] = init_value;
412 } else if (size < size_) {
413 // TODO(tommi): Should we allocate a new, smaller buffer?
414 // It might be faster for us to simply change the size.
417 size_ = size;
419 return true;
422 CheckBool push_back(const T& item) WARN_UNUSED_RESULT {
423 return append(&item, 1);
426 const T& back() const {
427 return buffer_[size_ - 1];
430 T& back() {
431 return buffer_[size_ - 1];
434 const T* begin() const {
435 if (!size_)
436 return NULL;
437 return &buffer_[0];
440 T* begin() {
441 if (!size_)
442 return NULL;
443 return &buffer_[0];
446 const T* end() const {
447 if (!size_)
448 return NULL;
449 return &buffer_[size_ - 1];
452 T* end() {
453 if (!size_)
454 return NULL;
455 return &buffer_[size_ - 1];
458 const T& operator[](size_t index) const {
459 DCHECK(index < size_);
460 return buffer_[index];
463 T& operator[](size_t index) {
464 DCHECK(index < size_);
465 return buffer_[index];
468 size_t size() const {
469 return size_;
472 T* data() const {
473 return buffer_;
476 // Returns true if an allocation failure has ever occurred for this object.
477 bool failed() const {
478 return alloc_size_ == kAllocationFailure;
481 protected:
482 T* buffer_;
483 size_t size_; // how much of the buffer we're using.
484 size_t alloc_size_; // how much space we have allocated.
485 Allocator alloc_;
488 } // namespace courgette
490 #endif // COURGETTE_MEMORY_ALLOCATOR_H_