| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project 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 V8_SLOT_SET_H | 5 #ifndef V8_SLOT_SET_H |
| 6 #define V8_SLOT_SET_H | 6 #define V8_SLOT_SET_H |
| 7 | 7 |
| 8 #include <stack> | 8 #include <stack> |
| 9 | 9 |
| 10 #include "src/allocation.h" | 10 #include "src/allocation.h" |
| 11 #include "src/base/atomic-utils.h" | 11 #include "src/base/atomic-utils.h" |
| 12 #include "src/base/bits.h" | 12 #include "src/base/bits.h" |
| 13 #include "src/utils.h" | 13 #include "src/utils.h" |
| 14 | 14 |
| 15 namespace v8 { | 15 namespace v8 { |
| 16 namespace internal { | 16 namespace internal { |
| 17 | 17 |
| 18 enum SlotCallbackResult { KEEP_SLOT, REMOVE_SLOT }; | 18 enum SlotCallbackResult { KEEP_SLOT, REMOVE_SLOT }; |
| 19 | 19 |
| 20 // Data structure for maintaining a set of slots in a standard (non-large) | 20 // Data structure for maintaining a set of slots in a standard (non-large) |
| 21 // page. The base address of the page must be set with SetPageStart before any | 21 // page. The base address of the page must be set with SetPageStart before any |
| 22 // operation. | 22 // operation. |
| 23 // The data structure assumes that the slots are pointer size aligned and | 23 // The data structure assumes that the slots are pointer size aligned and |
| 24 // splits the valid slot offset range into kBuckets buckets. | 24 // splits the valid slot offset range into kBuckets buckets. |
| 25 // Each bucket is a bitmap with a bit corresponding to a single slot offset. | 25 // Each bucket is a bitmap with a bit corresponding to a single slot offset. |
| 26 class SlotSet : public Malloced { | 26 class SlotSet : public Malloced { |
| 27 public: | 27 public: |
| 28 enum IterationMode { PREFREE_EMPTY_BUCKETS, KEEP_EMPTY_BUCKETS }; | 28 enum EmptyBucketMode { |
| 29 FREE_EMPTY_BUCKETS, // An empty bucket will be deallocated immediately. |
| 30 PREFREE_EMPTY_BUCKETS, // An empty bucket will be unlinked from the slot |
| 31 // set, but deallocated on demand by a sweeper |
| 32 // thread. |
| 33 KEEP_EMPTY_BUCKETS // An empty bucket will be kept. |
| 34 }; |
| 29 | 35 |
| 30 SlotSet() { | 36 SlotSet() { |
| 31 for (int i = 0; i < kBuckets; i++) { | 37 for (int i = 0; i < kBuckets; i++) { |
| 32 bucket[i].SetValue(nullptr); | 38 bucket[i].SetValue(nullptr); |
| 33 } | 39 } |
| 34 } | 40 } |
| 35 | 41 |
| 36 ~SlotSet() { | 42 ~SlotSet() { |
| 37 for (int i = 0; i < kBuckets; i++) { | 43 for (int i = 0; i < kBuckets; i++) { |
| 38 ReleaseBucket(i); | 44 ReleaseBucket(i); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 67 uint32_t cell = current_bucket[cell_index].Value(); | 73 uint32_t cell = current_bucket[cell_index].Value(); |
| 68 if (cell) { | 74 if (cell) { |
| 69 uint32_t bit_mask = 1u << bit_index; | 75 uint32_t bit_mask = 1u << bit_index; |
| 70 if (cell & bit_mask) { | 76 if (cell & bit_mask) { |
| 71 current_bucket[cell_index].ClearBit(bit_index); | 77 current_bucket[cell_index].ClearBit(bit_index); |
| 72 } | 78 } |
| 73 } | 79 } |
| 74 } | 80 } |
| 75 } | 81 } |
| 76 | 82 |
| 83 void PreFreeEmptyBucket(int bucket_index) { |
| 84 base::LockGuard<base::Mutex> guard(&to_be_freed_buckets_mutex_); |
| 85 base::AtomicValue<uint32_t>* bucket_ptr = bucket[bucket_index].Value(); |
| 86 to_be_freed_buckets_.push(bucket_ptr); |
| 87 bucket[bucket_index].SetValue(nullptr); |
| 88 } |
| 89 |
| 77 // The slot offsets specify a range of slots at addresses: | 90 // The slot offsets specify a range of slots at addresses: |
| 78 // [page_start_ + start_offset ... page_start_ + end_offset). | 91 // [page_start_ + start_offset ... page_start_ + end_offset). |
| 79 void RemoveRange(int start_offset, int end_offset) { | 92 void RemoveRange(int start_offset, int end_offset, EmptyBucketMode mode) { |
| 80 CHECK_LE(end_offset, 1 << kPageSizeBits); | 93 CHECK_LE(end_offset, 1 << kPageSizeBits); |
| 81 DCHECK_LE(start_offset, end_offset); | 94 DCHECK_LE(start_offset, end_offset); |
| 82 int start_bucket, start_cell, start_bit; | 95 int start_bucket, start_cell, start_bit; |
| 83 SlotToIndices(start_offset, &start_bucket, &start_cell, &start_bit); | 96 SlotToIndices(start_offset, &start_bucket, &start_cell, &start_bit); |
| 84 int end_bucket, end_cell, end_bit; | 97 int end_bucket, end_cell, end_bit; |
| 85 SlotToIndices(end_offset, &end_bucket, &end_cell, &end_bit); | 98 SlotToIndices(end_offset, &end_bucket, &end_cell, &end_bit); |
| 86 uint32_t start_mask = (1u << start_bit) - 1; | 99 uint32_t start_mask = (1u << start_bit) - 1; |
| 87 uint32_t end_mask = ~((1u << end_bit) - 1); | 100 uint32_t end_mask = ~((1u << end_bit) - 1); |
| 88 if (start_bucket == end_bucket && start_cell == end_cell) { | 101 if (start_bucket == end_bucket && start_cell == end_cell) { |
| 89 ClearCell(start_bucket, start_cell, ~(start_mask | end_mask)); | 102 ClearCell(start_bucket, start_cell, ~(start_mask | end_mask)); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 101 } | 114 } |
| 102 } | 115 } |
| 103 // The rest of the current bucket is cleared. | 116 // The rest of the current bucket is cleared. |
| 104 // Move on to the next bucket. | 117 // Move on to the next bucket. |
| 105 current_bucket++; | 118 current_bucket++; |
| 106 current_cell = 0; | 119 current_cell = 0; |
| 107 } | 120 } |
| 108 DCHECK(current_bucket == end_bucket || | 121 DCHECK(current_bucket == end_bucket || |
| 109 (current_bucket < end_bucket && current_cell == 0)); | 122 (current_bucket < end_bucket && current_cell == 0)); |
| 110 while (current_bucket < end_bucket) { | 123 while (current_bucket < end_bucket) { |
| 111 ReleaseBucket(current_bucket); | 124 if (mode == PREFREE_EMPTY_BUCKETS) { |
| 125 PreFreeEmptyBucket(current_bucket); |
| 126 } else if (mode == FREE_EMPTY_BUCKETS) { |
| 127 ReleaseBucket(current_bucket); |
| 128 } |
| 112 current_bucket++; | 129 current_bucket++; |
| 113 } | 130 } |
| 114 // All buckets between start_bucket and end_bucket are cleared. | 131 // All buckets between start_bucket and end_bucket are cleared. |
| 115 DCHECK(current_bucket == end_bucket && current_cell <= end_cell); | 132 DCHECK(current_bucket == end_bucket && current_cell <= end_cell); |
| 116 if (current_bucket == kBuckets || | 133 if (current_bucket == kBuckets || |
| 117 bucket[current_bucket].Value() == nullptr) { | 134 bucket[current_bucket].Value() == nullptr) { |
| 118 return; | 135 return; |
| 119 } | 136 } |
| 120 while (current_cell < end_cell) { | 137 while (current_cell < end_cell) { |
| 121 bucket[current_bucket].Value()[current_cell].SetValue(0); | 138 bucket[current_bucket].Value()[current_cell].SetValue(0); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 141 // If the callback returns REMOVE_SLOT then the slot is removed from the set. | 158 // If the callback returns REMOVE_SLOT then the slot is removed from the set. |
| 142 // Returns the new number of slots. | 159 // Returns the new number of slots. |
| 143 // This method should only be called on the main thread. | 160 // This method should only be called on the main thread. |
| 144 // | 161 // |
| 145 // Sample usage: | 162 // Sample usage: |
| 146 // Iterate([](Address slot_address) { | 163 // Iterate([](Address slot_address) { |
| 147 // if (good(slot_address)) return KEEP_SLOT; | 164 // if (good(slot_address)) return KEEP_SLOT; |
| 148 // else return REMOVE_SLOT; | 165 // else return REMOVE_SLOT; |
| 149 // }); | 166 // }); |
| 150 template <typename Callback> | 167 template <typename Callback> |
| 151 int Iterate(Callback callback, IterationMode mode) { | 168 int Iterate(Callback callback, EmptyBucketMode mode) { |
| 152 int new_count = 0; | 169 int new_count = 0; |
| 153 for (int bucket_index = 0; bucket_index < kBuckets; bucket_index++) { | 170 for (int bucket_index = 0; bucket_index < kBuckets; bucket_index++) { |
| 154 if (bucket[bucket_index].Value() != nullptr) { | 171 if (bucket[bucket_index].Value() != nullptr) { |
| 155 int in_bucket_count = 0; | 172 int in_bucket_count = 0; |
| 156 base::AtomicValue<uint32_t>* current_bucket = | 173 base::AtomicValue<uint32_t>* current_bucket = |
| 157 bucket[bucket_index].Value(); | 174 bucket[bucket_index].Value(); |
| 158 int cell_offset = bucket_index * kBitsPerBucket; | 175 int cell_offset = bucket_index * kBitsPerBucket; |
| 159 for (int i = 0; i < kCellsPerBucket; i++, cell_offset += kBitsPerCell) { | 176 for (int i = 0; i < kCellsPerBucket; i++, cell_offset += kBitsPerCell) { |
| 160 if (current_bucket[i].Value()) { | 177 if (current_bucket[i].Value()) { |
| 161 uint32_t cell = current_bucket[i].Value(); | 178 uint32_t cell = current_bucket[i].Value(); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 179 // computed value, and retry. We can do this, because this | 196 // computed value, and retry. We can do this, because this |
| 180 // method will only be called on the main thread and filtering | 197 // method will only be called on the main thread and filtering |
| 181 // threads will only remove slots. | 198 // threads will only remove slots. |
| 182 old_cell = current_bucket[i].Value(); | 199 old_cell = current_bucket[i].Value(); |
| 183 new_cell &= old_cell; | 200 new_cell &= old_cell; |
| 184 } | 201 } |
| 185 } | 202 } |
| 186 } | 203 } |
| 187 } | 204 } |
| 188 if (mode == PREFREE_EMPTY_BUCKETS && in_bucket_count == 0) { | 205 if (mode == PREFREE_EMPTY_BUCKETS && in_bucket_count == 0) { |
| 189 base::LockGuard<base::Mutex> guard(&to_be_freed_buckets_mutex_); | 206 PreFreeEmptyBucket(bucket_index); |
| 190 base::AtomicValue<uint32_t>* bucket_ptr = | |
| 191 bucket[bucket_index].Value(); | |
| 192 to_be_freed_buckets_.push(bucket_ptr); | |
| 193 bucket[bucket_index].SetValue(nullptr); | |
| 194 } | 207 } |
| 195 new_count += in_bucket_count; | 208 new_count += in_bucket_count; |
| 196 } | 209 } |
| 197 } | 210 } |
| 198 return new_count; | 211 return new_count; |
| 199 } | 212 } |
| 200 | 213 |
| 201 void FreeToBeFreedBuckets() { | 214 void FreeToBeFreedBuckets() { |
| 202 base::LockGuard<base::Mutex> guard(&to_be_freed_buckets_mutex_); | 215 base::LockGuard<base::Mutex> guard(&to_be_freed_buckets_mutex_); |
| 203 while (!to_be_freed_buckets_.empty()) { | 216 while (!to_be_freed_buckets_.empty()) { |
| (...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 467 Address page_start_; | 480 Address page_start_; |
| 468 base::AtomicValue<Chunk*> chunk_; | 481 base::AtomicValue<Chunk*> chunk_; |
| 469 base::Mutex to_be_freed_chunks_mutex_; | 482 base::Mutex to_be_freed_chunks_mutex_; |
| 470 std::stack<Chunk*> to_be_freed_chunks_; | 483 std::stack<Chunk*> to_be_freed_chunks_; |
| 471 }; | 484 }; |
| 472 | 485 |
| 473 } // namespace internal | 486 } // namespace internal |
| 474 } // namespace v8 | 487 } // namespace v8 |
| 475 | 488 |
| 476 #endif // V8_SLOT_SET_H | 489 #endif // V8_SLOT_SET_H |
| OLD | NEW |