1313#define HASHLIB_H
1414
1515#include < array>
16+ #include < atomic>
1617#include < stdexcept>
1718#include < algorithm>
1819#include < optional>
@@ -1355,8 +1356,18 @@ class idict
13551356template <typename K, typename OPS>
13561357class mfp
13571358{
1358- mutable idict<K, 0 , OPS> database;
1359- mutable std::vector<int > parents;
1359+ idict<K, 0 , OPS> database;
1360+ class AtomicParent {
1361+ public:
1362+ explicit AtomicParent (int p) : parent(p) {}
1363+ AtomicParent (const AtomicParent &other) : parent(other.get()) {}
1364+ AtomicParent &operator =(const AtomicParent &other) { set (other.get ()); return *this ; }
1365+ int get () const { return parent.load (std::memory_order_relaxed); }
1366+ void set (int p) { parent.store (p, std::memory_order_relaxed); }
1367+ private:
1368+ std::atomic<int > parent;
1369+ };
1370+ std::vector<AtomicParent> parents;
13601371
13611372public:
13621373 typedef typename idict<K, 0 >::const_iterator const_iterator;
@@ -1367,12 +1378,14 @@ class mfp
13671378
13681379 // Finds a given element's index. If it isn't in the data structure,
13691380 // it is added as its own set
1370- int operator ()(const K &key) const
1381+ int operator ()(const K &key)
13711382 {
13721383 int i = database (key);
13731384 // If the lookup caused the database to grow,
13741385 // also add a corresponding entry in parents initialized to -1 (no parent)
1375- parents.resize (database.size (), -1 );
1386+ if (parents.size () < database.size ()) {
1387+ parents.emplace_back (-1 );
1388+ }
13761389 return i;
13771390 }
13781391
@@ -1382,21 +1395,38 @@ class mfp
13821395 return database[index];
13831396 }
13841397
1398+ // Why this method is correct for concurent ifind() calls:
1399+ // Consider the mfp state after the last non-const method call before
1400+ // a particular call to ifind(i). In this state, i's parent chain leads
1401+ // to some root R. Let S be the set of integers s such that ifind(s) = R
1402+ // in this state. Let 'orig_parents' be the value of 'parents' in this state.
1403+ //
1404+ // Now consider the concurrent calls to ifind(s), s ∈ S, before the next non-const method
1405+ // call. Consider the atomic writes performed by various ifind() calls, in any causally
1406+ // consistent order. The first atomic write can only set parents[k] to R, because the
1407+ // atomic read of parents[p] in the first while loop can only observe the value
1408+ // 'orig_parents[p]'. Subsequent writes can also only set parents[k] to R, because the
1409+ // parents[p] reads either observe 'orig_parents[p]' or R (and observing R ends the first
1410+ // while loop immediately). Thus all parents[p] reads observe either 'orig_parents[p]'
1411+ // or R, so ifind() always returns R.
13851412 int ifind (int i) const
13861413 {
13871414 int p = i, k = i;
13881415
1389- while (parents[p] != -1 )
1390- p = parents[p];
1391-
1416+ while (true ) {
1417+ int pp = parents[p].get ();
1418+ if (pp < 0 )
1419+ break ;
1420+ p = pp;
1421+ }
13921422 // p is now the representative of i
13931423 // Now we traverse from i up to the representative again
13941424 // and make p the parent of all the nodes along the way.
13951425 // This is a side effect and doesn't affect the return value.
13961426 // It speeds up future find operations
13971427 while (k != p) {
1398- int next_k = parents[k];
1399- parents[k] = p ;
1428+ int next_k = parents[k]. get () ;
1429+ const_cast <AtomicParent*>(& parents[k])-> set (p) ;
14001430 k = next_k;
14011431 }
14021432
@@ -1411,23 +1441,23 @@ class mfp
14111441 j = ifind (j);
14121442
14131443 if (i != j)
1414- parents[i] = j ;
1444+ parents[i]. set (j) ;
14151445 }
14161446
14171447 void ipromote (int i)
14181448 {
14191449 int k = i;
14201450
14211451 while (k != -1 ) {
1422- int next_k = parents[k];
1423- parents[k] = i ;
1452+ int next_k = parents[k]. get () ;
1453+ parents[k]. set (i) ;
14241454 k = next_k;
14251455 }
14261456
1427- parents[i] = - 1 ;
1457+ parents[i]. set (- 1 ) ;
14281458 }
14291459
1430- int lookup (const K &a) const
1460+ int lookup (const K &a)
14311461 {
14321462 return ifind ((*this )(a));
14331463 }
0 commit comments