Skip to content

Commit b7a7281

Browse files
authored
Merge pull request #5408 from rocallahan/atomic-mfp
Make `mfp` const methods thread-safe.
2 parents ba1a347 + 2f81c55 commit b7a7281

File tree

2 files changed

+45
-14
lines changed

2 files changed

+45
-14
lines changed

kernel/hashlib.h

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
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
13551356
template<typename K, typename OPS>
13561357
class 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

13611372
public:
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
}

kernel/yosys_common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#define YOSYS_COMMON_H
2222

2323
#include <array>
24+
#include <atomic>
2425
#include <map>
2526
#include <set>
2627
#include <tuple>

0 commit comments

Comments
 (0)