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 |