|
1
|
1 #pragma once
|
|
|
2
|
|
|
3 #include <vector>
|
|
|
4 #include <set>
|
|
|
5 #include <map>
|
|
|
6 #include <memory>
|
|
|
7 #include <pfc/synchro.h>
|
|
|
8
|
|
|
9 template<typename obj_t> class notifyList : public std::vector<obj_t*> {
|
|
|
10 public:
|
|
|
11 void operator+=( obj_t * obj ) { this->push_back(obj); }
|
|
|
12 void operator-=( obj_t * obj ) {
|
|
|
13 for(auto i = this->begin(); i != this->end(); ++i) {
|
|
|
14 if (*i == obj) {this->erase(i); return;}
|
|
|
15 }
|
|
|
16 PFC_ASSERT(!"Should not get here");
|
|
|
17 }
|
|
|
18 bool contains(obj_t* obj) const {
|
|
|
19 for (auto i : *this) {
|
|
|
20 if (i == obj) return true;
|
|
|
21 }
|
|
|
22 return false;
|
|
|
23 }
|
|
|
24 };
|
|
|
25
|
|
|
26
|
|
|
27
|
|
|
28 // Efficient notifylist. Efficient addremove, ordered operation.
|
|
|
29 template<typename obj_t> class notifyList2 {
|
|
|
30 public:
|
|
|
31 typedef uint64_t key_t;
|
|
|
32 typedef obj_t * val_t;
|
|
|
33
|
|
|
34 void add( obj_t * obj ) {
|
|
|
35 auto key = m_increment++;
|
|
|
36 m_ordered[key] = obj;
|
|
|
37 m_unordered[obj] = key;
|
|
|
38 }
|
|
|
39 void remove(obj_t * obj) {
|
|
|
40 auto i1 = m_unordered.find(obj);
|
|
|
41 if (i1 != m_unordered.end()) {
|
|
|
42 m_ordered.erase(i1->second);
|
|
|
43 m_unordered.erase(i1);
|
|
|
44 }
|
|
|
45 }
|
|
|
46 void operator+=(obj_t * obj) {add(obj);}
|
|
|
47 void operator-=(obj_t * obj) {remove(obj);}
|
|
|
48
|
|
|
49 typedef std::vector<obj_t*> state_t;
|
|
|
50
|
|
|
51 state_t get() const {
|
|
|
52 state_t ret;
|
|
|
53 ret.resize(m_ordered.size());
|
|
|
54 size_t walk = 0;
|
|
|
55 for( auto i = m_ordered.begin(); i != m_ordered.end(); ++i ) {
|
|
|
56 ret[walk++] = i->second;
|
|
|
57 }
|
|
|
58 return std::move(ret);
|
|
|
59 }
|
|
|
60 size_t size() const { return m_unordered.size(); }
|
|
|
61 bool contains( obj_t * obj ) const { return m_unordered.find( obj ) != m_unordered.end(); }
|
|
|
62
|
|
|
63 key_t firstKey() const {
|
|
|
64 auto iter = m_ordered.begin();
|
|
|
65 if (iter == m_ordered.end()) return keyInvalid();
|
|
|
66 return iter->first;
|
|
|
67 }
|
|
|
68 key_t nextKey(key_t key) const {
|
|
|
69 auto iter = m_ordered.upper_bound( key );
|
|
|
70 if (iter == m_ordered.end()) return keyInvalid();
|
|
|
71 return iter->first;
|
|
|
72 }
|
|
|
73 obj_t * resolveKey( key_t key ) const {
|
|
|
74 auto iter = m_ordered.find( key );
|
|
|
75 if (iter != m_ordered.end()) return iter->second;
|
|
|
76 return nullptr;
|
|
|
77 }
|
|
|
78 bool validKey(key_t key) const {
|
|
|
79 return m_ordered.find( key ) != m_ordered.end();
|
|
|
80 }
|
|
|
81 static key_t keyInvalid() { return (key_t)(-1); }
|
|
|
82 private:
|
|
|
83
|
|
|
84 key_t m_increment;
|
|
|
85
|
|
|
86 std::map<key_t, val_t> m_ordered;
|
|
|
87 std::map<val_t, key_t> m_unordered;
|
|
|
88 };
|
|
|
89
|
|
|
90 template<typename obj_t> class notifyListUnordered : public std::set<obj_t*> {
|
|
|
91 public:
|
|
|
92 void operator+=( obj_t * obj ) {
|
|
|
93 this->insert( obj );
|
|
|
94 }
|
|
|
95 void operator-=( obj_t * obj ) {
|
|
|
96 this->erase( obj );
|
|
|
97 }
|
|
|
98 };
|
|
|
99
|
|
|
100 //! Notify list v3 \n
|
|
|
101 //! Traits: \n
|
|
|
102 //! * Ordered \n
|
|
|
103 //! * Efficient add/remove \n
|
|
|
104 //! * Handles every possible scenario of add/remove in mid enumeration \n
|
|
|
105 //! * Use begin()/end() to walk with no further workarounds \n
|
|
|
106 //! * If you've done anything between ++ and *iter - or are using the notifylist in multiple threads, check *iter for giving you a null. Otherwise, after ++ you always have a valid value, or end. \n
|
|
|
107 //! Available in two flavours, non thread safe (notifyList3<>) and thread safe (notifyList3MT<>). \n
|
|
|
108 //! Multi threaded version ensures thread safety internally - though once it gives you a pointer, there's no safeguard against removal attempt while the pointer is being used, hence not adivsed.
|
|
|
109 template<typename obj_t, typename sync_t> class notifyList3_ {
|
|
|
110 private:
|
|
|
111 typedef notifyList3_<obj_t, sync_t> self_t;
|
|
|
112 typedef notifyList2< obj_t > content_t;
|
|
|
113 public:
|
|
|
114 typedef typename content_t::key_t contentKey_t;
|
|
|
115 struct data_t {
|
|
|
116 content_t content;
|
|
|
117 sync_t sync;
|
|
|
118 };
|
|
|
119
|
|
|
120 typedef std::shared_ptr< data_t > dataRef_t;
|
|
|
121
|
|
|
122 notifyList3_( ) : m_data(std::make_shared< data_t >()) {}
|
|
|
123
|
|
|
124 void operator+=( obj_t * obj ) { add(obj); }
|
|
|
125 void operator-=( obj_t * obj ) { remove(obj); }
|
|
|
126
|
|
|
127 void add( obj_t * obj ) {
|
|
|
128 inWriteSync( m_data->sync );
|
|
|
129 m_data->content.add( obj );
|
|
|
130 }
|
|
|
131 void remove( obj_t * obj ) {
|
|
|
132 inWriteSync( m_data->sync );
|
|
|
133 m_data->content.remove( obj );
|
|
|
134 }
|
|
|
135 size_t size() const {
|
|
|
136 inReadSync( m_data->sync );
|
|
|
137 return m_data->content.size();
|
|
|
138 }
|
|
|
139
|
|
|
140 class const_iterator {
|
|
|
141 public:
|
|
|
142
|
|
|
143 const_iterator( dataRef_t ref, contentKey_t key ) : m_data(ref), m_key(key) {
|
|
|
144 }
|
|
|
145
|
|
|
146 const_iterator const & operator++(int) {
|
|
|
147 increment();
|
|
|
148 return *this;
|
|
|
149 }
|
|
|
150 const_iterator operator++() {
|
|
|
151 const_iterator ret ( *this );
|
|
|
152 increment();
|
|
|
153 return std::move(ret);
|
|
|
154 }
|
|
|
155
|
|
|
156 void increment() {
|
|
|
157 PFC_ASSERT( isValid() );
|
|
|
158 inReadSync( m_data->sync );
|
|
|
159 m_key = m_data->content.nextKey( m_key );
|
|
|
160 }
|
|
|
161
|
|
|
162 bool isValid() const {
|
|
|
163 inReadSync( m_data->sync );
|
|
|
164 return m_data->content.validKey( m_key );
|
|
|
165 }
|
|
|
166
|
|
|
167 bool operator==( const_iterator const & other ) const {
|
|
|
168 return equals( *this, other );
|
|
|
169 }
|
|
|
170 bool operator!=( const_iterator const & other ) const {
|
|
|
171 return !equals( *this, other );
|
|
|
172 }
|
|
|
173
|
|
|
174 static bool equals( const_iterator const & i1, const_iterator const & i2 ) {
|
|
|
175 return i1.m_key == i2.m_key;
|
|
|
176 }
|
|
|
177
|
|
|
178 //! Returns the referenced value. Will be null in case of invalidation in mid-enumeration.
|
|
|
179 obj_t * operator*() const {
|
|
|
180 inReadSync( m_data->sync );
|
|
|
181 return m_data->content.resolveKey( m_key );
|
|
|
182 }
|
|
|
183
|
|
|
184 private:
|
|
|
185 const_iterator() = delete;
|
|
|
186
|
|
|
187 const dataRef_t m_data;
|
|
|
188 contentKey_t m_key;
|
|
|
189 };
|
|
|
190
|
|
|
191 const_iterator begin() const {
|
|
|
192 inReadSync( m_data->sync );
|
|
|
193 return const_iterator( m_data, m_data->content.firstKey() );
|
|
|
194 }
|
|
|
195 const_iterator end() const {
|
|
|
196 inReadSync( m_data->sync );
|
|
|
197 return const_iterator( m_data, content_t::keyInvalid() );
|
|
|
198 }
|
|
|
199
|
|
|
200 private:
|
|
|
201 notifyList3_( const self_t & ) = delete;
|
|
|
202 void operator=( const self_t & ) = delete;
|
|
|
203
|
|
|
204
|
|
|
205 const dataRef_t m_data;
|
|
|
206 };
|
|
|
207
|
|
|
208 //! Thread safe notifyList3. \n
|
|
|
209 //! See: notifyList3_
|
|
|
210 template<typename obj_t>
|
|
|
211 class notifyList3MT : public notifyList3_<obj_t, pfc::readWriteLock> {
|
|
|
212 public:
|
|
|
213
|
|
|
214 };
|
|
|
215
|
|
|
216 //! Non-thread-safe notifyList3. \n
|
|
|
217 //! See: notifyList3_
|
|
|
218 template<typename obj_t>
|
|
|
219 class notifyList3 : public notifyList3_<obj_t, pfc::dummyLock> {
|
|
|
220 public:
|
|
|
221
|
|
|
222 };
|