1 // Copyright 2018 the V8 project 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 #ifndef V8_BASE_REGION_ALLOCATOR_H_ 6 #define V8_BASE_REGION_ALLOCATOR_H_ 7 8 #include <set> 9 10 #include "src/base/address-region.h" 11 #include "src/base/utils/random-number-generator.h" 12 #include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck 13 14 namespace v8 { 15 namespace base { 16 17 // Helper class for managing used/free regions within [address, address+size) 18 // region. Minimum allocation unit is |page_size|. Requested allocation size 19 // is rounded up to |page_size|. 20 // The region allocation algorithm implements best-fit with coalescing strategy: 21 // it tries to find a smallest suitable free region upon allocation and tries 22 // to merge region with its neighbors upon freeing. 23 // 24 // This class does not perform any actual region reservation. 25 // Not thread-safe. 26 class V8_BASE_EXPORT RegionAllocator final { 27 public: 28 using Address = uintptr_t; 29 30 static constexpr Address kAllocationFailure = static_cast<Address>(-1); 31 32 enum class RegionState { 33 // The region can be allocated from. 34 kFree, 35 // The region has been carved out of the wider area and is not allocatable. 36 kExcluded, 37 // The region has been allocated and is managed by a RegionAllocator. 38 kAllocated, 39 }; 40 41 RegionAllocator(Address address, size_t size, size_t page_size); 42 RegionAllocator(const RegionAllocator&) = delete; 43 RegionAllocator& operator=(const RegionAllocator&) = delete; 44 ~RegionAllocator(); 45 46 // Allocates region of |size| (must be |page_size|-aligned). Returns 47 // the address of the region on success or kAllocationFailure. 48 Address AllocateRegion(size_t size); 49 // Same as above but tries to randomize the region displacement. 50 Address AllocateRegion(RandomNumberGenerator* rng, size_t size); 51 52 // Allocates region of |size| at |requested_address| if it's free. Both the 53 // address and the size must be |page_size|-aligned. On success returns 54 // true. 55 // This kind of allocation is supposed to be used during setup phase to mark 56 // certain regions as used or for randomizing regions displacement. 57 // By default regions are marked as used, but can also be allocated as 58 // RegionState::kExcluded to prevent the RegionAllocator from using that 59 // memory range, which is useful when reserving any area to remap shared 60 // memory into. 61 bool AllocateRegionAt(Address requested_address, size_t size, 62 RegionState region_state = RegionState::kAllocated); 63 64 // Frees region at given |address|, returns the size of the region. 65 // There must be a used region starting at given address otherwise nothing 66 // will be freed and 0 will be returned. FreeRegion(Address address)67 size_t FreeRegion(Address address) { return TrimRegion(address, 0); } 68 69 // Decreases size of the previously allocated region at |address|, returns 70 // freed size. |new_size| must be |page_size|-aligned and 71 // less than or equal to current region's size. Setting new size to zero 72 // frees the region. 73 size_t TrimRegion(Address address, size_t new_size); 74 75 // If there is a used region starting at given address returns its size 76 // otherwise 0. 77 size_t CheckRegion(Address address); 78 79 // Returns true if there are no pages allocated in given region. 80 bool IsFree(Address address, size_t size); 81 begin()82 Address begin() const { return whole_region_.begin(); } end()83 Address end() const { return whole_region_.end(); } size()84 size_t size() const { return whole_region_.size(); } 85 contains(Address address)86 bool contains(Address address) const { 87 return whole_region_.contains(address); 88 } 89 contains(Address address,size_t size)90 bool contains(Address address, size_t size) const { 91 return whole_region_.contains(address, size); 92 } 93 94 // Total size of not yet aquired regions. free_size()95 size_t free_size() const { return free_size_; } 96 97 // The alignment of the allocated region's addresses and granularity of 98 // the allocated region's sizes. page_size()99 size_t page_size() const { return page_size_; } 100 101 void Print(std::ostream& os) const; 102 103 private: 104 class Region : public AddressRegion { 105 public: Region(Address address,size_t size,RegionState state)106 Region(Address address, size_t size, RegionState state) 107 : AddressRegion(address, size), state_(state) {} 108 is_free()109 bool is_free() const { return state_ == RegionState::kFree; } is_allocated()110 bool is_allocated() const { return state_ == RegionState::kAllocated; } is_excluded()111 bool is_excluded() const { return state_ == RegionState::kExcluded; } set_state(RegionState state)112 void set_state(RegionState state) { state_ = state; } 113 state()114 RegionState state() { return state_; } 115 116 void Print(std::ostream& os) const; 117 118 private: 119 RegionState state_; 120 }; 121 122 // The whole region. 123 const Region whole_region_; 124 125 // Number of |page_size_| in the whole region. 126 const size_t region_size_in_pages_; 127 128 // If the free size is less than this value - stop trying to randomize the 129 // allocation addresses. 130 const size_t max_load_for_randomization_; 131 132 // Size of all free regions. 133 size_t free_size_; 134 135 // Minimum region size. Must be a pow of 2. 136 const size_t page_size_; 137 138 struct AddressEndOrder { operatorAddressEndOrder139 bool operator()(const Region* a, const Region* b) const { 140 return a->end() < b->end(); 141 } 142 }; 143 // All regions ordered by addresses. 144 using AllRegionsSet = std::set<Region*, AddressEndOrder>; 145 AllRegionsSet all_regions_; 146 147 struct SizeAddressOrder { operatorSizeAddressOrder148 bool operator()(const Region* a, const Region* b) const { 149 if (a->size() != b->size()) return a->size() < b->size(); 150 return a->begin() < b->begin(); 151 } 152 }; 153 // Free regions ordered by sizes and addresses. 154 std::set<Region*, SizeAddressOrder> free_regions_; 155 156 // Returns region containing given address or nullptr. 157 AllRegionsSet::iterator FindRegion(Address address); 158 159 // Adds given region to the set of free regions. 160 void FreeListAddRegion(Region* region); 161 162 // Finds best-fit free region for given size. 163 Region* FreeListFindRegion(size_t size); 164 165 // Removes given region from the set of free regions. 166 void FreeListRemoveRegion(Region* region); 167 168 // Splits given |region| into two: one of |new_size| size and a new one 169 // having the rest. The new region is returned. 170 Region* Split(Region* region, size_t new_size); 171 172 // For two coalescing regions merges |next| to |prev| and deletes |next|. 173 void Merge(AllRegionsSet::iterator prev_iter, 174 AllRegionsSet::iterator next_iter); 175 176 FRIEND_TEST(RegionAllocatorTest, AllocateExcluded); 177 FRIEND_TEST(RegionAllocatorTest, AllocateRegionRandom); 178 FRIEND_TEST(RegionAllocatorTest, Contains); 179 FRIEND_TEST(RegionAllocatorTest, FindRegion); 180 FRIEND_TEST(RegionAllocatorTest, Fragmentation); 181 }; 182 183 } // namespace base 184 } // namespace v8 185 186 #endif // V8_BASE_REGION_ALLOCATOR_H_ 187