| OLD | NEW |
| 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 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 59 | 59 |
| 60 // Manages a temporary file. The file is created in the %TEMP% folder and | 60 // Manages a temporary file. The file is created in the %TEMP% folder and |
| 61 // is deleted when the file handle is closed. | 61 // is deleted when the file handle is closed. |
| 62 // NOTE: Since the file will be used as backing for a memory allocation, | 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. | 63 // it will never be so big that size_t cannot represent its size. |
| 64 class TempFile { | 64 class TempFile { |
| 65 public: | 65 public: |
| 66 TempFile(); | 66 TempFile(); |
| 67 ~TempFile(); | 67 ~TempFile(); |
| 68 | 68 |
| 69 __declspec(noinline) void Create(); | 69 bool Create(); |
| 70 void Close(); | 70 void Close(); |
| 71 __declspec(noinline) void SetSize(size_t size); | 71 bool SetSize(size_t size); |
| 72 | 72 |
| 73 // Returns true iff the temp file is currently open. | 73 // Returns true iff the temp file is currently open. |
| 74 bool valid() const; | 74 bool valid() const; |
| 75 | 75 |
| 76 // Returns the handle of the temporary file or INVALID_HANDLE_VALUE if | 76 // Returns the handle of the temporary file or INVALID_HANDLE_VALUE if |
| 77 // a temp file has not been created. | 77 // a temp file has not been created. |
| 78 base::PlatformFile handle() const; | 78 base::PlatformFile handle() const; |
| 79 | 79 |
| 80 // Returns the size of the temp file. If the temp file doesn't exist, | |
| 81 // the return value is 0. | |
| 82 size_t size() const; | |
| 83 | |
| 84 protected: | 80 protected: |
| 85 __declspec(noinline) FilePath PrepareTempFile(); | |
| 86 | |
| 87 base::PlatformFile file_; | 81 base::PlatformFile file_; |
| 88 size_t size_; | |
| 89 }; | 82 }; |
| 90 | 83 |
| 91 // Manages a read/write virtual mapping of a physical file. | 84 // Manages a read/write virtual mapping of a physical file. |
| 92 class FileMapping { | 85 class FileMapping { |
| 93 public: | 86 public: |
| 94 FileMapping(); | 87 FileMapping(); |
| 95 ~FileMapping(); | 88 ~FileMapping(); |
| 96 | 89 |
| 97 // Map a file from beginning to |size|. | 90 // Map a file from beginning to |size|. |
| 98 __declspec(noinline) void Create(HANDLE file, size_t size); | 91 bool Create(HANDLE file, size_t size); |
| 99 void Close(); | 92 void Close(); |
| 100 | 93 |
| 101 // Returns true iff a mapping has been created. | 94 // Returns true iff a mapping has been created. |
| 102 bool valid() const; | 95 bool valid() const; |
| 103 | 96 |
| 104 // Returns a writable pointer to the beginning of the memory mapped file. | 97 // Returns a writable pointer to the beginning of the memory mapped file. |
| 105 // If Create has not been called successfully, return value is NULL. | 98 // If Create has not been called successfully, return value is NULL. |
| 106 void* view() const; | 99 void* view() const; |
| 107 | 100 |
| 108 protected: | 101 protected: |
| 109 __declspec(noinline) void InitializeView(size_t size); | 102 bool InitializeView(size_t size); |
| 110 | 103 |
| 111 HANDLE mapping_; | 104 HANDLE mapping_; |
| 112 void* view_; | 105 void* view_; |
| 113 }; | 106 }; |
| 114 | 107 |
| 115 // Manages a temporary file and a memory mapping of the temporary file. | 108 // 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 | 109 // 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, | 110 // 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. | 111 // you can get a pointer to the TempMapping instance that owns that memory. |
| 119 class TempMapping { | 112 class TempMapping { |
| 120 public: | 113 public: |
| 121 TempMapping(); | 114 TempMapping(); |
| 122 ~TempMapping(); | 115 ~TempMapping(); |
| 123 | 116 |
| 124 // Creates a temporary file of size |size| and maps it into the current | 117 // Creates a temporary file of size |size| and maps it into the current |
| 125 // process' address space. | 118 // process's address space. |
| 126 __declspec(noinline) void Initialize(size_t size); | 119 bool Initialize(size_t size); |
| 127 | 120 |
| 128 // Returns a writable pointer to the reserved memory. | 121 // Returns a writable pointer to the reserved memory. |
| 129 void* memory() const; | 122 void* memory() const; |
| 130 | 123 |
| 124 // Returns true if the mapping is valid and memory is available. |
| 125 bool valid() const; |
| 126 |
| 131 // Returns a pointer to the TempMapping instance that allocated the |mem| | 127 // Returns a pointer to the TempMapping instance that allocated the |mem| |
| 132 // block of memory. It's the callers responsibility to make sure that | 128 // block of memory. It's the callers responsibility to make sure that |
| 133 // the memory block was allocated by the TempMapping class. | 129 // the memory block was allocated by the TempMapping class. |
| 134 static TempMapping* GetMappingFromPtr(void* mem); | 130 static TempMapping* GetMappingFromPtr(void* mem); |
| 135 | 131 |
| 136 protected: | 132 protected: |
| 137 TempFile file_; | 133 TempFile file_; |
| 138 FileMapping mapping_; | 134 FileMapping mapping_; |
| 139 }; | 135 }; |
| 140 | 136 |
| 141 // An STL compatible memory allocator class that allocates memory either | 137 // A memory allocator class that allocates memory either from the heap or via a |
| 142 // from the heap or via a temporary file. A file allocation will be made | 138 // temporary file. The interface is STL inspired but the class does not throw |
| 143 // if either the requested memory size exceeds |kMaxHeapAllocationSize| | 139 // STL exceptions on allocation failure. Instead it returns NULL. |
| 144 // or if a heap allocation fails. | 140 // A file allocation will be made if either the requested memory size exceeds |
| 141 // |kMaxHeapAllocationSize| or if a heap allocation fails. |
| 145 // Allocating the memory as a mapping of a temporary file solves the problem | 142 // 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 | 143 // 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 | 144 // allocation. This can happen because these resources are too small, or |
| 148 // already committed to other processes. Provided there is enough disk, the | 145 // already committed to other processes. Provided there is enough disk, the |
| 149 // temporary file acts like a pagefile that other processes can't access. | 146 // temporary file acts like a pagefile that other processes can't access. |
| 150 template<class T> | 147 template<class T> |
| 151 class MemoryAllocator { | 148 class MemoryAllocator { |
| 152 public: | 149 public: |
| 153 typedef T value_type; | 150 typedef T value_type; |
| 154 typedef value_type* pointer; | 151 typedef value_type* pointer; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 167 | 164 |
| 168 // 5MB is the maximum heap allocation size that we'll attempt. | 165 // 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 | 166 // When applying a patch for Chrome 10.X we found that at this |
| 170 // threshold there were 17 allocations higher than this threshold | 167 // threshold there were 17 allocations higher than this threshold |
| 171 // (largest at 136MB) 10 allocations just below the threshold and 6362 | 168 // (largest at 136MB) 10 allocations just below the threshold and 6362 |
| 172 // smaller allocations. | 169 // smaller allocations. |
| 173 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5; | 170 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5; |
| 174 | 171 |
| 175 template<class OtherT> | 172 template<class OtherT> |
| 176 struct rebind { | 173 struct rebind { |
| 177 // convert an MemoryAllocator<T> to a MemoryAllocator<OtherT> | 174 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT> |
| 178 typedef MemoryAllocator<OtherT> other; | 175 typedef MemoryAllocator<OtherT> other; |
| 179 }; | 176 }; |
| 180 | 177 |
| 181 MemoryAllocator() _THROW0() { | 178 MemoryAllocator() _THROW0() { |
| 182 } | 179 } |
| 183 | 180 |
| 184 // We can't use an explicit constructor here, as dictated by our style guide. | 181 // 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. | 182 // The implementation of basic_string in Visual Studio 2010 prevents this. |
| 186 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { | 183 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { // NOLINT |
| 187 } | 184 } |
| 188 | 185 |
| 189 template<class OtherT> | 186 template<class OtherT> |
| 190 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { | 187 MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { // NOLINT |
| 191 } | 188 } |
| 192 | 189 |
| 193 ~MemoryAllocator() { | 190 ~MemoryAllocator() { |
| 194 } | 191 } |
| 195 | 192 |
| 196 void deallocate(pointer ptr, size_type size) { | 193 void deallocate(pointer ptr, size_type size) { |
| 197 uint8* mem = reinterpret_cast<uint8*>(ptr); | 194 uint8* mem = reinterpret_cast<uint8*>(ptr); |
| 198 mem -= sizeof(T); | 195 mem -= sizeof(T); |
| 199 if (mem[0] == HEAP_ALLOCATION) { | 196 if (mem[0] == HEAP_ALLOCATION) { |
| 200 delete [] mem; | 197 delete [] mem; |
| 201 } else { | 198 } else { |
| 202 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]); | 199 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]); |
| 203 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem); | 200 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem); |
| 204 delete mapping; | 201 delete mapping; |
| 205 } | 202 } |
| 206 } | 203 } |
| 207 | 204 |
| 208 pointer allocate(size_type count) { | 205 pointer allocate(size_type count) { |
| 209 // We use the first byte of each allocation to mark the allocation type. | 206 // 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 | 207 // However, so that the allocation is properly aligned, we allocate an |
| 211 // extra element and then use the first byte of the first element | 208 // extra element and then use the first byte of the first element |
| 212 // to mark the allocation type. | 209 // to mark the allocation type. |
| 213 count++; | 210 count++; |
| 214 | 211 |
| 215 if (count > max_size()) | 212 if (count > max_size()) |
| 216 throw std::length_error("overflow"); | 213 return NULL; |
| 217 | 214 |
| 218 size_type bytes = count * sizeof(T); | 215 size_type bytes = count * sizeof(T); |
| 219 uint8* mem = NULL; | 216 uint8* mem = NULL; |
| 220 | 217 |
| 221 // First see if we can do this allocation on the heap. | 218 // First see if we can do this allocation on the heap. |
| 222 if (count < kMaxHeapAllocationSize) | 219 if (count < kMaxHeapAllocationSize) |
| 223 mem = new(std::nothrow) uint8[bytes]; | 220 mem = new(std::nothrow) uint8[bytes]; |
| 224 if (mem != NULL) { | 221 if (mem != NULL) { |
| 225 mem[0] = static_cast<uint8>(HEAP_ALLOCATION); | 222 mem[0] = static_cast<uint8>(HEAP_ALLOCATION); |
| 226 } else { | 223 } else { |
| 227 // If either the heap allocation failed or the request exceeds the | 224 // If either the heap allocation failed or the request exceeds the |
| 228 // max heap allocation threshold, we back the allocation with a temp file. | 225 // max heap allocation threshold, we back the allocation with a temp file. |
| 229 TempMapping* mapping = new TempMapping(); | 226 TempMapping* mapping = new(std::nothrow) TempMapping(); |
| 230 mapping->Initialize(bytes); | 227 if (mapping && mapping->Initialize(bytes)) { |
| 231 mem = reinterpret_cast<uint8*>(mapping->memory()); | 228 mem = reinterpret_cast<uint8*>(mapping->memory()); |
| 232 mem[0] = static_cast<uint8>(FILE_ALLOCATION); | 229 mem[0] = static_cast<uint8>(FILE_ALLOCATION); |
| 230 } |
| 233 } | 231 } |
| 234 return reinterpret_cast<pointer>(mem + sizeof(T)); | 232 return mem ? reinterpret_cast<pointer>(mem + sizeof(T)) : NULL; |
| 235 } | 233 } |
| 236 | 234 |
| 237 pointer allocate(size_type count, const void* hint) { | 235 pointer allocate(size_type count, const void* hint) { |
| 238 return allocate(count); | 236 return allocate(count); |
| 239 } | 237 } |
| 240 | 238 |
| 241 void construct(pointer ptr, const T& value) { | 239 void construct(pointer ptr, const T& value) { |
| 242 ::new(ptr) T(value); | 240 ::new(ptr) T(value); |
| 243 } | 241 } |
| 244 | 242 |
| 245 void destroy(pointer ptr) { | 243 void destroy(pointer ptr) { |
| 246 ptr->~T(); | 244 ptr->~T(); |
| 247 } | 245 } |
| 248 | 246 |
| 249 size_t max_size() const _THROW0() { | 247 size_type max_size() const _THROW0() { |
| 250 size_type count = static_cast<size_type>(-1) / sizeof(T); | 248 size_type count = static_cast<size_type>(-1) / sizeof(T); |
| 251 return (0 < count ? count : 1); | 249 return (0 < count ? count : 1); |
| 252 } | 250 } |
| 253 }; | 251 }; |
| 254 | 252 |
| 255 #else // OS_WIN | 253 #else // OS_WIN |
| 256 | 254 |
| 257 // On Mac, Linux, we just use the default STL allocator. | 255 // On Mac, Linux, we use a bare bones implementation that only does |
| 256 // heap allocations. |
| 258 template<class T> | 257 template<class T> |
| 259 class MemoryAllocator : public std::allocator<T> { | 258 class MemoryAllocator { |
| 260 public: | 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; |
| 267 |
| 268 template<class OtherT> |
| 269 struct rebind { |
| 270 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT> |
| 271 typedef MemoryAllocator<OtherT> other; |
| 272 }; |
| 273 |
| 274 MemoryAllocator() { |
| 275 } |
| 276 |
| 277 explicit MemoryAllocator(const MemoryAllocator<T>& other) { |
| 278 } |
| 279 |
| 280 template<class OtherT> |
| 281 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) { |
| 282 } |
| 283 |
| 284 ~MemoryAllocator() { |
| 285 } |
| 286 |
| 287 void deallocate(pointer ptr, size_type size) { |
| 288 delete [] ptr; |
| 289 } |
| 290 |
| 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)]); |
| 296 } |
| 297 |
| 298 pointer allocate(size_type count, const void* hint) { |
| 299 return allocate(count); |
| 300 } |
| 301 |
| 302 void construct(pointer ptr, const T& value) { |
| 303 ::new(ptr) T(value); |
| 304 } |
| 305 |
| 306 void destroy(pointer ptr) { |
| 307 ptr->~T(); |
| 308 } |
| 309 |
| 310 size_type max_size() const { |
| 311 size_type count = static_cast<size_type>(-1) / sizeof(T); |
| 312 return (0 < count ? count : 1); |
| 313 } |
| 261 }; | 314 }; |
| 262 | 315 |
| 263 #endif // OS_WIN | 316 #endif // OS_WIN |
| 264 | 317 |
| 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); |
| 329 |
| 330 NoThrowBuffer() : buffer_(NULL), size_(0), alloc_size_(0) { |
| 331 } |
| 332 |
| 333 ~NoThrowBuffer() { |
| 334 clear(); |
| 335 } |
| 336 |
| 337 void clear() { |
| 338 if (buffer_) { |
| 339 alloc_.deallocate(buffer_, alloc_size_); |
| 340 buffer_ = NULL; |
| 341 size_ = 0; |
| 342 alloc_size_ = 0; |
| 343 } |
| 344 } |
| 345 |
| 346 bool empty() const { |
| 347 return size_ == 0; |
| 348 } |
| 349 |
| 350 CheckBool reserve(size_t size) WARN_UNUSED_RESULT { |
| 351 if (failed()) |
| 352 return false; |
| 353 |
| 354 if (size <= alloc_size_) |
| 355 return true; |
| 356 |
| 357 if (size < kStartSize) |
| 358 size = kStartSize; |
| 359 |
| 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_); |
| 368 } |
| 369 buffer_ = new_buffer; |
| 370 alloc_size_ = size; |
| 371 } |
| 372 |
| 373 return !failed(); |
| 374 } |
| 375 |
| 376 CheckBool append(const T* data, size_t size) WARN_UNUSED_RESULT { |
| 377 if (failed()) |
| 378 return false; |
| 379 |
| 380 if (size > alloc_.max_size() - size_) |
| 381 return false; |
| 382 |
| 383 if (!size) |
| 384 return true; |
| 385 |
| 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; |
| 394 } |
| 395 } |
| 396 if (!reserve(new_size)) |
| 397 return false; |
| 398 } |
| 399 |
| 400 memcpy(buffer_ + size_, data, size * sizeof(T)); |
| 401 size_ += size; |
| 402 |
| 403 return true; |
| 404 } |
| 405 |
| 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. |
| 415 } |
| 416 |
| 417 size_ = size; |
| 418 |
| 419 return true; |
| 420 } |
| 421 |
| 422 CheckBool push_back(const T& item) WARN_UNUSED_RESULT { |
| 423 return append(&item, 1); |
| 424 } |
| 425 |
| 426 const T& back() const { |
| 427 return buffer_[size_ - 1]; |
| 428 } |
| 429 |
| 430 T& back() { |
| 431 return buffer_[size_ - 1]; |
| 432 } |
| 433 |
| 434 const T* begin() const { |
| 435 if (!size_) |
| 436 return NULL; |
| 437 return &buffer_[0]; |
| 438 } |
| 439 |
| 440 T* begin() { |
| 441 if (!size_) |
| 442 return NULL; |
| 443 return &buffer_[0]; |
| 444 } |
| 445 |
| 446 const T* end() const { |
| 447 if (!size_) |
| 448 return NULL; |
| 449 return &buffer_[size_ - 1]; |
| 450 } |
| 451 |
| 452 T* end() { |
| 453 if (!size_) |
| 454 return NULL; |
| 455 return &buffer_[size_ - 1]; |
| 456 } |
| 457 |
| 458 const T& operator[](size_t index) const { |
| 459 DCHECK(index < size_); |
| 460 return buffer_[index]; |
| 461 } |
| 462 |
| 463 T& operator[](size_t index) { |
| 464 DCHECK(index < size_); |
| 465 return buffer_[index]; |
| 466 } |
| 467 |
| 468 size_t size() const { |
| 469 return size_; |
| 470 } |
| 471 |
| 472 T* data() const { |
| 473 return buffer_; |
| 474 } |
| 475 |
| 476 // Returns true if an allocation failure has ever occurred for this object. |
| 477 bool failed() const { |
| 478 return alloc_size_ == kAllocationFailure; |
| 479 } |
| 480 |
| 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_; |
| 486 }; |
| 487 |
| 265 } // namespace courgette | 488 } // namespace courgette |
| 266 | 489 |
| 267 #endif // COURGETTE_MEMORY_ALLOCATOR_H_ | 490 #endif // COURGETTE_MEMORY_ALLOCATOR_H_ |
| OLD | NEW |