Click
Loading...
Searching...
No Matches
multithread.hh
1// -*- c-basic-offset: 4 -*-
2#ifndef CLICK_MULTITHREAD_HH
3#define CLICK_MULTITHREAD_HH
4
5#include <click/config.h>
6#include <click/glue.hh>
7#include <click/vector.hh>
8#include <click/bitvector.hh>
9#include <click/machine.hh>
10#include <click/sync.hh>
11
12#if CLICK_LINUXMODULE
13# error This file is not meant for Kernel mode
14#endif
15
16CLICK_DECLS
17
18/*
19 * Read efficient version of per thread, by giving a bitvector telling
20 * which threads will be used, a mapping can be used to only read from
21 * variables actually used
22 *
23 * Mapping is from index inside the storage vector to thread id
24 */
25template <typename T>
26class per_thread_oread : public per_thread<T> { public:
27 void compress(Bitvector usable) {
28 mapping.resize(usable.weight(),0);
29 int id = 0;
30 for (int i = 0; i < usable.size(); i++) {
31 if (usable[i]) {
32 mapping[id] = i;
33 id++;
34 }
35 }
37 }
38
39 inline bool initialized() {
40 return mapping.size() > 0;
41 }
42
43 inline T& get_value(int i) const{
44 return per_thread<T>::storage[mapping[i]].v;
45 }
46
47 inline void set_value(int i, T v) {
48 per_thread<T>::storage[mapping[i]].v = v;
49 }
50
51 inline int get_mapping(int index) {
52 return mapping[index];
53 }
54
55private:
57};
58
59/*
60 * Memory efficient version which only duplicate the variable per thread
61 * actually used, given a bitvector telling which one they are. However
62 * this comes at the price of one more indirection as we
63 * need a table to map thread ids to positions inside the storage
64 * vector. The mapping table itself is a vector of integers, so
65 * if your data structure is not bigger than two ints, it is not worth it.
66 */
67template <typename T>
68class per_thread_omem { private:
69 typedef struct {
70 T v;
71 } CLICK_CACHE_ALIGN AT;
72
73 AT* storage;
75 unsigned _size;
76
77public:
78 per_thread_omem() : storage(0),mapping(),_size(0) {
79
80 }
81
82 ~per_thread_omem() {
83 if (storage)
84 delete[] storage;
85 }
86
87 void initialize(Bitvector usable, T v=T()) {
88 _size = usable.weight();
89 storage = new AT[_size];
90 mapping.resize(click_max_cpu_ids(), 0);
91 int id = 0;
92 for (unsigned i = 0; i < click_max_cpu_ids(); i++) {
93 if (usable[i]) {
94 storage[id].v = v;
95 mapping[i] = id++;
96 }
97 }
98 }
99
100 inline T* operator->() const {
101 return &(storage[mapping[click_current_cpu_id()]].v);
102 }
103 inline T& operator*() const {
104 return storage[mapping[click_current_cpu_id()]].v;
105 }
106
107 inline T& operator+=(const T& add) const {
108 storage[mapping[click_current_cpu_id()]].v += add;
109 return storage[mapping[click_current_cpu_id()]].v;
110 }
111
112 inline T& operator++() const { // prefix ++
113 return ++(storage[mapping[click_current_cpu_id()]].v);
114 }
115
116 inline T operator++(int) const { // postfix ++
117 return storage[mapping[click_current_cpu_id()]].v++;
118 }
119
120 inline T& operator--() const {
121 return --(storage[mapping[click_current_cpu_id()]].v);
122 }
123
124 inline T operator--(int) const {
125 return storage[mapping[click_current_cpu_id()]].v--;
126 }
127
128 inline T& operator=(T value) const {
129 set(value);
130 return storage[mapping[click_current_cpu_id()]].v;
131 }
132
133 inline void set(T v) {
134 storage[mapping[click_current_cpu_id()]].v = v;
135 }
136
137 inline void setAll(T v) {
138 for (int i = 0; i < weight(); i++)
139 storage[i].v = v;
140 }
141
142 inline T& get() const{
143 return storage[mapping[click_current_cpu_id()]].v;
144 }
145
146 inline T& get_value_for_thread(int thread_id) const{
147 return storage[mapping[thread_id]].v;
148 }
149
150 inline void set_value_for_thread(int thread_id, T v) {
151 storage[mapping[thread_id]].v = v;
152 }
153
154 inline T& get_value(int i) const {
155 return storage[i].v;
156 }
157
158 inline void set_value(int i, T v) {
159 storage[i] = v;
160 }
161
162 inline unsigned weight() const {
163 return _size;
164 }
165};
166
173
174 static int sum(int i) {
175 return i;
176 }
177
178 static unsigned usum(unsigned i) {
179 return i;
180 }
181
182 static int sum(per_thread<int> &v) {
183 PER_THREAD_SUM(unsigned, s, v);
184 return s;
185 }
186
187 static unsigned sum(per_thread<unsigned> &v) {
188 PER_THREAD_SUM(unsigned, s, v);
189 return s;
190 }
191
192 static void set(int &i, int v) {
193 i = v;
194 }
195
196 static void uset(unsigned &i, unsigned v) {
197 i = v;
198 }
199
200 static void set(per_thread<int> &pt, int v) {
201 PER_THREAD_SET(pt , v);
202 }
203
204 static void uset(per_thread<unsigned> &pt, unsigned v) {
205 PER_THREAD_SET(pt , v);
206 }
207
208#if HAVE_INT64_TYPES
209 static int64_t sum_long(int64_t i) {
210 return i;
211 }
212
213 static uint64_t usum_long(uint64_t i) {
214 return i;
215 }
216
217 static void set_long(int64_t &i, int64_t v) {
218 i = v;
219 }
220
221 static void uset_long(uint64_t &i, uint64_t v) {
222 i = v;
223 }
224
225 static int64_t sum_long(per_thread<int64_t> &v) {
226 PER_THREAD_SUM(int64_t, s, v);
227 return s;
228 }
229
230 static int64_t usum_long(per_thread<uint64_t> &v) {
231 PER_THREAD_SUM(uint64_t, s, v);
232 return s;
233 }
234
235 static void set_long(per_thread<int64_t> &pt, int64_t v) {
236 PER_THREAD_SET(pt, v);
237 }
238
239 static void uset_long(per_thread<uint64_t> &pt, uint64_t v) {
240 PER_THREAD_SET(pt, v);
241 }
242#endif
243};
244
283template <typename T, int N>
285
286 unprotected_rcu_singlewriter() : rcu_current(0) {
287 }
288
289 unprotected_rcu_singlewriter(T v) : rcu_current(0) {
290 storage[rcu_current] = v;
291 }
292
294 }
295
296 inline void initialize(T v) {
297 storage[rcu_current] = v;
298 }
299
300 inline const T& read_begin() {
301 return storage[rcu_current];
302 }
303
304 inline void read_end() {
305
306 }
307
308 inline const T& read() const {
309 return storage[rcu_current];
310 }
311
312 inline const T operator*() const {
313 return read();
314 }
315
316 inline const T* operator->() const {
317 return &read();
318 }
319
320 inline T& write_begin() {
321 int rcu_next = (rcu_current + 1) & (N - 1);
322 storage[rcu_next] = storage[rcu_current];
323 return storage[rcu_next];
324 }
325
326 inline void write_commit() {
327 click_write_fence();
328 rcu_current = (rcu_current + 1) & (N - 1);
329 }
330
331 inline void write_abort() {
332 }
333
334 inline const T& read_begin(int) {
335 return read_begin();
336 }
337
338 inline void read_end(int) {
339 return read_end();
340 }
341
342protected:
343 T storage[N];
344 volatile int rcu_current;
345};
346
347
359template <typename T,int N>
361
362 unprotected_rcu() : _write_lock() {
363
364 }
365
366 inline T& write_begin() {
367 _write_lock.acquire();
369 }
370
371 inline void write_commit() {
373 _write_lock.release();
374 }
375
376 inline void write_abort() {
377 _write_lock.release();
378 }
379
380private:
381 Spinlock _write_lock;
382};
383
389template <typename T>
390class shared { public:
391
392 shared() {
393 _refcnt = 0;
394 }
395
396 shared(T p) : _p(p) {
397 _refcnt = 0;
398 }
399
400 class ptr { public:
401 inline ptr() : _v(0) {};
402 inline ptr(shared<T>* v) : _v(v) {_v->get();}
403 inline ~ptr() {
404 release();
405 }
406
407 inline T* operator->() {
408 return &_v->_p;
409 }
410
411 inline T* get() {
412 return &_v->_p;
413 }
414
415 inline T& operator*() {
416 return _v->_p;
417 }
418
419 inline ptr(const ptr &o) {
420 _v = o._v;
421 o._v = 0;
422 }
423
424 inline uint32_t refcnt() {
425 return _v->_refcnt;
426 }
427
428 inline void assign(shared<T>* v) {
429 release();
430 _v = v;
431 _v->get();
432 }
433
434 inline void assign_release(ptr &o) {
435 release();
436 _v = o._v;
437 o._v = 0;
438 }
439
440 inline void operator=(const ptr &o) {
441 release();
442 _v = o._v;
443 if (_v)
444 _v->get();
445 }
446
447 inline void release() {
448 if (_v)
449 _v->put();
450 _v = 0;
451 }
452
453 inline operator bool() const {
454 return _v;
455 }
456
457 private:
458 mutable shared<T>* _v;
459 };
460
461 //shared does not support write_ptr, we typedef for convenience
462 typedef ptr write_ptr;
463
464 inline ptr operator->() {
465 return ptr(this);
466 }
467
468 inline ptr read() {
469 return ptr(this);
470 }
471
472 inline ptr write() {
473 return write_ptr(this);
474 }
475
476
477 inline void get(ptr &o) {
478 o.assign(this);
479 }
480
481 uint32_t refcnt() {
482 return _refcnt;
483 }
484
485 inline bool unshared() {
486 return _refcnt == 0;
487 }
488
489 inline bool write_held() {
490 return hasref();
491 }
492
493 inline bool hasref() {
494 return _refcnt > 0;
495 }
496
497 T* unprotected_ptr() {
498 return &_p;
499 }
500
501private:
502 T _p;
503 atomic_uint32_t _refcnt;
504
505 inline void get() {
506 _refcnt++;
507 }
508
509 inline void put() {
510 click_read_fence();
511 _refcnt--;
512 }
513};
514
518template <typename T>
519class not_shared { public:
520
521 not_shared() {
522 }
523
524 not_shared(T p) : _p(p) {
525 }
526
527 class ptr { public:
528 inline ptr() : _v(0) {};
529 inline ptr(not_shared<T>* v) : _v(v) {_v->get();}
530 inline ~ptr() {
531 release();
532 }
533
534 inline T* operator->() {
535 return &_v->_p;
536 }
537
538 inline T* get() {
539 return &_v->_p;
540 }
541
542 inline T& operator*() {
543 return _v->_p;
544 }
545
546 inline ptr(const ptr &o) {
547 _v = o._v;
548 o._v = 0;
549 }
550
551
552 inline void assign(not_shared<T>* v) {
553 release();
554 _v = v;
555 _v->get();
556 }
557
558 inline void assign_release(ptr &o) {
559 release();
560 _v = o._v;
561 o._v = 0;
562 }
563
564 inline void operator=(const ptr &o) {
565 release();
566 _v = o._v;
567 if (_v)
568 _v->get();
569 }
570
571 inline void release() {
572 if (_v)
573 _v->put();
574 _v = 0;
575 }
576
577 inline operator bool() const {
578 return _v;
579 }
580
581 private:
582 mutable not_shared<T>* _v;
583 };
584
585 //shared does not support write_ptr, we typedef for convenience
586 typedef ptr write_ptr;
587
588 inline ptr operator->() {
589 return ptr(this);
590 }
591
592 inline ptr read() {
593 return ptr(this);
594 }
595
596 inline ptr write() {
597 return write_ptr(this);
598 }
599
600
601 inline void get(ptr &o) {
602 o.assign(this);
603 }
604
605 T* unprotected_ptr() {
606 return &_p;
607 }
608
609 inline bool unshared() {
610 return true;
611 }
612
613 inline bool write_held() {
614 return true;
615 }
616
617 inline bool hasref() {
618 return true;
619 }
620
621
622private:
623 T _p;
624
625 inline void get() {
626 }
627
628 inline void put() {
629 }
630};
631
643class RWLock { public:
644
645 RWLock() {
646 _refcnt = 0;
647 }
648
656 inline void read_begin() {
657 uint32_t current_refcnt;
658 do {
659 current_refcnt = _refcnt;
660#if ! CLICK_ATOMIC_COMPARE_SWAP
661 } while ((int32_t)current_refcnt < 0 || (!_refcnt.compare_and_swap(current_refcnt,current_refcnt+1)));
662#else
663 } while ((int32_t)current_refcnt < 0 || (_refcnt.compare_swap(current_refcnt,current_refcnt+1) != current_refcnt));
664#endif
665 }
666
670 inline void read_end() {
671 click_read_fence();
672 _refcnt--;
673 }
674
681 inline void read_get() {
682 _refcnt++;
683 }
684
690 inline void write_begin() {
691#if ! CLICK_ATOMIC_COMPARE_SWAP
692 while (!_refcnt.compare_and_swap(0,-1)) click_relax_fence();
693#else
694 while (_refcnt.compare_swap(0,-1) != 0) click_relax_fence();
695#endif
696 }
697
701 inline bool write_attempt() CLICK_WARN_UNUSED_RESULT {
702#if ! CLICK_ATOMIC_COMPARE_SWAP
703 return (_refcnt.compare_and_swap(0,-1));
704#else
705 return (_refcnt.compare_swap(0,-1) == 0);
706#endif
707 }
708
714 inline void write_get() {
715 _refcnt--;
716 }
717
721 inline void write_end() {
722 click_write_fence();
723 _refcnt++;
724 }
725
729 inline void write_to_read() {
730 click_write_fence();
731 assert(_refcnt == (uint32_t)-1);
732 _refcnt = 1;
733 }
734
762 inline bool read_to_write() CLICK_WARN_UNUSED_RESULT;
763
764 inline uint32_t refcnt() {
765 return _refcnt;
766 }
767
768private:
769 atomic_uint32_t _refcnt;
770
771};
772
773
777template <class V>
778class __rwlock : public RWLock { public:
779 __rwlock() : _v(){
780 }
781
782 __rwlock(V v) : _v(v) {
783 }
784
785 V _v;
786
787 V* operator->() {
788 return &_v;
789 }
790
791 V& operator*() {
792 return _v;
793 }
794};
795
808class rXwlockPR { public:
809 rXwlockPR() : max_write(-65535) {
810 _refcnt = 0;
811 }
812
813 rXwlockPR(int32_t max_writers) {
814 _refcnt = 0;
815 set_max_writers(max_writers);
816 }
817
818 void set_max_writers(int32_t max_writers) {
819 assert(max_writers < 65535);
820 read_begin();
821 max_write = - max_writers;
822 read_end();
823 }
824
825 inline void read_begin() {
826 uint32_t current_refcnt;
827 do {
828 current_refcnt = _refcnt;
829 if (unlikely((int32_t)current_refcnt < 0)) {
830 if ((int32_t)current_refcnt <= -65536) {
831 //Just wait for the other reader out there to win
832 } else {
833#if ! CLICK_ATOMIC_COMPARE_SWAP
834 if (_refcnt.compare_and_swap(current_refcnt,current_refcnt - 65536)) {
835#else
836 if (_refcnt.compare_swap(current_refcnt,current_refcnt - 65536) == current_refcnt) {
837#endif
838 //We could lower the value, so wait for it to reach -65536 (0 writer but one reader waiting) and continue
839 do {
840 click_relax_fence();
841 } while((int32_t)_refcnt != -65536);
842 //When it is -65536, driver cannot take it and reader are waiting, so we can set it directly
843 _refcnt = 1;
844 break;
845 }
846 }
847 } else { // >= 0, just grab another reader (>0)
848#if ! CLICK_ATOMIC_COMPARE_SWAP
849 if (likely(_refcnt.compare_and_swap(current_refcnt,current_refcnt+1)))
850#else
851 if (likely(_refcnt.compare_swap(current_refcnt,current_refcnt+1) == current_refcnt))
852#endif
853 break;
854 }
855 click_relax_fence();
856 } while (1);
857 }
858
859 inline void read_end() {
860 click_read_fence();
861 _refcnt--;
862 }
863
864 inline void read_get() {
865 _refcnt++;
866 }
867
868 inline void write_begin() {
869 uint32_t current_refcnt;
870 do {
871 current_refcnt = _refcnt;
872 if (likely((int32_t)current_refcnt <= 0 && (int32_t)current_refcnt > max_write)) {
873#if ! CLICK_ATOMIC_COMPARE_SWAP
874 if (_refcnt.compare_and_swap(current_refcnt,current_refcnt - 1))
875#else
876 if (_refcnt.compare_swap(current_refcnt,current_refcnt - 1) == current_refcnt)
877#endif
878 break;
879 }
880 click_relax_fence();
881 } while (1);
882 }
883
884 inline void write_end() {
885 click_write_fence();
886 _refcnt++;
887 }
888
889private:
890 atomic_uint32_t _refcnt;
891 int32_t max_write;
892} CLICK_CACHE_ALIGN;
893
906class rXwlockPW { public:
907 rXwlockPW() : max_write(-65535) {
908 _refcnt = 0;
909 }
910
911 rXwlockPW(int32_t max_writers) {
912 _refcnt = 0;
913 set_max_writers(max_writers);
914 }
915
916 void set_max_writers(int32_t max_writers) {
917 assert(max_writers < 65535);
918 write_begin();
919 max_write = - max_writers;
920 write_end();
921 }
922
923 inline void write_begin() {
924 uint32_t current_refcnt;
925 do {
926 current_refcnt = _refcnt;
927 if (unlikely((int32_t)current_refcnt < 0)) {
928 if ((int32_t)current_refcnt <= -65536) {
929 //Just wait for the other reader out there to win
930 } else {
931#if ! CLICK_ATOMIC_COMPARE_SWAP
932 if (_refcnt.compare_and_swap(current_refcnt,current_refcnt - 65536)) {
933#else
934 if (_refcnt.compare_swap(current_refcnt,current_refcnt - 65536) == current_refcnt) {
935#endif
936 //We could lower the value, so wait for it to reach -65536 (0 writer but one reader waiting) and continue
937 do {
938 click_relax_fence();
939 } while((int32_t)_refcnt != -65536);
940 //When it is -65536, driver cannot take it and reader are waiting, so we can set it directly
941 _refcnt = 1;
942 break;
943 }
944 }
945 } else { // >= 0, just grab another reader (>0)
946#if ! CLICK_ATOMIC_COMPARE_SWAP
947 if (likely(_refcnt.compare_and_swap(current_refcnt,current_refcnt+1)))
948#else
949 if (likely(_refcnt.compare_swap(current_refcnt,current_refcnt+1) == current_refcnt))
950#endif
951 break;
952 }
953 click_relax_fence();
954 } while (1);
955 }
956
957 inline void write_end() {
958 click_write_fence();
959 _refcnt--;
960 }
961
962 inline void write_get() {
963 _refcnt++;
964 }
965
966 inline void read_begin() {
967 uint32_t current_refcnt;
968 do {
969 current_refcnt = _refcnt;
970 if (likely((int32_t)current_refcnt <= 0 && (int32_t)current_refcnt > max_write)) {
971#if ! CLICK_ATOMIC_COMPARE_SWAP
972 if (_refcnt.compare_and_swap(current_refcnt,current_refcnt - 1))
973#else
974 if (_refcnt.compare_swap(current_refcnt,current_refcnt - 1) == current_refcnt)
975#endif
976 break;
977 }
978 click_relax_fence();
979 } while (1);
980 }
981
982 inline void read_end() {
983 click_read_fence();
984 _refcnt++;
985 }
986
987
988private:
989 atomic_uint32_t _refcnt;
990 int32_t max_write;
991} CLICK_CACHE_ALIGN;
992
993
994
995class rXwlock { public:
996 rXwlock() {
997 _refcnt = 0;
998 }
999
1000 inline void read_begin() {
1001 uint32_t current_refcnt;
1002 current_refcnt = _refcnt;
1003#if ! CLICK_ATOMIC_COMPARE_SWAP
1004 while ((int32_t)current_refcnt < 0 || !_refcnt.compare_and_swap(current_refcnt,current_refcnt+1)) {
1005#else
1006 while ((int32_t)current_refcnt < 0 || _refcnt.compare_swap(current_refcnt,current_refcnt+1) != current_refcnt) {
1007#endif
1008 click_relax_fence();
1009 current_refcnt = _refcnt;
1010 }
1011 }
1012
1013 void set_max_writers(int32_t max_writers) {
1014 (void)max_writers;
1015 }
1016
1017
1018
1019 inline void read_end() {
1020 click_read_fence();
1021 _refcnt--;
1022 }
1023
1024 inline void read_get() {
1025 _refcnt++;
1026 }
1027
1028 inline void write_begin() {
1029 uint32_t current_refcnt;
1030 current_refcnt = _refcnt;
1031#if ! CLICK_ATOMIC_COMPARE_SWAP
1032 while ((int32_t)current_refcnt > 0 || !_refcnt.compare_and_swap(current_refcnt,current_refcnt-1)) {
1033#else
1034 while ((int32_t)current_refcnt > 0 || _refcnt.compare_swap(current_refcnt,current_refcnt-1) != current_refcnt) {
1035#endif
1036 click_relax_fence();
1037 current_refcnt = _refcnt;
1038 }
1039 }
1040
1041 inline void write_end() {
1042 click_write_fence();
1043 _refcnt++;
1044 }
1045
1046private:
1047 atomic_uint32_t _refcnt;
1048} CLICK_CACHE_ALIGN;
1049
1050
1054template <typename V>
1055class rwlock { public:
1056
1057 rwlock() : _v() {
1058 }
1059
1060 rwlock(V v) : _v(v) {
1061 }
1062
1063 class ptr { public :
1064 ptr(const ptr &o) {
1065 _p = o._p;
1066 if (_p)
1067 _p->read_get();
1068 }
1069
1070 void release() {
1071 if (_p)
1072 _p->read_end();
1073 _p = 0;
1074 }
1075
1076 ~ptr() {
1077 release();
1078 }
1079
1080 const V* operator->() const {
1081 return &_p->_v;
1082 }
1083
1084 const V& operator*() const {
1085 return _p->_v;
1086 }
1087
1088 void operator=(const ptr &o) {
1089 _p = o._p;
1090 if (_p) {
1091 _p->read_begin();
1092 }
1093 }
1094
1095 inline uint32_t refcnt() {
1096 return _p->refcnt();
1097 }
1098
1102 inline void assign(rwlock<V>* v) {
1103 release();
1104 _p = &v->_v;
1105 if (_p)
1106 _p->read_begin();
1107 }
1108
1109 inline operator bool() const {
1110 return _p;
1111 }
1112
1113 ptr() : _p(0) {};
1114 private:
1115 __rwlock<V>* _p;
1116
1117 ptr(__rwlock<V>* p) : _p(p) {}; //Read must be held
1118
1119 friend class rwlock<V>;
1120 };
1121
1122 class write_ptr { public :
1123 write_ptr(const write_ptr &o) {
1124 _p = o._p;
1125 if (_p) {
1126 _p->write_get();
1127 }
1128 }
1129
1130 void release() {
1131 if (_p)
1132 _p->write_end();
1133 _p = 0;
1134 }
1135
1136 ~write_ptr() {
1137 release();
1138 }
1139
1140 V* operator->() {
1141 return &_p->_v;
1142 }
1143
1144 V& operator*() {
1145 return _p->_v;
1146 }
1147
1148 void operator=(const write_ptr &o) {
1149 if (o._p)
1150 _p->write_end();
1151 _p = o._p;
1152 if (_p) {
1153 _p->write_get();
1154 }
1155 }
1156
1157 uint32_t refcnt() {
1158 return _p->refcnt();
1159 }
1160
1164 inline void assign(rwlock<V>* v) {
1165 release();
1166 _p = &v->_v;
1167 if (_p)
1168 _p->write_begin();
1169 }
1170
1175 inline void assign_release(write_ptr& o) {
1176 release();
1177 _p = o._p;
1178 o._p = 0;
1179 }
1180
1181 inline operator bool() const {
1182 return _p;
1183 }
1184
1185 write_ptr() : _p(0) {};
1186 private:
1187 __rwlock<V>* _p;
1188
1189 write_ptr(__rwlock<V>* p) : _p(p) {}; //Write must be held
1190
1191 friend class rwlock<V>;
1192 };
1193
1194 ptr read() {
1195 _v.read_begin();
1196 return ptr(&_v);
1197 }
1198
1199 write_ptr write() {
1200 _v.write_begin();
1201 return write_ptr(&_v);
1202 }
1203
1212 write_ptr read_to_write(const ptr &o) {
1213 o._p = 0;
1214 if (_v.read_to_write()) {
1215 return write_ptr(_v);
1216 }
1217 return write_ptr();
1218 }
1219
1225 ptr write_to_read(const write_ptr &o) {
1226 o._p = 0;
1227 _v.write_to_read();
1228 return const_ptr(_v);
1229 }
1230
1231 V* unprotected_ptr() {
1232 return &_v._v;
1233 }
1234
1235 uint32_t refcnt() {
1236 return _v.refcnt();
1237 }
1238private:
1239 __rwlock<V> _v;
1240};
1241
1242inline bool
1244 /* Sadly, read_to_write is more complex than write_to_read,
1245 * because
1246 * two readers could want to become writer at the same time,
1247 * probably having some reference that will become invalid if
1248 * someone else grab the write. In this case one of them must
1249 * fail and retry again, or we'll have a deadlock.
1250 *
1251 * The trick is to add 1000 to the refcnt, only one potential
1252 * writer will be able to do that so after that we can wait for
1253 * the count to be 1000000 (no more reader) and make it -1
1254 */
1255 click_write_fence();
1256
1257 //All that being said, we make a first attempt in case we would be the only reader
1258#if ! CLICK_ATOMIC_COMPARE_SWAP
1259 if (_refcnt.compare_and_swap(1,-1))
1260#else
1261 if (_refcnt.compare_swap(1,-1) == 1)
1262#endif
1263 return true;
1264
1265 uint32_t current_refcnt;
1266 uint32_t new_refcnt;
1267 do {
1268 current_refcnt = _refcnt;
1269 new_refcnt = current_refcnt+1000000;
1270 if (current_refcnt > 1000000) {
1271 read_end();
1272 click_relax_fence();
1273 return false;
1274 }
1275#if ! CLICK_ATOMIC_COMPARE_SWAP
1276 } while (!_refcnt.compare_and_swap(current_refcnt,new_refcnt));
1277 while (!_refcnt.compare_and_swap(1000001,-1)) click_relax_fence();
1278#else
1279 } while (_refcnt.compare_swap(current_refcnt,new_refcnt) != current_refcnt);
1280 while (_refcnt.compare_swap(1000001,-1) != 1000001) click_relax_fence();
1281#endif
1282 return true;
1283}
1284
1311template <typename T>
1312class click_rcu { public:
1313 click_rcu() : rcu_current(0) {
1314 refcnt[0] = 0;
1315 refcnt[1] = 0;
1316 }
1317
1318 click_rcu(T v) : rcu_current(0) {
1319 refcnt[0] = 0;
1320 refcnt[1] = 0;
1321 initialize(v);
1322 }
1323
1324 ~click_rcu() {}
1325
1326 inline void initialize(T v) {
1327 storage[rcu_current] = v;
1328 }
1329
1348 inline const T& read_begin(int &rcu_current_local) {
1349 uint32_t current_refcnt;
1350 do {
1351 rcu_current_local = rcu_current;
1352 click_read_fence();
1353 current_refcnt = refcnt[rcu_current_local].value();
1354#if ! CLICK_ATOMIC_COMPARE_SWAP
1355 if (current_refcnt == (uint32_t)-1 || !(refcnt[rcu_current_local].compare_and_swap(current_refcnt,current_refcnt+1))) {
1356#else
1357 if (current_refcnt == (uint32_t)-1 || (refcnt[rcu_current_local].compare_swap(current_refcnt,current_refcnt+1) != current_refcnt)) {
1358#endif
1359 click_relax_fence();
1360 } else {
1361 break;
1362 }
1363 } while (1);
1364 click_write_fence();
1365 //The reference is holded now, we are sure that no writer will edit this until read_end is called.
1366 return storage[rcu_current_local];
1367 }
1368
1369 inline void read_end(const int &rcu_current_local) {
1370 click_compiler_fence(); //No reorder of refcnt--
1371 refcnt[rcu_current_local]--;
1372 }
1373
1374 inline T read() {
1375 int flags;
1376 T v = read_begin(flags);
1377 read_end(flags);
1378 return v;
1379 }
1380
1387 inline T& write_begin() {
1388 int rcu_next;
1389 retry:
1390 do {
1391 rcu_next = (rcu_current + 1) & 1;
1392#if ! CLICK_ATOMIC_COMPARE_SWAP
1393 } while (!refcnt[rcu_next].compare_and_swap(0,-1));
1394#else
1395 } while (refcnt[rcu_next].compare_swap(0,-1) != 0);
1396#endif
1397
1398 /*As the other writer writes the refcnt before rcu_current, we could
1399 *have grabbed the new actual bucket*/
1400 if (rcu_next == rcu_current) {
1401 refcnt[rcu_next] = 0;
1402 goto retry;
1403 }
1404
1405 //Refcnt is now -1, and we are the only writer
1406 storage[rcu_next] = storage[rcu_current];
1407 return storage[rcu_next];
1408 }
1409
1410 inline void write_commit() {
1411 int rcu_next = (rcu_current + 1) & 1;
1412 rcu_current = rcu_next;
1413 click_write_fence();
1414 refcnt[rcu_next] = 0;
1415 }
1416
1417 inline void write_abort() {
1418 /*Just set the refcnt to 0, as rcu_current is not changed, another writer
1419 will grab the same bucket, and the readers will not see a thing*/
1420 click_write_fence();
1421 refcnt[(rcu_current + 1) & 1] = 0;
1422 }
1423
1424 inline void write(const T &v) {
1425 write_begin() = v;
1426 write_commit();
1427 }
1428
1429protected:
1430 atomic_uint32_t refcnt[2];
1431 T storage[2];
1432 volatile int rcu_current;
1433};
1434
1435
1439template <typename T>
1440class fast_rcu { public:
1441#define RCU_N 2
1442 fast_rcu() : _rcu_current(0), _epochs(0), _write_epoch(1) {
1443 }
1444
1445 fast_rcu(T v) : _rcu_current(0), _epochs(0), _write_epoch(1){
1446 initialize(v);
1447 }
1448
1449 ~fast_rcu() {}
1450
1451 inline void initialize(T v) {
1452 _storage[_rcu_current].v = v;
1453 }
1454
1455 inline const T& read_begin(int &) {
1456 int w_epoch = _write_epoch;
1457 *_epochs = w_epoch;
1458 click_write_fence(); //Actually read rcu_current after storing epoch
1459 int rcu_current_local = _rcu_current;
1460 click_read_fence();//Do not read _rcu_current later
1461 //The reference is holded now, we are sure that no writer will edit this until read_end is called.
1462 return _storage[rcu_current_local].v;
1463 }
1464
1465 inline void read_end(const int &) {
1466 click_compiler_fence(); //No load or store after writing the epoch bak to 0
1467 *_epochs = 0;
1468 }
1469
1470 inline T read() {
1471 int flags;
1472 T v = read_begin(flags);
1473 read_end(flags);
1474 return v;
1475 }
1476
1477 inline T& write_begin(int& rcu_current_local) {
1478
1479 _write_lock.acquire();
1480 rcu_current_local = _rcu_current;
1481
1482 int rcu_next = (rcu_current_local + 1) & (RCU_N - 1);
1483 int bad_epoch = (_write_epoch - RCU_N) + 1;
1484
1485 unsigned i = 0;
1486 loop:
1487 for (; i < _epochs.weight(); i ++) {
1488 int te = _epochs.get_value(i);
1489 if (unlikely(te != 0 && te == bad_epoch)) {
1490 click_relax_fence();
1491 goto loop;
1492 }
1493 //TODO : if allow RCU_N > 2, compute a min_epoch at the same time so next writer can avoid looping
1494 }
1495
1496 //All epochs are 0 (no accessed, or at least not to the current ptr)
1497 _storage[rcu_next].v = _storage[rcu_current_local].v;
1498
1499 return _storage[rcu_next].v;
1500 }
1501
1502 inline void write_commit(int rcu_current_local) {
1503 click_write_fence(); //Prevent write to finish after this
1504 _rcu_current = (rcu_current_local + 1) & 1;
1505 click_compiler_fence(); //Write epoch must happen after rcu_current, to be sure that thr old bucket is not read and accessed with the new epoch.
1506 ++_write_epoch;
1507 _write_lock.release();
1508 }
1509
1510protected:
1511
1512 typedef struct {
1513 T v;
1514 } CLICK_CACHE_ALIGN AT;
1515
1516 volatile int _rcu_current;
1518 AT _storage[2];
1519 volatile int _write_epoch;
1520 Spinlock _write_lock;
1521
1522};
1523
1524CLICK_ENDDECLS
1525#endif
Click's bitvector class.
Definition multithread.hh:778
Vector of bits.
Definition bitvector.hh:21
int weight() const
Return the number of true bits.
Definition bitvector.hh:394
int size() const
Return the number of bits in the bitvector.
Definition bitvector.hh:192
Definition multithread.hh:643
void write_end()
Definition multithread.hh:721
void write_begin()
Definition multithread.hh:690
void write_get()
Definition multithread.hh:714
void write_to_read()
Definition multithread.hh:729
bool read_to_write()
Definition multithread.hh:1243
void read_begin()
Definition multithread.hh:656
void read_get()
Definition multithread.hh:681
void read_end()
Definition multithread.hh:670
bool write_attempt()
Definition multithread.hh:701
A recursive spinlock for SMP Click threads.
Definition sync.hh:439
void release()
Releases the Spinlock.
Definition sync.hh:534
void acquire()
Acquires the Spinlock.
Definition sync.hh:488
Vector template.
Definition vector.hh:111
void resize(size_type n, value_argument_type v=T())
Resize the vector to contain n elements.
Definition vector.hh:390
A 32-bit integer with support for atomic operations.
Definition atomic.hh:71
bool compare_and_swap(uint32_t expected, uint32_t desired) CLICK_BUILTINS_NONDEPRECATED
Perform a compare-and-swap operation.
Definition atomic.hh:734
Definition multithread.hh:1312
const T & read_begin(int &rcu_current_local)
Definition multithread.hh:1348
T & write_begin()
Definition multithread.hh:1387
Definition multithread.hh:1440
Definition multithread.hh:519
Definition multithread.hh:172
Definition sync.hh:57
void resize(unsigned int max_cpu_id, T v)
Definition sync.hh:97
T & get_value(int i) const
Definition sync.hh:190
Definition multithread.hh:808
Definition multithread.hh:906
Definition multithread.hh:1055
write_ptr read_to_write(const ptr &o)
Definition multithread.hh:1212
ptr write_to_read(const write_ptr &o)
Definition multithread.hh:1225
Definition multithread.hh:390
Definition multithread.hh:284
Definition multithread.hh:360
Classes for synchronizing among multiple CPUs, particularly in the Linux kernel.
Timestamp operator*(const Timestamp &a, double b)
Scale a by a factor of b and return the result.
Definition timestamp.hh:1447
Click's vector container template.