Click
Loading...
Searching...
No Matches
hashtablemp.hh
1#ifndef CLICK_HASHTABLEMP_HH
2#define CLICK_HASHTABLEMP_HH
3#include <click/glue.hh>
4#include <click/hashcode.hh>
5#include <click/hashcontainer.hh>
6#include <click/allocator.hh>
7#include <functional>
8#include <click/multithread.hh>
9#if CLICK_DEBUG_HASHMAP
10# define click_hashmp_assert(x) assert(x)
11#else
12# define click_hashmp_assert(x)
13#endif
14
15#define HASTABLEMP_POW2_SZ 1
16
17CLICK_DECLS
18
19//Strut serving as constant template parameters
20// MT_T is used when multithreading could be enabled
21// NOMT when MT is always disabled
22struct MT_T {
23 bool _mt;
24 MT_T(bool mt = true) {
25 _mt = mt;
26 }
27
28 inline bool mt() const {
29 return _mt;
30 }
31
32 inline void disable_mt() {
33 _mt = false;
34 }
35};
36
37struct NOMT {
38 NOMT(bool) {};
39 inline bool mt() const { return false; };
40 inline void disable_mt() { };
41};
42
54template <typename K, typename V, typename Item, typename MT=MT_T>
55class HashContainerMP { public:
56
57 class ListItem { public:
58 ListItem(const ListItem &li) : item(li.item), key(li.key), _hashnext(0) {};
59 ListItem(const K &k,const V &v) : item(v), key(k), _hashnext(0) {};
60 Item item;
61 K key;
62 ListItem* _hashnext;
63 };
64
66
67 class ListItemPtr { public:
68 ListItemPtr() : head(0) {};
69 ListItem* head;
70 };
71
72 class Bucket { public:
73 Bucket() : list() {
74
75 }
77 };
78
79 class Table { public:
80 Table() :
81#if HASTABLEMP_POW2_SZ
82 _nbuckets_mask(0),
83#else
84 _nbuckets(0),
85#endif
86 buckets(0) {
87 _size = 0;
88 }
89 uint32_t nbuckets() {
90#if HASTABLEMP_POW2_SZ
91 return _nbuckets_mask + 1;
92#else
93 return _nbuckets;
94#endif
95 }
96 #if HASTABLEMP_POW2_SZ
97 uint32_t _nbuckets_mask;
98 #else
99 uint32_t _nbuckets;
100 #endif
101 Bucket* buckets;
102 atomic_uint32_t _size;
103 private:
104 Table(const Table &o) {};
105 void operator=(const Table &o){};
106 };
107
108 typedef typename Item::ptr ptr;
109 typedef typename Item::write_ptr write_ptr;
110
111 class iterator {
113 int _b;
114 ListItem* _prev;
115 ListItem* _item;
116 public:
117 ~iterator() {
118 if (_h) {
119 if (_b < _h->_table->nbuckets()) {
120 if (likely(_h->mt()))
121 _h->_table->buckets[_b].list.read_end();
122 }
123 if (likely(_h->mt()))
124 _h->_table.read_end();
125 }
126 }
127
129 void operator++() {
130 if (_b == _h->_table->nbuckets()) return;
131 if (_item) {
132 _item = _item->_hashnext;
133 _prev = _item;
134 }
135 while (!_item) {
136 //click_chatter("Bucket %d : %p",_b,_h->_table->buckets[_b]);
137 if (likely(_h->mt()))
138 _h->_table->buckets[_b].list.read_end();
139 if (++_b == _h->_table->nbuckets()) return;
140 if (likely(_h->mt()))
141 _h->_table->buckets[_b].list.read_begin();
142 _item = _h->_table->buckets[_b].list->head;
143 _prev = 0;
144 }
145 }
146
147 void operator++(int) {
148 operator++();
149 }
150
151
152 operator bool() {
153 return _item;
154 }
155
156 const K& key() {
157 return _item->key;
158 }
159
160 ptr operator *() {
161 return _item->item.read();
162 }
163
164 ptr operator ->() {
165 return _item->item.read();
166 }
167
168
169 iterator(const iterator &o) {
170 _h = o._h;
171 _prev = o._prev;
172 _item = o._item;
173 _b = o._b;
174 o._h = 0;
175 }
176
177 protected:
178 //global read lock must be held!
179 iterator(HashContainerMP<K,V,Item,MT>* h) : _h(h), _prev(0) {
180 _b = 0;
181 if (likely(_h->mt()))
182 _h->_table->buckets[_b].list.read_begin();
183 _item = _h->_table->buckets[_b].list->head;
184 if (_item == 0) {
185 (*this)++;
186 }
187 }
188 friend class HashContainerMP<K,V,Item,MT>;
189 };
190
191 class write_iterator {
193 int _b;
194 ListItem* _prev;
195 ListItem* _item;
196 public:
197 ~write_iterator() {
198 if (_h) {
199 if (_b < _h->_table->nbuckets()) {
200 if (likely(_h->mt()))
201 _h->_table->buckets[_b].list.write_end();
202 }
203 if (likely(_h->mt()))
204 _h->_table.read_end();
205 }
206 _h = 0; //Prevent read destruction
207 }
208
210 void operator++() {
211 if (_b == _h->_table->nbuckets()) return;
212 if (_item) {
213 _item = _item->_hashnext;
214 _prev = _item;
215 }
216 while (!_item) {
217 if (likely(_h->mt()))
218 _h->_table->buckets[_b].list.write_end();
219 if (++_b == _h->_table->nbuckets()) return;
220 if (likely(_h->mt()))
221 _h->_table->buckets[_b].list.write_begin();
222 _item = _h->_table->buckets[_b].list->head;
223 _prev = 0;
224 }
225 }
226
227 void operator++(int) {
228 operator++();
229 }
230
231
232 operator bool() {
233 return _item;
234 }
235
236 const K& key() {
237 return _item->key;
238 }
239
240 write_ptr operator *() {
241 return _item->item.write();
242 }
243
244 write_ptr operator ->() {
245 return _item->item.write();
246 }
247
248 void erase() {
249 if (_prev) {
250 _prev->_hashnext = _item->_hashnext;
251 } else {
252 _h->_table->buckets[_b].list->head = _item->_hashnext;
253 }
254 ListItem* next = _item->_hashnext;
255 _h->erase_item(_item);
256 _item = next;
257 if (next == 0)
258 ++(*this);
259 }
260
261 protected:
262 //global read lock must be held!
263 write_iterator(HashContainerMP<K,V,Item,MT>* h) : _h(h), _prev(0) {
264 _b = 0;
265 if (likely(h->mt()))
266 _h->_table->buckets[_b].list.write_begin();
267 _item = _h->_table->buckets[_b].list->head;
268 if (_item == 0) {
269 (*this)++;
270 }
271 }
272 friend class HashContainerMP<K,V,Item,MT>;
273 };
274
275 static ListItem *&hashnext(ListItem *e) {
276 return e->_hashnext;
277 }
278 static const K& hashkey(const ListItem *e) {
279 return e->key;
280 }
281 static bool hashkeyeq(const K &a, const K &b) {
282 return a == b;
283 }
284
285 typedef uint32_t size_type;
286
287 enum {
288#if CLICK_LINUXMODULE
289 max_bucket_count = 4194303,
290#else
291 max_bucket_count = (uint32_t) -1,
292#endif
293 initial_bucket_count = 63
294 };
295
298
300 explicit HashContainerMP(size_type n);
301
304
305
307 inline size_type size() {
308 size_type s;
309 if (likely(mt()))
310 _table.read_begin();
311 s = _table->_size;
312 if (likely(mt()))
313 _table.read_end();
314 return s;
315 }
316
318 inline size_type buckets() {
319 size_type s;
320 if (likely(mt()))
321 _table.read_begin();
322 s = _table->nbuckets();
323 if (likely(mt()))
324 _table.read_end();
325 return s;
326 }
327
329 inline bool empty() {
330 return size() == 0;
331 }
332
333
335 inline bool unbalanced() {
336 bool r;
337 if (likely(mt()))
338 _table.read_begin();
339 r = _table->_size > 2 * _table->nbuckets() && _table->nbuckets() < max_bucket_count;
340 if (likely(mt()))
341 _table.read_end();
342 return r;
343 }
344
345 inline iterator begin();
346
347 inline write_iterator write_begin();
348
349 //Searchs
351 inline bool contains(const K& key);
352
354 inline ptr find(const K &key);
355
357 inline write_ptr find_write(const K &key);
358
360 inline bool find_clean(const K &key, std::function<void(V &storage)> store, std::function<bool(V &storage)> shouldremove);
361
363 inline bool find_erase(const K &key, V &storage);
364
366 inline bool find_erase_clean(const K &key, std::function<void(V &storage)> store, std::function<bool(V &storage)> shouldremove);
367
368 //Insertions that do not replace
369 /* @brief find a key, and if it does not exists, inserts the given value. Return a read pointer to the entry. */
370 inline ptr find_insert(const K &key, const V &value);
371
372 /* @brief find a key, and if it does not exists, inserts the given value. Return a write pointer to the entry. */
373 inline write_ptr find_insert_write(const K &key, const V &value);
374
375 /* Looks for a key, if it does not exists on_create is called to "create" the value to be inserted.
376 * This is very similar to find_insert, but is useful for cases where the object should only be constructed if it does not already exists,
377 * this is more performant that using find/contains, then insert a newly created value, rehashing again etc. Also it is "atomic". */
378 inline ptr find_create(const K &key,std::function<V(void)> on_create);
379
380 //Replacing insertions
385 inline void insert(const K &key, const V &value, std::function<void(V&value)> on_replace = [](V&){});
386
387 /* @brie Insert a key in the table, calling clean on collisions. If a key exists, it is replaced. */
388 inline void insert_clean(const K &key, const V &value, std::function<bool(V &storage)> shouldremove);
389
390 //Simple removal
391 inline void erase(const K &key);
392
393 void clear();
394
395 void rehash(size_type n);
396
397 inline bool alone(bool atomic = false);
398
399
400 uint32_t refcnt() {
401 return _table.refcnt();
402 }
403
404 inline void disable_mt() {
405 _mt.disable_mt();
406 }
407
408 inline bool mt() {
409 return _mt.mt();
410 }
411
412 inline void resize_clear(uint32_t n) {
413 deinitialize();
414 initialize(n);
415 }
416 protected:
417 MT _mt;
418
419 __rwlock<Table> _table;
420 per_thread<ListItem*> _pending_release;
421
422 void initialize(size_type n);
423 void deinitialize();
424
427
428 //_table read lock must be held
429 size_type bucket(const K &key);
430
431 //Write lock of the bucket must be held, and the item be out of the structure
432 void erase_item(ListItem* item) {
433 if (item->item.unshared()) { //Safe because we have the write lock, so nobody can take this element now
434 _allocator.release(item);
435 } else {
436 item->_hashnext = _pending_release.get();
437 _pending_release.set(item);
438 }
439 }
440
441 ListItem* allocate(const ListItem &li) {
442 ListItem* it;
443 if ((it = _pending_release.get()) && it->item.unshared()) {
444 _pending_release.set(it->_hashnext);
445 return it;
446 }
447 return _allocator.allocate(li);
448 }
449
450 void release_pending(bool force=false) {
451 ListItem* it;
452 ListItem* next;
453 for (unsigned i = 0; i < _pending_release.weight() ; i++) {
454 it = _pending_release.get_value(i);
455 while (it) {
456 next = it->_hashnext;
457 _allocator.release(it);
458 it = next;
459 }
460 }
461 }
462
463 template <bool do_remove>
464 inline bool _find_clean(const K &key, std::function<void(V &storage)> store, std::function<bool(V &storage)> shouldremove, std::function<void(ListItem* &head)> on_missing = [](ListItem* &head){});
465
466
467 friend class iterator;
468
469};
470
471template <typename K, typename V, typename Item, typename MT>
473{
474 _table->_size = 0;
475#if HASTABLEMP_POW2_SZ
476 _table->_nbuckets_mask = next_pow2(n) - 1;
477#else
478 _table->_nbuckets = n;
479#endif
480 _table->buckets = (Bucket *) CLICK_LALLOC(sizeof(Bucket) * _table->nbuckets());
481 for (size_type b = 0; b < _table->nbuckets(); ++b)
482 new(&_table->buckets[b]) Bucket();
483}
484
485template <typename K, typename V, typename Item, typename MT>
487{
488 for (size_type b = 0; b < _table->nbuckets(); ++b)
489 _table->buckets[b].~Bucket();
490 CLICK_LFREE(_table->buckets, sizeof(Bucket) * _table->nbuckets());
491}
492
493
494template <typename K, typename V, typename Item, typename MT>
496 _mt(true), _table(), _pending_release(0)
497{
498 initialize(initial_bucket_count);
499}
500
501template <typename K, typename V, typename Item, typename MT>
503 _mt(true), _table(), _pending_release(0)
504{
505 size_type b = 1;
506 while (b < nb && b < max_bucket_count)
507 b = ((b + 1) << 1) - 1;
508
509 initialize(b);
510}
511
512template <typename K, typename V, typename Item, typename MT>
514{
515 clear();
516 release_pending(true);
517 deinitialize();
518}
519#define MAKE_FIND(ptr_type) \
520 if (likely(mt()))\
521 _table.read_begin();\
522 size_type b = bucket(key);\
523 Bucket& bucket = _table->buckets[b];\
524 if (likely(mt()))\
525 bucket.list.read_begin();\
526 if (likely(mt()))\
527 _table.read_end();\
528 ListItem *pprev;\
529 ptr_type p;\
530 for (pprev = bucket.list->head; pprev; pprev = hashnext(pprev))\
531 if (hashkeyeq(hashkey(pprev), key)) {\
532 p.assign(&pprev->item);\
533 break;\
534 }\
535 if (likely(mt()))\
536 bucket.list.read_end();\
537
538template <typename K, typename V, typename Item, typename MT>
539inline typename HashContainerMP<K,V,Item,MT>::ptr
541{
542 MAKE_FIND(ptr);
543 return p;
544}
545
546template <typename K, typename V, typename Item, typename MT>
547inline typename HashContainerMP<K,V,Item,MT>::write_ptr
549{
550 MAKE_FIND(write_ptr);
551 return p;
552}
553
560template <typename K, typename V, typename Item, typename MT> template <bool do_remove>
561inline bool
562HashContainerMP<K,V,Item,MT>::_find_clean(const K &key, std::function<void(V &c)> store, std::function<bool(V &c)> shoulddelete, std::function<void(ListItem* &head)> on_missing)
563{
564 if (likely(mt()))
565 _table.read_begin();
566 size_type b = bucket(key);
567 Bucket& bucket = _table->buckets[b];
568 if (likely(mt()))
569 bucket.list.write_begin();
570 if (likely(mt()))
571 _table.read_end();
572 ListItem *pprev = bucket.list->head;
573 ListItem* *pprev_ptr = &bucket.list->head;
574 while (pprev) {
575 //We have the write lock of the bucket, so cleaning along the way is fine
576 if (shoulddelete(*pprev->item.unprotected_ptr())) {
577 *pprev_ptr = pprev->_hashnext;
578 ListItem* tmp = pprev;
579 pprev = *pprev_ptr;
580 erase_item(tmp);
581 _table->_size--;
582 } else {
583 if (hashkeyeq(hashkey(pprev), key)) {
584 store(*pprev->item.unprotected_ptr());
585 if (do_remove) {
586 *pprev_ptr = pprev->_hashnext;
587 erase_item(pprev);
588 _table->_size--;
589 }
590 goto found;
591 }
592 pprev_ptr = &pprev->_hashnext;
593 pprev = pprev->_hashnext;
594 }
595 }
596 on_missing(bucket.list->head);
597 if (likely(mt()))
598 bucket.list.write_end();
599 return false;
600 found:
601 if (likely(mt()))
602 bucket.list.write_end();
603 return true;
604}
605
611template <typename K, typename V, typename Item, typename MT>
612inline bool
613HashContainerMP<K,V,Item,MT>::find_erase_clean(const K &key, std::function<void(V &c)> store, std::function<bool(V &c)> shoulddelete)
614{
615 return _find_clean<true>(key, store, shoulddelete);
616}
617
622template <typename K, typename V, typename Item, typename MT>
623inline bool
624HashContainerMP<K,V,Item,MT>::find_clean(const K &key, std::function<void(V &c)> store, std::function<bool(V &c)> shoulddelete)
625{
626 return _find_clean<false>(key, store, shoulddelete);
627}
628
633template <typename K, typename V, typename Item, typename MT>
634inline void
635HashContainerMP<K,V,Item,MT>::insert_clean(const K &key, const V& value, std::function<bool(V &c)> shoulddelete)
636{
637 auto inserter = [key,value,this](ListItem* &head){
638 ListItem* e = allocate(ListItem(key,value));
639 *e->item.unprotected_ptr() = value;
640 click_hashmp_assert(e->item.unshared());
641 e->_hashnext = head;
642 head = e;
643 _table->_size++;
644 };
645 _find_clean<false>(key, [&value](V&c){c = value;}, shoulddelete, inserter);
646}
647
648
654template <typename K, typename V, typename Item, typename MT>
655inline bool
656HashContainerMP<K,V,Item,MT>::find_erase(const K &key, V &storage) {
657 return find_erase_clean(key, [&storage](V&c){storage = c;}, [](V&){return false;});
658}
659
665template <typename K, typename V, typename Item, typename MT>
666inline void
668 find_erase_clean(key, [](V&){}, [](V&){return false;});
669}
670
671#define MAKE_FIND_INSERT(ptr_type,on_exists,value) \
672 if (likely(mt()))\
673 _table.read_begin();\
674 size_type b = bucket(key);\
675 Bucket& bucket = _table->buckets[b];\
676\
677retry:\
678 if (likely(mt()))\
679 bucket.list.read_begin();\
680 \
681 ListItem *pprev;\
682 ptr_type p;\
683 for (pprev = bucket.list->head; pprev; pprev = hashnext(pprev)) {\
684 if (hashkeyeq(hashkey(pprev), key)) {\
685 p.assign(&pprev->item);\
686 break;\
687 }\
688 }\
689 if (p) {\
690 on_exists(pprev->item);\
691 if (likely(mt()))\
692 bucket.list.read_end();\
693 } else {\
694 if (likely(mt()) && !bucket.list.read_to_write()) {\
695 goto retry;\
696 }\
697 ListItem* e = allocate(ListItem(key,value));\
698 click_hashmp_assert(e->item.unshared());\
699 e->_hashnext = bucket.list->head;\
700\
701 bucket.list->head = e;\
702\
703 p.assign(&e->item);\
704\
705 _table->_size++;\
706 if (likely(mt()))\
707 bucket.list.write_end();\
708 }\
709 if (likely(mt()))\
710 _table.read_end();\
711
712template <typename K, typename V, typename Item, typename MT>
713inline typename HashContainerMP<K,V,Item,MT>::ptr
714HashContainerMP<K,V,Item,MT>::find_insert(const K &key,const V &value) {
715 MAKE_FIND_INSERT(ptr,(void),value);
716 click_hashmp_assert(p.hasref());
717 return p;
718}
719
723template <typename K, typename V, typename Item, typename MT>
724inline void
725HashContainerMP<K,V,Item,MT>::insert(const K &key,const V &value, std::function<void(V&value)> on_replace) {
726 MAKE_FIND_INSERT(write_ptr,{on_replace(*pprev->item.unprotected_ptr());pprev->item = value;},value);
727}
728
729template <typename K, typename V, typename Item, typename MT>
730inline typename HashContainerMP<K,V,Item,MT>::ptr
731HashContainerMP<K,V,Item,MT>::find_create(const K &key,std::function<V(void)> on_create) {
732 MAKE_FIND_INSERT(ptr,(void),on_create());
733 return p;
734}
735
736template <typename K, typename V, typename Item, typename MT>
737inline typename HashContainerMP<K,V,Item,MT>::write_ptr
738HashContainerMP<K,V,Item,MT>::find_insert_write(const K &key,const V &value) {
739 MAKE_FIND_INSERT(write_ptr,(void),value);
740 click_hashmp_assert(p.write_held());
741 return p;
742}
743
744template <typename K, typename V, typename Item, typename MT>
747{
748 if (likely(mt()))
749 _table.read_begin();
750 return iterator(this);
751}
752
753template <typename K, typename V, typename Item, typename MT>
756{
757 if (likely(mt()))
758 _table.read_begin();
759 return write_iterator(this);
760}
761
762template <typename K, typename V, typename Item, typename MT>
763inline bool
765{
766 if (_table.refcnt() != 0) return false;
767 if (likely(mt())) {
768 if (atomic)
769 _table.write_begin();
770 else
771 _table.read_begin();
772 }
773 bool alone = true;
774 for (int i = 0; i < _table->nbuckets();i++) {
775 Bucket& b = _table->buckets[i];
776 if (b.list.refcnt() != 0) {
777 alone = false;
778 break;
779 }
780 if (likely(mt())) {
781 if (atomic)
782 b.list.write_begin();
783 else
784 b.list.read_begin();
785 }
786 ListItem* item = b.list->head;
787 while (item) {
788 if (!item->item.unshared()) {
789 alone = false;
790 break;
791 }
792 item = item->_hashnext;
793 }
794 if (likely(mt())) {
795 if (atomic) {
796 b.list.write_end();
797 } else
798 b.list.read_end();
799 }
800 if (!alone)
801 break;
802 }
803 if (likely(mt())) {
804 if (atomic)
805 _table.write_end();
806 else
807 _table.read_end();
808 }
809 return alone;
810}
811
812
813
814//_table.read_lock must be held !
815template <typename K, typename V, typename Item, typename MT>
816inline typename HashContainerMP<K,V,Item,MT>::size_type
818{
819#if HASTABLEMP_POW2_SZ
820 return ((size_type) hashcode(key)) & _table->_nbuckets_mask;
821#else
822 return ((size_type) hashcode(key)) % _table->nbuckets();
823#endif
824}
825
826template <typename K, typename V, typename Item, typename MT>
828{
829 if (likely(mt()))
830 _table.write_begin();
831 for (size_type i = 0; i < _table->nbuckets(); ++i) {
832 Bucket& b = _table->buckets[i];
833 if (likely(mt()))
834 b.list.write_begin();
835 ListItem* item = b.list->head;
836 ListItem* next;
837 while (item) {
838 next = item->_hashnext;
839 erase_item(item);
840 item = next;
841 }
842 b.list->head = 0;
843 if (likely(mt()))
844 b.list.write_end();
845 }
846 if (likely(mt()))
847 _table.write_end();
848 _table->_size = 0;
849}
850
851
852template <typename K, typename V, typename Item, typename MT>
853inline bool HashContainerMP<K,V,Item,MT>::contains(const K& key)
854{
855 if (likely(mt()))
856 _table.read_begin();
857 Bucket &b = _table->buckets[bucket(key)];
858 if (likely(mt()))
859 b.list.read_begin();
860 if (likely(mt()))
861 _table.read_end();
862 for (ListItem* list = b.list->head; list; list = list->_hashnext) {
863 if (hashkeyeq(hashkey(list), key)) {
864 if (likely(mt()))
865 b.list.read_end();
866 return true;
867 }
868 }
869 if (likely(mt()))
870 _table.read_end();
871
872 return false;
873}
874
875/*
876//These are functions from HashTable still to implement in an MT-way.
877//Normally, all needed structures are there.
878template <typename K, typename V, typename Item, typename MT=MT_T>
879inline typename HashContainerMP<K,V,Item,MT>::iterator
880HashContainerMP<K,V,Item,MT>::find_prefer(const K &key)
881{
882 size_type b = bucket(key);
883 T **pprev;
884 for (pprev = &_buckets[b]; *pprev; pprev = &_hashnext(*pprev))
885 if (_hashkeyeq(_hashkey(*pprev), key)) {
886 T *element = *pprev;
887 *pprev = _hashnext(element);
888 _hashnext(element) = _buckets[b];
889 _buckets[b] = element;
890 return iterator(this, b, &_buckets[b], element);
891 }
892 return iterator(this, b, &_buckets[b], 0);
893}
894
895template <typename K, typename V, typename Item, typename MT=MT_T>
896T *HashContainerMP<K,V,Item,MT>::set(iterator &it, T *element, bool balance)
897{
898 click_hashmp_assert(it._hc == this && it._bucket < nbuckets());
899 click_hashmp_assert(bucket(_hashkey(element)) == it._bucket);
900 click_hashmp_assert(!it._element || _hashkeyeq(_hashkey(element), _hashkey(it._element)));
901 T *old = it.get();
902 if (unlikely(old == element))
903 return old;
904 if (!element) {
905 --_size;
906 if (!(*it._pprev = it._element = _hashnext(old)))
907 ++it;
908 return old;
909 }
910 if (old)
911 _hashnext(element) = _hashnext(old);
912 else {
913 ++_size;
914 if (unlikely(unbalanced()) && balance) {
915 rehash(bucket_count() + 1);
916 it._bucket = bucket(_hashkey(element));
917 it._pprev = &_buckets[it._bucket];
918 }
919 if (!(_hashnext(element) = *it._pprev))
920 }
921 *it._pprev = it._element = element;
922 return old;
923}
924
925template <typename K, typename V, typename Item, typename MT=MT_T>
926inline T *HashContainerMP<K,V,Item,MT>::erase(const K &key)
927{
928 iterator it = find(key);
929 return set(it, 0);
930}
931
932template <typename K, typename V, typename Item, typename MT=MT_T>
933inline void HashContainerMP<K,V,Item,MT>::swap(HashContainerMP<K,V> &o)
934{
935 HashContainer_rep<T, A> rep(_rep);
936 _rep = o._rep;
937 o._rep = rep;
938}
939
940template <typename K, typename V, typename Item, typename MT=MT_T>
941void HashContainerMP<K,V,Item,MT>::rehash(size_type n)
942{
943 size_type new_nbuckets = 1;
944 while (new_nbuckets < n && new_nbuckets < max_bucket_count)
945 new_nbuckets = ((new_nbuckets + 1) << 1) - 1;
946 click_hashmp_assert(new_nbuckets > 0 && new_nbuckets <= max_bucket_count);
947 if (_nbuckets == new_nbuckets)
948 return;
949
950 T **new_buckets = (T **) CLICK_LALLOC(sizeof(T *) * new_nbuckets);
951 for (size_type b = 0; b < new/; ++b)
952 new_buckets[b] = 0;
953
954 size_type old_nbuckets = _nbuckets;
955 T **old_buckets = _buckets;
956 _nbuckets = new_nbuckets;
957 _buckets = new_buckets;
958
959 for (size_t b = 0; b < old_nbuckets; b++)
960 for (T *element = old_buckets[b]; element; ) {
961 T *next = _hashnext(element);
962 size_type new_b = bucket(_hashkey(element));
963 _hashnext(element) = new_buckets[new_b];
964 new_buckets[new_b] = element;
965 element = next;
966 }
967
968 CLICK_LFREE(old_buckets, sizeof(T *) * old_nbuckets);
969}
970*/
971
972template <typename K, typename V>
973class HashTableMP : public HashContainerMP<K,V,shared<V> > { public:
974 HashTableMP() : HashContainerMP<K,V,shared<V> >::HashContainerMP() {
975 }
976
977 HashTableMP(int n) : HashContainerMP<K,V,shared<V> >::HashContainerMP(n) {
978 }
979};
980
981template <typename K, typename V>
982class HashTableH : public HashContainerMP<K,V,not_shared<V>,NOMT > { public:
983 HashTableH() : HashContainerMP<K,V,not_shared<V>,NOMT >::HashContainerMP() {
984 }
985
986 HashTableH(int n) : HashContainerMP<K,V,not_shared<V>,NOMT >::HashContainerMP(n) {
987 }
988};
989
990
991template <typename K, typename V>
992class RWHashTableMP : public HashContainerMP<K,V,rwlock<V> > {
993
994
995};
996
997
998template <typename K, typename Vin, typename Time = click_jiffies_t, template <typename> class Protector = shared, typename MT=MT_T>
999class AgingTableMP {
1000 struct V {
1001 Vin value;
1002 Time time;
1003 };
1004
1005 protected:
1007 Time _timeout;
1008 typedef typename HashContainerMP<K,V,Protector<V>,MT >::size_type size_type;
1009
1010 public:
1011 AgingTableMP(Time timeout = 60) : _table(), _timeout(timeout) {
1012 }
1013
1014 void set_timeout(Time timeout) {
1015 _timeout = timeout;
1016 }
1017
1021 inline bool find(K key, const Time &now, Vin& storage, const bool update_age = true) {
1022 return _table.find_clean(key, [&storage,now,update_age](V&c){storage = c.value;if (update_age) c.time = now;}, [now,this](V& v){
1023 bool remove = v.time + _timeout < now;
1024#ifdef DEBUG_AGING
1025 if (remove)
1026 click_chatter("Removing entry upon search (time is %d, expired %d, now %d)",v.time,_timeout,now);
1027#endif
1028 return remove;
1029 });
1030 }
1031
1035 inline void insert(const K &key, const Time &now, const Vin& value) {
1036 const V val = {.value = value, .time = now};
1037 _table.insert_clean(key, val, [now,this](V& v){
1038 bool remove = v.time + _timeout < now;
1039#ifdef DEBUG_AGING
1040 if (remove)
1041 click_chatter("Removing entry upon insertion (time is %d, expired %d, now %d)",v.time,_timeout,now);
1042#endif
1043 return remove;
1044 });
1045 }
1046
1047 inline size_type size() {
1048 return _table.size();
1049 }
1050};
1051
1052template <typename K, typename Vin, typename Time = click_jiffies_t>
1053class AgingTable : public AgingTableMP<K,Vin,Time,not_shared,NOMT> { public:
1054 AgingTable(Time timeout = 60 * CLICK_HZ ) : AgingTableMP<K,Vin,Time,not_shared,NOMT>(timeout) {
1055 }
1056};
1057CLICK_ENDDECLS
1058#endif
Definition multithread.hh:778
Intrusive hash table template, MT safe version.
Definition hashtablemp.hh:55
ptr find(const K &key)
Return a read pointer for the element with key, if any.
bool find_erase_clean(const K &key, std::function< void(V &storage)> store, std::function< bool(V &storage)> shouldremove)
Find a key, and call store if found before removing it. Calls clean on colliding entries upon the way...
bool contains(const K &key)
Test if an element with key key exists in the table.
write_ptr find_write(const K &key)
Return a write pointer for the element with key, if any.
bool find_erase(const K &key, V &storage)
Copy the value of V for K in storage if K is found, delete it from the table and return true....
bool empty()
Return true iff size() == 0.
Definition hashtablemp.hh:329
bool unbalanced()
Return true if this HashContainer should be rebalanced.
Definition hashtablemp.hh:335
size_type buckets()
Return the number of buckets.
Definition hashtablemp.hh:318
~HashContainerMP()
Destroy the HashContainer.
size_type size()
Return the number of elements stored.
Definition hashtablemp.hh:307
void insert(const K &key, const V &value, std::function< void(V &value)> on_replace=[](V &){})
Insert a key. Replace an item if it already exists, insert it if not. Gives the ability to do somethi...
bool find_clean(const K &key, std::function< void(V &storage)> store, std::function< bool(V &storage)> shouldremove)
Find a key K and call store if found. Calls clean on colliding entries upon the way.
HashContainerMP()
Construct an empty HashContainer.
HashContainerMP(size_type n)
Construct an empty HashContainer with at least n buckets.
void write_end()
Definition multithread.hh:721
void write_begin()
Definition multithread.hh:690
void read_begin()
Definition multithread.hh:656
void read_end()
Definition multithread.hh:670
A 32-bit integer with support for atomic operations.
Definition atomic.hh:71
Definition multithread.hh:519
Definition sync.hh:57
unsigned weight() const
Definition sync.hh:203
T & get_value(int i) const
Definition sync.hh:190
Definition allocator.hh:59
Definition multithread.hh:390