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