Warning, file /graphics/krita/libs/image/3rdparty/lock_free_map/concurrent_map.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /*------------------------------------------------------------------------ 0002 Junction: Concurrent data structures in C++ 0003 Copyright (c) 2016 Jeff Preshing 0004 Distributed under the Simplified BSD License. 0005 Original location: https://github.com/preshing/junction 0006 This software is distributed WITHOUT ANY WARRANTY; without even the 0007 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 0008 See the LICENSE file for more information. 0009 ------------------------------------------------------------------------*/ 0010 0011 #ifndef CONCURRENTMAP_H 0012 #define CONCURRENTMAP_H 0013 0014 #include "leapfrog.h" 0015 #include "qsbr.h" 0016 0017 template <typename K, typename V, class KT = DefaultKeyTraits<K>, class VT = DefaultValueTraits<V> > 0018 class ConcurrentMap 0019 { 0020 public: 0021 typedef K Key; 0022 typedef V Value; 0023 typedef KT KeyTraits; 0024 typedef VT ValueTraits; 0025 typedef quint32 Hash; 0026 typedef Leapfrog<ConcurrentMap> Details; 0027 0028 private: 0029 Atomic<typename Details::Table*> m_root; 0030 QSBR m_gc; 0031 0032 public: 0033 ConcurrentMap(quint64 capacity = Details::InitialSize) : m_root(Details::Table::create(capacity)) 0034 { 0035 } 0036 0037 ~ConcurrentMap() 0038 { 0039 typename Details::Table* table = m_root.loadNonatomic(); 0040 table->destroy(); 0041 m_gc.flush(); 0042 } 0043 0044 QSBR &getGC() 0045 { 0046 return m_gc; 0047 } 0048 0049 bool migrationInProcess() 0050 { 0051 return quint64(m_root.loadNonatomic()->jobCoordinator.loadConsume()) > 1; 0052 } 0053 0054 // publishTableMigration() is called by exactly one thread from Details::TableMigration::run() 0055 // after all the threads participating in the migration have completed their work. 0056 void publishTableMigration(typename Details::TableMigration* migration) 0057 { 0058 m_root.store(migration->m_destination, Release); 0059 // Caller will GC the TableMigration and the source table. 0060 } 0061 0062 // A Mutator represents a known cell in the hash table. 0063 // It's meant for manipulations within a temporary function scope. 0064 // Obviously you must not call QSBR::Update while holding a Mutator. 0065 // Any operation that modifies the table (exchangeValue, eraseValue) 0066 // may be forced to follow a redirected cell, which changes the Mutator itself. 0067 // Note that even if the Mutator was constructed from an existing cell, 0068 // exchangeValue() can still trigger a resize if the existing cell was previously marked deleted, 0069 // or if another thread deletes the key between the two steps. 0070 class Mutator 0071 { 0072 private: 0073 friend class ConcurrentMap; 0074 0075 ConcurrentMap& m_map; 0076 typename Details::Table* m_table; 0077 typename Details::Cell* m_cell; 0078 Value m_value; 0079 0080 // Constructor: Find existing cell 0081 Mutator(ConcurrentMap& map, Key key, bool) : m_map(map), m_value(Value(ValueTraits::NullValue)) 0082 { 0083 Hash hash = KeyTraits::hash(key); 0084 for (;;) { 0085 m_table = m_map.m_root.load(Consume); 0086 m_cell = Details::find(hash, m_table); 0087 if (!m_cell) { 0088 return; 0089 } 0090 0091 Value value = m_cell->value.load(Consume); 0092 if (value != Value(ValueTraits::Redirect)) { 0093 // Found an existing value 0094 m_value = value; 0095 return; 0096 } 0097 // We've encountered a Redirect value. Help finish the migration. 0098 m_table->jobCoordinator.participate(); 0099 // Try again using the latest root. 0100 } 0101 } 0102 0103 // Constructor: Insert or find cell 0104 Mutator(ConcurrentMap& map, Key key) : m_map(map), m_value(Value(ValueTraits::NullValue)) 0105 { 0106 Hash hash = KeyTraits::hash(key); 0107 for (;;) { 0108 m_table = m_map.m_root.load(Consume); 0109 quint64 overflowIdx; 0110 switch (Details::insertOrFind(hash, m_table, m_cell, overflowIdx)) { // Modifies m_cell 0111 case Details::InsertResult_InsertedNew: { 0112 // We've inserted a new cell. Don't load m_cell->value. 0113 return; 0114 } 0115 case Details::InsertResult_AlreadyFound: { 0116 // The hash was already found in the table. 0117 Value value = m_cell->value.load(Consume); 0118 if (value == Value(ValueTraits::Redirect)) { 0119 // We've encountered a Redirect value. 0120 break; // Help finish the migration. 0121 } 0122 // Found an existing value 0123 m_value = value; 0124 return; 0125 } 0126 case Details::InsertResult_Overflow: { 0127 // Unlike ConcurrentMap_Linear, we don't need to keep track of & pass a "mustDouble" flag. 0128 // Passing overflowIdx is sufficient to prevent an infinite loop here. 0129 // It defines the start of the range of cells to check while estimating total cells in use. 0130 // After the first migration, deleted keys are purged, so if we hit this line during the 0131 // second loop iteration, every cell in the range will be in use, thus the estimate will be 100%. 0132 // (Concurrent deletes could result in further iterations, but it will eventually settle.) 0133 Details::beginTableMigration(m_map, m_table, overflowIdx); 0134 break; 0135 } 0136 } 0137 // A migration has been started (either by us, or another thread). Participate until it's complete. 0138 m_table->jobCoordinator.participate(); 0139 // Try again using the latest root. 0140 } 0141 } 0142 0143 public: 0144 Value getValue() const 0145 { 0146 // Return previously loaded value. Don't load it again. 0147 return Value(m_value); 0148 } 0149 0150 Value exchangeValue(Value desired) 0151 { 0152 for (;;) { 0153 Value oldValue = m_value; 0154 if (m_cell->value.compareExchangeStrong(m_value, desired, ConsumeRelease)) { 0155 // Exchange was successful. Return previous value. 0156 Value result = m_value; 0157 m_value = desired; // Leave the mutator in a valid state 0158 return result; 0159 } 0160 // The CAS failed and m_value has been updated with the latest value. 0161 if (m_value != Value(ValueTraits::Redirect)) { 0162 if (oldValue == Value(ValueTraits::NullValue) && m_value != Value(ValueTraits::NullValue)) { 0163 // racing write inserted new value 0164 } 0165 // There was a racing write (or erase) to this cell. 0166 // Pretend we exchanged with ourselves, and just let the racing write win. 0167 return desired; 0168 } 0169 0170 // We've encountered a Redirect value. Help finish the migration. 0171 Hash hash = m_cell->hash.load(Relaxed); 0172 for (;;) { 0173 // Help complete the migration. 0174 m_table->jobCoordinator.participate(); 0175 // Try again in the new table. 0176 m_table = m_map.m_root.load(Consume); 0177 m_value = Value(ValueTraits::NullValue); 0178 quint64 overflowIdx; 0179 0180 switch (Details::insertOrFind(hash, m_table, m_cell, overflowIdx)) { // Modifies m_cell 0181 case Details::InsertResult_AlreadyFound: 0182 m_value = m_cell->value.load(Consume); 0183 if (m_value == Value(ValueTraits::Redirect)) { 0184 break; 0185 } 0186 goto breakOuter; 0187 case Details::InsertResult_InsertedNew: 0188 goto breakOuter; 0189 case Details::InsertResult_Overflow: 0190 Details::beginTableMigration(m_map, m_table, overflowIdx); 0191 break; 0192 } 0193 // We were redirected... again 0194 } 0195 breakOuter:; 0196 // Try again in the new table. 0197 } 0198 } 0199 0200 void assignValue(Value desired) 0201 { 0202 exchangeValue(desired); 0203 } 0204 0205 Value eraseValue() 0206 { 0207 for (;;) { 0208 if (m_value == Value(ValueTraits::NullValue)) { 0209 return Value(m_value); 0210 } 0211 0212 if (m_cell->value.compareExchangeStrong(m_value, Value(ValueTraits::NullValue), Consume)) { 0213 // Exchange was successful and a non-NULL value was erased and returned by reference in m_value. 0214 Value result = m_value; 0215 m_value = Value(ValueTraits::NullValue); // Leave the mutator in a valid state 0216 return result; 0217 } 0218 0219 // The CAS failed and m_value has been updated with the latest value. 0220 if (m_value != Value(ValueTraits::Redirect)) { 0221 // There was a racing write (or erase) to this cell. 0222 // Pretend we erased nothing, and just let the racing write win. 0223 return Value(ValueTraits::NullValue); 0224 } 0225 0226 // We've been redirected to a new table. 0227 Hash hash = m_cell->hash.load(Relaxed); // Re-fetch hash 0228 for (;;) { 0229 // Help complete the migration. 0230 m_table->jobCoordinator.participate(); 0231 // Try again in the new table. 0232 m_table = m_map.m_root.load(Consume); 0233 m_cell = Details::find(hash, m_table); 0234 if (!m_cell) { 0235 m_value = Value(ValueTraits::NullValue); 0236 return m_value; 0237 } 0238 0239 m_value = m_cell->value.load(Relaxed); 0240 if (m_value != Value(ValueTraits::Redirect)) { 0241 break; 0242 } 0243 } 0244 } 0245 } 0246 }; 0247 0248 Mutator insertOrFind(Key key) 0249 { 0250 return Mutator(*this, key); 0251 } 0252 0253 Mutator find(Key key) 0254 { 0255 return Mutator(*this, key, false); 0256 } 0257 0258 // Lookup without creating a temporary Mutator. 0259 Value get(Key key) 0260 { 0261 Hash hash = KeyTraits::hash(key); 0262 for (;;) { 0263 typename Details::Table* table = m_root.load(Consume); 0264 typename Details::Cell* cell = Details::find(hash, table); 0265 if (!cell) { 0266 return Value(ValueTraits::NullValue); 0267 } 0268 0269 Value value = cell->value.load(Consume); 0270 if (value != Value(ValueTraits::Redirect)) { 0271 return value; // Found an existing value 0272 } 0273 // We've been redirected to a new table. Help with the migration. 0274 table->jobCoordinator.participate(); 0275 // Try again in the new table. 0276 } 0277 } 0278 0279 Value assign(Key key, Value desired) 0280 { 0281 Mutator iter(*this, key); 0282 return iter.exchangeValue(desired); 0283 } 0284 0285 Value exchange(Key key, Value desired) 0286 { 0287 Mutator iter(*this, key); 0288 return iter.exchangeValue(desired); 0289 } 0290 0291 Value erase(Key key) 0292 { 0293 Mutator iter(*this, key, false); 0294 return iter.eraseValue(); 0295 } 0296 0297 // The easiest way to implement an Iterator is to prevent all Redirects. 0298 // The currrent Iterator does that by forbidding concurrent inserts. 0299 // To make it work with concurrent inserts, we'd need a way to block TableMigrations. 0300 class Iterator 0301 { 0302 private: 0303 typename Details::Table* m_table; 0304 quint64 m_idx; 0305 Key m_hash; 0306 Value m_value; 0307 0308 public: 0309 Iterator() = default; 0310 Iterator(ConcurrentMap& map) 0311 { 0312 // Since we've forbidden concurrent inserts (for now), nonatomic would suffice here, but let's plan ahead: 0313 m_table = map.m_root.load(Consume); 0314 m_idx = -1; 0315 next(); 0316 } 0317 0318 void setMap(ConcurrentMap& map) 0319 { 0320 m_table = map.m_root.load(Consume); 0321 m_idx = -1; 0322 next(); 0323 } 0324 0325 void next() 0326 { 0327 while (++m_idx <= m_table->sizeMask) { 0328 // Index still inside range of table. 0329 typename Details::CellGroup* group = m_table->getCellGroups() + (m_idx >> 2); 0330 typename Details::Cell* cell = group->cells + (m_idx & 3); 0331 m_hash = cell->hash.load(Relaxed); 0332 0333 if (m_hash != KeyTraits::NullHash) { 0334 // Cell has been reserved. 0335 m_value = cell->value.load(Relaxed); 0336 if (m_value != Value(ValueTraits::NullValue)) 0337 return; // Yield this cell. 0338 } 0339 } 0340 // That's the end of the map. 0341 m_hash = KeyTraits::NullHash; 0342 m_value = Value(ValueTraits::NullValue); 0343 } 0344 0345 bool isValid() const 0346 { 0347 #ifdef SANITY_CHECK 0348 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_value != Value(ValueTraits::Redirect), false); 0349 #endif 0350 return m_value != Value(ValueTraits::NullValue); 0351 } 0352 0353 Key getKey() const 0354 { 0355 // Since we've forbidden concurrent inserts (for now), nonatomic would suffice here, but let's plan ahead: 0356 return KeyTraits::dehash(m_hash); 0357 } 0358 0359 Value getValue() const 0360 { 0361 return m_value; 0362 } 0363 }; 0364 }; 0365 0366 #endif // CONCURRENTMAP_LEAPFROG_H