51 static const void* POISON_PTR = (void*)0xffbadbac; |
51 static const void* POISON_PTR = (void*)0xffbadbac; |
52 #endif |
52 #endif |
53 #endif |
53 #endif |
54 |
54 |
55 // Node |
55 // Node |
56 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
56 template <typename CONFIG, MEMFLAGS F> |
57 inline typename ConcurrentHashTable<VALUE, CONFIG, F>::Node* |
57 inline typename ConcurrentHashTable<CONFIG, F>::Node* |
58 ConcurrentHashTable<VALUE, CONFIG, F>:: |
58 ConcurrentHashTable<CONFIG, F>:: |
59 Node::next() const |
59 Node::next() const |
60 { |
60 { |
61 return OrderAccess::load_acquire(&_next); |
61 return OrderAccess::load_acquire(&_next); |
62 } |
62 } |
63 |
63 |
64 // Bucket |
64 // Bucket |
65 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
65 template <typename CONFIG, MEMFLAGS F> |
66 inline typename ConcurrentHashTable<VALUE, CONFIG, F>::Node* |
66 inline typename ConcurrentHashTable<CONFIG, F>::Node* |
67 ConcurrentHashTable<VALUE, CONFIG, F>:: |
67 ConcurrentHashTable<CONFIG, F>:: |
68 Bucket::first_raw() const |
68 Bucket::first_raw() const |
69 { |
69 { |
70 return OrderAccess::load_acquire(&_first); |
70 return OrderAccess::load_acquire(&_first); |
71 } |
71 } |
72 |
72 |
73 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
73 template <typename CONFIG, MEMFLAGS F> |
74 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
74 inline void ConcurrentHashTable<CONFIG, F>:: |
75 Bucket::release_assign_node_ptr( |
75 Bucket::release_assign_node_ptr( |
76 typename ConcurrentHashTable<VALUE, CONFIG, F>::Node* const volatile * dst, |
76 typename ConcurrentHashTable<CONFIG, F>::Node* const volatile * dst, |
77 typename ConcurrentHashTable<VALUE, CONFIG, F>::Node* node) const |
77 typename ConcurrentHashTable<CONFIG, F>::Node* node) const |
78 { |
78 { |
79 // Due to this assert this methods is not static. |
79 // Due to this assert this methods is not static. |
80 assert(is_locked(), "Must be locked."); |
80 assert(is_locked(), "Must be locked."); |
81 Node** tmp = (Node**)dst; |
81 Node** tmp = (Node**)dst; |
82 OrderAccess::release_store(tmp, clear_set_state(node, *dst)); |
82 OrderAccess::release_store(tmp, clear_set_state(node, *dst)); |
83 } |
83 } |
84 |
84 |
85 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
85 template <typename CONFIG, MEMFLAGS F> |
86 inline typename ConcurrentHashTable<VALUE, CONFIG, F>::Node* |
86 inline typename ConcurrentHashTable<CONFIG, F>::Node* |
87 ConcurrentHashTable<VALUE, CONFIG, F>:: |
87 ConcurrentHashTable<CONFIG, F>:: |
88 Bucket::first() const |
88 Bucket::first() const |
89 { |
89 { |
90 // We strip the states bit before returning the ptr. |
90 // We strip the states bit before returning the ptr. |
91 return clear_state(OrderAccess::load_acquire(&_first)); |
91 return clear_state(OrderAccess::load_acquire(&_first)); |
92 } |
92 } |
93 |
93 |
94 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
94 template <typename CONFIG, MEMFLAGS F> |
95 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
95 inline bool ConcurrentHashTable<CONFIG, F>:: |
96 Bucket::have_redirect() const |
96 Bucket::have_redirect() const |
97 { |
97 { |
98 return is_state(first_raw(), STATE_REDIRECT_BIT); |
98 return is_state(first_raw(), STATE_REDIRECT_BIT); |
99 } |
99 } |
100 |
100 |
101 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
101 template <typename CONFIG, MEMFLAGS F> |
102 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
102 inline bool ConcurrentHashTable<CONFIG, F>:: |
103 Bucket::is_locked() const |
103 Bucket::is_locked() const |
104 { |
104 { |
105 return is_state(first_raw(), STATE_LOCK_BIT); |
105 return is_state(first_raw(), STATE_LOCK_BIT); |
106 } |
106 } |
107 |
107 |
108 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
108 template <typename CONFIG, MEMFLAGS F> |
109 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
109 inline void ConcurrentHashTable<CONFIG, F>:: |
110 Bucket::lock() |
110 Bucket::lock() |
111 { |
111 { |
112 int i = 0; |
112 int i = 0; |
113 // SpinYield would be unfair here |
113 // SpinYield would be unfair here |
114 while (!this->trylock()) { |
114 while (!this->trylock()) { |
121 SpinPause(); |
121 SpinPause(); |
122 } |
122 } |
123 } |
123 } |
124 } |
124 } |
125 |
125 |
126 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
126 template <typename CONFIG, MEMFLAGS F> |
127 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
127 inline void ConcurrentHashTable<CONFIG, F>:: |
128 Bucket::release_assign_last_node_next( |
128 Bucket::release_assign_last_node_next( |
129 typename ConcurrentHashTable<VALUE, CONFIG, F>::Node* node) |
129 typename ConcurrentHashTable<CONFIG, F>::Node* node) |
130 { |
130 { |
131 assert(is_locked(), "Must be locked."); |
131 assert(is_locked(), "Must be locked."); |
132 Node* const volatile * ret = first_ptr(); |
132 Node* const volatile * ret = first_ptr(); |
133 while (clear_state(*ret) != NULL) { |
133 while (clear_state(*ret) != NULL) { |
134 ret = clear_state(*ret)->next_ptr(); |
134 ret = clear_state(*ret)->next_ptr(); |
135 } |
135 } |
136 release_assign_node_ptr(ret, node); |
136 release_assign_node_ptr(ret, node); |
137 } |
137 } |
138 |
138 |
139 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
139 template <typename CONFIG, MEMFLAGS F> |
140 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
140 inline bool ConcurrentHashTable<CONFIG, F>:: |
141 Bucket::cas_first(typename ConcurrentHashTable<VALUE, CONFIG, F>::Node* node, |
141 Bucket::cas_first(typename ConcurrentHashTable<CONFIG, F>::Node* node, |
142 typename ConcurrentHashTable<VALUE, CONFIG, F>::Node* expect |
142 typename ConcurrentHashTable<CONFIG, F>::Node* expect |
143 ) |
143 ) |
144 { |
144 { |
145 if (is_locked()) { |
145 if (is_locked()) { |
146 return false; |
146 return false; |
147 } |
147 } |
164 return true; |
164 return true; |
165 } |
165 } |
166 return false; |
166 return false; |
167 } |
167 } |
168 |
168 |
169 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
169 template <typename CONFIG, MEMFLAGS F> |
170 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
170 inline void ConcurrentHashTable<CONFIG, F>:: |
171 Bucket::unlock() |
171 Bucket::unlock() |
172 { |
172 { |
173 assert(is_locked(), "Must be locked."); |
173 assert(is_locked(), "Must be locked."); |
174 assert(!have_redirect(), |
174 assert(!have_redirect(), |
175 "Unlocking a bucket after it has reached terminal state."); |
175 "Unlocking a bucket after it has reached terminal state."); |
176 OrderAccess::release_store(&_first, clear_state(first())); |
176 OrderAccess::release_store(&_first, clear_state(first())); |
177 } |
177 } |
178 |
178 |
179 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
179 template <typename CONFIG, MEMFLAGS F> |
180 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
180 inline void ConcurrentHashTable<CONFIG, F>:: |
181 Bucket::redirect() |
181 Bucket::redirect() |
182 { |
182 { |
183 assert(is_locked(), "Must be locked."); |
183 assert(is_locked(), "Must be locked."); |
184 OrderAccess::release_store(&_first, set_state(_first, STATE_REDIRECT_BIT)); |
184 OrderAccess::release_store(&_first, set_state(_first, STATE_REDIRECT_BIT)); |
185 } |
185 } |
186 |
186 |
187 // InternalTable |
187 // InternalTable |
188 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
188 template <typename CONFIG, MEMFLAGS F> |
189 inline ConcurrentHashTable<VALUE, CONFIG, F>:: |
189 inline ConcurrentHashTable<CONFIG, F>:: |
190 InternalTable::InternalTable(size_t log2_size) |
190 InternalTable::InternalTable(size_t log2_size) |
191 : _log2_size(log2_size), _size(((size_t)1ul) << _log2_size), |
191 : _log2_size(log2_size), _size(((size_t)1ul) << _log2_size), |
192 _hash_mask(~(~((size_t)0) << _log2_size)) |
192 _hash_mask(~(~((size_t)0) << _log2_size)) |
193 { |
193 { |
194 assert(_log2_size >= SIZE_SMALL_LOG2 && _log2_size <= SIZE_BIG_LOG2, |
194 assert(_log2_size >= SIZE_SMALL_LOG2 && _log2_size <= SIZE_BIG_LOG2, |
199 for (size_t i = 0; i < _size; ++i) { |
199 for (size_t i = 0; i < _size; ++i) { |
200 new (_buckets + i) Bucket(); |
200 new (_buckets + i) Bucket(); |
201 } |
201 } |
202 } |
202 } |
203 |
203 |
204 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
204 template <typename CONFIG, MEMFLAGS F> |
205 inline ConcurrentHashTable<VALUE, CONFIG, F>:: |
205 inline ConcurrentHashTable<CONFIG, F>:: |
206 InternalTable::~InternalTable() |
206 InternalTable::~InternalTable() |
207 { |
207 { |
208 FREE_C_HEAP_ARRAY(Bucket, _buckets); |
208 FREE_C_HEAP_ARRAY(Bucket, _buckets); |
209 } |
209 } |
210 |
210 |
211 // ScopedCS |
211 // ScopedCS |
212 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
212 template <typename CONFIG, MEMFLAGS F> |
213 inline ConcurrentHashTable<VALUE, CONFIG, F>:: |
213 inline ConcurrentHashTable<CONFIG, F>:: |
214 ScopedCS::ScopedCS(Thread* thread, ConcurrentHashTable<VALUE, CONFIG, F>* cht) |
214 ScopedCS::ScopedCS(Thread* thread, ConcurrentHashTable<CONFIG, F>* cht) |
215 : _thread(thread), |
215 : _thread(thread), |
216 _cht(cht), |
216 _cht(cht), |
217 _cs_context(GlobalCounter::critical_section_begin(_thread)) |
217 _cs_context(GlobalCounter::critical_section_begin(_thread)) |
218 { |
218 { |
219 // This version is published now. |
219 // This version is published now. |
220 if (OrderAccess::load_acquire(&_cht->_invisible_epoch) != NULL) { |
220 if (OrderAccess::load_acquire(&_cht->_invisible_epoch) != NULL) { |
221 OrderAccess::release_store_fence(&_cht->_invisible_epoch, (Thread*)NULL); |
221 OrderAccess::release_store_fence(&_cht->_invisible_epoch, (Thread*)NULL); |
222 } |
222 } |
223 } |
223 } |
224 |
224 |
225 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
225 template <typename CONFIG, MEMFLAGS F> |
226 inline ConcurrentHashTable<VALUE, CONFIG, F>:: |
226 inline ConcurrentHashTable<CONFIG, F>:: |
227 ScopedCS::~ScopedCS() |
227 ScopedCS::~ScopedCS() |
228 { |
228 { |
229 GlobalCounter::critical_section_end(_thread, _cs_context); |
229 GlobalCounter::critical_section_end(_thread, _cs_context); |
230 } |
230 } |
231 |
231 |
232 // BaseConfig |
232 template <typename CONFIG, MEMFLAGS F> |
233 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
|
234 inline void* ConcurrentHashTable<VALUE, CONFIG, F>:: |
|
235 BaseConfig::allocate_node(size_t size, const VALUE& value) |
|
236 { |
|
237 return AllocateHeap(size, F); |
|
238 } |
|
239 |
|
240 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
|
241 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
|
242 BaseConfig::free_node(void* memory, const VALUE& value) |
|
243 { |
|
244 FreeHeap(memory); |
|
245 } |
|
246 |
|
247 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
|
248 template <typename LOOKUP_FUNC> |
233 template <typename LOOKUP_FUNC> |
249 inline VALUE* ConcurrentHashTable<VALUE, CONFIG, F>:: |
234 inline typename CONFIG::Value* ConcurrentHashTable<CONFIG, F>:: |
250 MultiGetHandle::get(LOOKUP_FUNC& lookup_f, bool* grow_hint) |
235 MultiGetHandle::get(LOOKUP_FUNC& lookup_f, bool* grow_hint) |
251 { |
236 { |
252 return ScopedCS::_cht->internal_get(ScopedCS::_thread, lookup_f, grow_hint); |
237 return ScopedCS::_cht->internal_get(ScopedCS::_thread, lookup_f, grow_hint); |
253 } |
238 } |
254 |
239 |
255 // HaveDeletables |
240 // HaveDeletables |
256 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
241 template <typename CONFIG, MEMFLAGS F> |
257 template <typename EVALUATE_FUNC> |
242 template <typename EVALUATE_FUNC> |
258 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
243 inline bool ConcurrentHashTable<CONFIG, F>:: |
259 HaveDeletables<true, EVALUATE_FUNC>::have_deletable(Bucket* bucket, |
244 HaveDeletables<true, EVALUATE_FUNC>::have_deletable(Bucket* bucket, |
260 EVALUATE_FUNC& eval_f, |
245 EVALUATE_FUNC& eval_f, |
261 Bucket* prefetch_bucket) |
246 Bucket* prefetch_bucket) |
262 { |
247 { |
263 // Instantiated for pointer type (true), so we can use prefetch. |
248 // Instantiated for pointer type (true), so we can use prefetch. |
312 // A reader will zero this flag if it reads this/next version. |
297 // A reader will zero this flag if it reads this/next version. |
313 OrderAccess::release_store(&_invisible_epoch, thread); |
298 OrderAccess::release_store(&_invisible_epoch, thread); |
314 GlobalCounter::write_synchronize(); |
299 GlobalCounter::write_synchronize(); |
315 } |
300 } |
316 |
301 |
317 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
302 template <typename CONFIG, MEMFLAGS F> |
318 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
303 inline bool ConcurrentHashTable<CONFIG, F>:: |
319 try_resize_lock(Thread* locker) |
304 try_resize_lock(Thread* locker) |
320 { |
305 { |
321 if (_resize_lock->try_lock()) { |
306 if (_resize_lock->try_lock()) { |
322 if (_resize_lock_owner != NULL) { |
307 if (_resize_lock_owner != NULL) { |
323 assert(locker != _resize_lock_owner, "Already own lock"); |
308 assert(locker != _resize_lock_owner, "Already own lock"); |
356 } while(true); |
341 } while(true); |
357 _resize_lock_owner = locker; |
342 _resize_lock_owner = locker; |
358 _invisible_epoch = 0; |
343 _invisible_epoch = 0; |
359 } |
344 } |
360 |
345 |
361 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
346 template <typename CONFIG, MEMFLAGS F> |
362 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
347 inline void ConcurrentHashTable<CONFIG, F>:: |
363 unlock_resize_lock(Thread* locker) |
348 unlock_resize_lock(Thread* locker) |
364 { |
349 { |
365 _invisible_epoch = 0; |
350 _invisible_epoch = 0; |
366 assert(locker == _resize_lock_owner, "Not unlocked by locker."); |
351 assert(locker == _resize_lock_owner, "Not unlocked by locker."); |
367 _resize_lock_owner = NULL; |
352 _resize_lock_owner = NULL; |
368 _resize_lock->unlock(); |
353 _resize_lock->unlock(); |
369 } |
354 } |
370 |
355 |
371 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
356 template <typename CONFIG, MEMFLAGS F> |
372 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
357 inline void ConcurrentHashTable<CONFIG, F>:: |
373 free_nodes() |
358 free_nodes() |
374 { |
359 { |
375 // We assume we are not MT during freeing. |
360 // We assume we are not MT during freeing. |
376 for (size_t node_it = 0; node_it < _table->_size; node_it++) { |
361 for (size_t node_it = 0; node_it < _table->_size; node_it++) { |
377 Bucket* bucket = _table->get_buckets() + node_it; |
362 Bucket* bucket = _table->get_buckets() + node_it; |
382 Node::destroy_node(free_node); |
367 Node::destroy_node(free_node); |
383 } |
368 } |
384 } |
369 } |
385 } |
370 } |
386 |
371 |
387 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
372 template <typename CONFIG, MEMFLAGS F> |
388 inline typename ConcurrentHashTable<VALUE, CONFIG, F>::InternalTable* |
373 inline typename ConcurrentHashTable<CONFIG, F>::InternalTable* |
389 ConcurrentHashTable<VALUE, CONFIG, F>:: |
374 ConcurrentHashTable<CONFIG, F>:: |
390 get_table() const |
375 get_table() const |
391 { |
376 { |
392 return OrderAccess::load_acquire(&_table); |
377 return OrderAccess::load_acquire(&_table); |
393 } |
378 } |
394 |
379 |
395 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
380 template <typename CONFIG, MEMFLAGS F> |
396 inline typename ConcurrentHashTable<VALUE, CONFIG, F>::InternalTable* |
381 inline typename ConcurrentHashTable<CONFIG, F>::InternalTable* |
397 ConcurrentHashTable<VALUE, CONFIG, F>:: |
382 ConcurrentHashTable<CONFIG, F>:: |
398 get_new_table() const |
383 get_new_table() const |
399 { |
384 { |
400 return OrderAccess::load_acquire(&_new_table); |
385 return OrderAccess::load_acquire(&_new_table); |
401 } |
386 } |
402 |
387 |
403 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
388 template <typename CONFIG, MEMFLAGS F> |
404 inline typename ConcurrentHashTable<VALUE, CONFIG, F>::InternalTable* |
389 inline typename ConcurrentHashTable<CONFIG, F>::InternalTable* |
405 ConcurrentHashTable<VALUE, CONFIG, F>:: |
390 ConcurrentHashTable<CONFIG, F>:: |
406 set_table_from_new() |
391 set_table_from_new() |
407 { |
392 { |
408 InternalTable* old_table = _table; |
393 InternalTable* old_table = _table; |
409 // Publish the new table. |
394 // Publish the new table. |
410 OrderAccess::release_store(&_table, _new_table); |
395 OrderAccess::release_store(&_table, _new_table); |
414 _new_table = NULL; |
399 _new_table = NULL; |
415 DEBUG_ONLY(_new_table = (InternalTable*)POISON_PTR;) |
400 DEBUG_ONLY(_new_table = (InternalTable*)POISON_PTR;) |
416 return old_table; |
401 return old_table; |
417 } |
402 } |
418 |
403 |
419 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
404 template <typename CONFIG, MEMFLAGS F> |
420 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
405 inline void ConcurrentHashTable<CONFIG, F>:: |
421 internal_grow_range(Thread* thread, size_t start, size_t stop) |
406 internal_grow_range(Thread* thread, size_t start, size_t stop) |
422 { |
407 { |
423 assert(stop <= _table->_size, "Outside backing array"); |
408 assert(stop <= _table->_size, "Outside backing array"); |
424 assert(_new_table != NULL, "Grow not proper setup before start"); |
409 assert(_new_table != NULL, "Grow not proper setup before start"); |
425 // The state is also copied here. Hence all buckets in new table will be |
410 // The state is also copied here. Hence all buckets in new table will be |
454 _table->get_bucket(even_index)->first_ptr(), (Node*)POISON_PTR); |
439 _table->get_bucket(even_index)->first_ptr(), (Node*)POISON_PTR); |
455 ) |
440 ) |
456 } |
441 } |
457 } |
442 } |
458 |
443 |
459 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
444 template <typename CONFIG, MEMFLAGS F> |
460 template <typename LOOKUP_FUNC, typename DELETE_FUNC> |
445 template <typename LOOKUP_FUNC, typename DELETE_FUNC> |
461 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
446 inline bool ConcurrentHashTable<CONFIG, F>:: |
462 internal_remove(Thread* thread, LOOKUP_FUNC& lookup_f, DELETE_FUNC& delete_f) |
447 internal_remove(Thread* thread, LOOKUP_FUNC& lookup_f, DELETE_FUNC& delete_f) |
463 { |
448 { |
464 Bucket* bucket = get_bucket_locked(thread, lookup_f.get_hash()); |
449 Bucket* bucket = get_bucket_locked(thread, lookup_f.get_hash()); |
465 assert(bucket->is_locked(), "Must be locked."); |
450 assert(bucket->is_locked(), "Must be locked."); |
466 Node* const volatile * rem_n_prev = bucket->first_ptr(); |
451 Node* const volatile * rem_n_prev = bucket->first_ptr(); |
487 Node::destroy_node(rem_n); |
472 Node::destroy_node(rem_n); |
488 JFR_ONLY(_stats_rate.remove();) |
473 JFR_ONLY(_stats_rate.remove();) |
489 return true; |
474 return true; |
490 } |
475 } |
491 |
476 |
492 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
477 template <typename CONFIG, MEMFLAGS F> |
493 template <typename EVALUATE_FUNC, typename DELETE_FUNC> |
478 template <typename EVALUATE_FUNC, typename DELETE_FUNC> |
494 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
479 inline void ConcurrentHashTable<CONFIG, F>:: |
495 do_bulk_delete_locked_for(Thread* thread, size_t start_idx, size_t stop_idx, |
480 do_bulk_delete_locked_for(Thread* thread, size_t start_idx, size_t stop_idx, |
496 EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f, bool is_mt) |
481 EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f, bool is_mt) |
497 { |
482 { |
498 // Here we have resize lock so table is SMR safe, and there is no new |
483 // Here we have resize lock so table is SMR safe, and there is no new |
499 // table. Can do this in parallel if we want. |
484 // table. Can do this in parallel if we want. |
622 } |
607 } |
623 return bucket; |
608 return bucket; |
624 } |
609 } |
625 |
610 |
626 // Always called within critical section |
611 // Always called within critical section |
627 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
612 template <typename CONFIG, MEMFLAGS F> |
628 template <typename LOOKUP_FUNC> |
613 template <typename LOOKUP_FUNC> |
629 typename ConcurrentHashTable<VALUE, CONFIG, F>::Node* |
614 typename ConcurrentHashTable<CONFIG, F>::Node* |
630 ConcurrentHashTable<VALUE, CONFIG, F>:: |
615 ConcurrentHashTable<CONFIG, F>:: |
631 get_node(const Bucket* const bucket, LOOKUP_FUNC& lookup_f, |
616 get_node(const Bucket* const bucket, LOOKUP_FUNC& lookup_f, |
632 bool* have_dead, size_t* loops) const |
617 bool* have_dead, size_t* loops) const |
633 { |
618 { |
634 size_t loop_count = 0; |
619 size_t loop_count = 0; |
635 Node* node = bucket->first(); |
620 Node* node = bucket->first(); |
794 internal_shrink_epilog(thread); |
779 internal_shrink_epilog(thread); |
795 assert(_resize_lock_owner != thread, "Re-size lock held"); |
780 assert(_resize_lock_owner != thread, "Re-size lock held"); |
796 return true; |
781 return true; |
797 } |
782 } |
798 |
783 |
799 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
784 template <typename CONFIG, MEMFLAGS F> |
800 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
785 inline bool ConcurrentHashTable<CONFIG, F>:: |
801 internal_grow_prolog(Thread* thread, size_t log2_size) |
786 internal_grow_prolog(Thread* thread, size_t log2_size) |
802 { |
787 { |
803 // This double checking of _size_limit_reached/is_max_size_reached() |
788 // This double checking of _size_limit_reached/is_max_size_reached() |
804 // we only do in grow path, since grow means high load on table |
789 // we only do in grow path, since grow means high load on table |
805 // while shrink means low load. |
790 // while shrink means low load. |
958 current_node = current_node->next(); |
943 current_node = current_node->next(); |
959 } |
944 } |
960 return true; |
945 return true; |
961 } |
946 } |
962 |
947 |
963 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
948 template <typename CONFIG, MEMFLAGS F> |
964 template <typename FUNC> |
949 template <typename FUNC> |
965 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
950 inline void ConcurrentHashTable<CONFIG, F>:: |
966 do_scan_locked(Thread* thread, FUNC& scan_f) |
951 do_scan_locked(Thread* thread, FUNC& scan_f) |
967 { |
952 { |
968 assert(_resize_lock_owner == thread, "Re-size lock not held"); |
953 assert(_resize_lock_owner == thread, "Re-size lock not held"); |
969 // We can do a critical section over the entire loop but that would block |
954 // We can do a critical section over the entire loop but that would block |
970 // updates for a long time. Instead we choose to block resizes. |
955 // updates for a long time. Instead we choose to block resizes. |
975 break; /* ends critical section */ |
960 break; /* ends critical section */ |
976 } |
961 } |
977 } /* ends critical section */ |
962 } /* ends critical section */ |
978 } |
963 } |
979 |
964 |
980 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
965 template <typename CONFIG, MEMFLAGS F> |
981 template <typename EVALUATE_FUNC> |
966 template <typename EVALUATE_FUNC> |
982 inline size_t ConcurrentHashTable<VALUE, CONFIG, F>:: |
967 inline size_t ConcurrentHashTable<CONFIG, F>:: |
983 delete_check_nodes(Bucket* bucket, EVALUATE_FUNC& eval_f, |
968 delete_check_nodes(Bucket* bucket, EVALUATE_FUNC& eval_f, |
984 size_t num_del, Node** ndel) |
969 size_t num_del, Node** ndel) |
985 { |
970 { |
986 size_t dels = 0; |
971 size_t dels = 0; |
987 Node* const volatile * rem_n_prev = bucket->first_ptr(); |
972 Node* const volatile * rem_n_prev = bucket->first_ptr(); |
1002 } |
987 } |
1003 return dels; |
988 return dels; |
1004 } |
989 } |
1005 |
990 |
1006 // Constructor |
991 // Constructor |
1007 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
992 template <typename CONFIG, MEMFLAGS F> |
1008 inline ConcurrentHashTable<VALUE, CONFIG, F>:: |
993 inline ConcurrentHashTable<CONFIG, F>:: |
1009 ConcurrentHashTable(size_t log2size, size_t log2size_limit, size_t grow_hint) |
994 ConcurrentHashTable(size_t log2size, size_t log2size_limit, size_t grow_hint) |
1010 : _new_table(NULL), _log2_size_limit(log2size_limit), |
995 : _new_table(NULL), _log2_size_limit(log2size_limit), |
1011 _log2_start_size(log2size), _grow_hint(grow_hint), |
996 _log2_start_size(log2size), _grow_hint(grow_hint), |
1012 _size_limit_reached(false), _resize_lock_owner(NULL), |
997 _size_limit_reached(false), _resize_lock_owner(NULL), |
1013 _invisible_epoch(0) |
998 _invisible_epoch(0) |
1019 _table = new InternalTable(log2size); |
1004 _table = new InternalTable(log2size); |
1020 assert(log2size_limit >= log2size, "bad ergo"); |
1005 assert(log2size_limit >= log2size, "bad ergo"); |
1021 _size_limit_reached = _table->_log2_size == _log2_size_limit; |
1006 _size_limit_reached = _table->_log2_size == _log2_size_limit; |
1022 } |
1007 } |
1023 |
1008 |
1024 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
1009 template <typename CONFIG, MEMFLAGS F> |
1025 inline ConcurrentHashTable<VALUE, CONFIG, F>:: |
1010 inline ConcurrentHashTable<CONFIG, F>:: |
1026 ~ConcurrentHashTable() |
1011 ~ConcurrentHashTable() |
1027 { |
1012 { |
1028 delete _resize_lock; |
1013 delete _resize_lock; |
1029 free_nodes(); |
1014 free_nodes(); |
1030 delete _table; |
1015 delete _table; |
1031 } |
1016 } |
1032 |
1017 |
1033 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
1018 template <typename CONFIG, MEMFLAGS F> |
1034 inline size_t ConcurrentHashTable<VALUE, CONFIG, F>:: |
1019 inline size_t ConcurrentHashTable<CONFIG, F>:: |
1035 get_size_log2(Thread* thread) |
1020 get_size_log2(Thread* thread) |
1036 { |
1021 { |
1037 ScopedCS cs(thread, this); |
1022 ScopedCS cs(thread, this); |
1038 return _table->_log2_size; |
1023 return _table->_log2_size; |
1039 } |
1024 } |
1040 |
1025 |
1041 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
1026 template <typename CONFIG, MEMFLAGS F> |
1042 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
1027 inline bool ConcurrentHashTable<CONFIG, F>:: |
1043 shrink(Thread* thread, size_t size_limit_log2) |
1028 shrink(Thread* thread, size_t size_limit_log2) |
1044 { |
1029 { |
1045 size_t tmp = size_limit_log2 == 0 ? _log2_start_size : size_limit_log2; |
1030 size_t tmp = size_limit_log2 == 0 ? _log2_start_size : size_limit_log2; |
1046 bool ret = internal_shrink(thread, tmp); |
1031 bool ret = internal_shrink(thread, tmp); |
1047 return ret; |
1032 return ret; |
1048 } |
1033 } |
1049 |
1034 |
1050 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
1035 template <typename CONFIG, MEMFLAGS F> |
1051 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
1036 inline bool ConcurrentHashTable<CONFIG, F>:: |
1052 grow(Thread* thread, size_t size_limit_log2) |
1037 grow(Thread* thread, size_t size_limit_log2) |
1053 { |
1038 { |
1054 size_t tmp = size_limit_log2 == 0 ? _log2_size_limit : size_limit_log2; |
1039 size_t tmp = size_limit_log2 == 0 ? _log2_size_limit : size_limit_log2; |
1055 return internal_grow(thread, tmp); |
1040 return internal_grow(thread, tmp); |
1056 } |
1041 } |
1057 |
1042 |
1058 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
1043 template <typename CONFIG, MEMFLAGS F> |
1059 template <typename LOOKUP_FUNC, typename FOUND_FUNC> |
1044 template <typename LOOKUP_FUNC, typename FOUND_FUNC> |
1060 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
1045 inline bool ConcurrentHashTable<CONFIG, F>:: |
1061 get(Thread* thread, LOOKUP_FUNC& lookup_f, FOUND_FUNC& found_f, bool* grow_hint) |
1046 get(Thread* thread, LOOKUP_FUNC& lookup_f, FOUND_FUNC& found_f, bool* grow_hint) |
1062 { |
1047 { |
1063 bool ret = false; |
1048 bool ret = false; |
1064 ScopedCS cs(thread, this); |
1049 ScopedCS cs(thread, this); |
1065 VALUE* val = internal_get(thread, lookup_f, grow_hint); |
1050 VALUE* val = internal_get(thread, lookup_f, grow_hint); |
1088 } |
1073 } |
1089 JFR_ONLY(_stats_rate.add();) |
1074 JFR_ONLY(_stats_rate.add();) |
1090 return true; |
1075 return true; |
1091 } |
1076 } |
1092 |
1077 |
1093 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
1078 template <typename CONFIG, MEMFLAGS F> |
1094 template <typename SCAN_FUNC> |
1079 template <typename SCAN_FUNC> |
1095 inline bool ConcurrentHashTable<VALUE, CONFIG, F>:: |
1080 inline bool ConcurrentHashTable<CONFIG, F>:: |
1096 try_scan(Thread* thread, SCAN_FUNC& scan_f) |
1081 try_scan(Thread* thread, SCAN_FUNC& scan_f) |
1097 { |
1082 { |
1098 if (!try_resize_lock(thread)) { |
1083 if (!try_resize_lock(thread)) { |
1099 return false; |
1084 return false; |
1100 } |
1085 } |
1101 do_scan_locked(thread, scan_f); |
1086 do_scan_locked(thread, scan_f); |
1102 unlock_resize_lock(thread); |
1087 unlock_resize_lock(thread); |
1103 return true; |
1088 return true; |
1104 } |
1089 } |
1105 |
1090 |
1106 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
1091 template <typename CONFIG, MEMFLAGS F> |
1107 template <typename SCAN_FUNC> |
1092 template <typename SCAN_FUNC> |
1108 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
1093 inline void ConcurrentHashTable<CONFIG, F>:: |
1109 do_scan(Thread* thread, SCAN_FUNC& scan_f) |
1094 do_scan(Thread* thread, SCAN_FUNC& scan_f) |
1110 { |
1095 { |
1111 assert(!SafepointSynchronize::is_at_safepoint(), |
1096 assert(!SafepointSynchronize::is_at_safepoint(), |
1112 "must be outside a safepoint"); |
1097 "must be outside a safepoint"); |
1113 assert(_resize_lock_owner != thread, "Re-size lock held"); |
1098 assert(_resize_lock_owner != thread, "Re-size lock held"); |
1115 do_scan_locked(thread, scan_f); |
1100 do_scan_locked(thread, scan_f); |
1116 unlock_resize_lock(thread); |
1101 unlock_resize_lock(thread); |
1117 assert(_resize_lock_owner != thread, "Re-size lock held"); |
1102 assert(_resize_lock_owner != thread, "Re-size lock held"); |
1118 } |
1103 } |
1119 |
1104 |
1120 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
1105 template <typename CONFIG, MEMFLAGS F> |
1121 template <typename SCAN_FUNC> |
1106 template <typename SCAN_FUNC> |
1122 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
1107 inline void ConcurrentHashTable<CONFIG, F>:: |
1123 do_safepoint_scan(SCAN_FUNC& scan_f) |
1108 do_safepoint_scan(SCAN_FUNC& scan_f) |
1124 { |
1109 { |
1125 // We only allow this method to be used during a safepoint. |
1110 // We only allow this method to be used during a safepoint. |
1126 assert(SafepointSynchronize::is_at_safepoint(), |
1111 assert(SafepointSynchronize::is_at_safepoint(), |
1127 "must only be called in a safepoint"); |
1112 "must only be called in a safepoint"); |
1172 unlock_resize_lock(thread); |
1157 unlock_resize_lock(thread); |
1173 assert(_resize_lock_owner != thread, "Re-size lock held"); |
1158 assert(_resize_lock_owner != thread, "Re-size lock held"); |
1174 return true; |
1159 return true; |
1175 } |
1160 } |
1176 |
1161 |
1177 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
1162 template <typename CONFIG, MEMFLAGS F> |
1178 template <typename EVALUATE_FUNC, typename DELETE_FUNC> |
1163 template <typename EVALUATE_FUNC, typename DELETE_FUNC> |
1179 inline void ConcurrentHashTable<VALUE, CONFIG, F>:: |
1164 inline void ConcurrentHashTable<CONFIG, F>:: |
1180 bulk_delete(Thread* thread, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f) |
1165 bulk_delete(Thread* thread, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f) |
1181 { |
1166 { |
1182 assert(!SafepointSynchronize::is_at_safepoint(), |
1167 assert(!SafepointSynchronize::is_at_safepoint(), |
1183 "must be outside a safepoint"); |
1168 "must be outside a safepoint"); |
1184 lock_resize_lock(thread); |
1169 lock_resize_lock(thread); |
1185 do_bulk_delete_locked(thread, eval_f, del_f); |
1170 do_bulk_delete_locked(thread, eval_f, del_f); |
1186 unlock_resize_lock(thread); |
1171 unlock_resize_lock(thread); |
1187 } |
1172 } |
1188 |
1173 |
1189 template <typename VALUE, typename CONFIG, MEMFLAGS F> |
1174 template <typename CONFIG, MEMFLAGS F> |
1190 template <typename VALUE_SIZE_FUNC> |
1175 template <typename VALUE_SIZE_FUNC> |
1191 inline TableStatistics ConcurrentHashTable<VALUE, CONFIG, F>:: |
1176 inline TableStatistics ConcurrentHashTable<CONFIG, F>:: |
1192 statistics_calculate(Thread* thread, VALUE_SIZE_FUNC& vs_f) |
1177 statistics_calculate(Thread* thread, VALUE_SIZE_FUNC& vs_f) |
1193 { |
1178 { |
1194 NumberSeq summary; |
1179 NumberSeq summary; |
1195 size_t literal_bytes = 0; |
1180 size_t literal_bytes = 0; |
1196 InternalTable* table = get_table(); |
1181 InternalTable* table = get_table(); |