Skip to content

Commit db12910

Browse files
committed
Fix DCLP race on nbrCache
1 parent bf20439 commit db12910

1 file changed

Lines changed: 25 additions & 21 deletions

File tree

include/dbscan/grid.h

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#pragma once
2525

26+
#include <atomic>
2627
#include <mutex>
2728
#include "cell.h"
2829
#include "point.h"
@@ -77,7 +78,7 @@ struct grid {
7778
tableT* table=NULL;
7879
treeT* tree=NULL;
7980
intT totalPoints;
80-
cellBuf **nbrCache;
81+
std::atomic<cellBuf*>* nbrCache;
8182
std::mutex* cacheLocks;
8283

8384
/**
@@ -90,11 +91,11 @@ struct grid {
9091
r(rr), pMin(pMinn), cellCapacity(cellMax), totalPoints(0) {
9192

9293
cells = newA(cellT, cellCapacity);
93-
nbrCache = newA(cellBuf*, cellCapacity);
94+
nbrCache = new std::atomic<cellBuf*>[cellCapacity];
9495
cacheLocks = (std::mutex*) malloc(cellCapacity * sizeof(std::mutex));
9596
parallel_for(0, cellCapacity, [&](intT i) {
9697
new (&cacheLocks[i]) std::mutex();
97-
nbrCache[i] = NULL;
98+
nbrCache[i].store(nullptr, std::memory_order_relaxed);
9899
cells[i].init();
99100
});
100101
numCells = 0;
@@ -107,9 +108,10 @@ struct grid {
107108
free(cells);
108109
free(cacheLocks);
109110
parallel_for(0, cellCapacity, [&](intT i) {
110-
if(nbrCache[i]) delete nbrCache[i];
111+
auto cached = nbrCache[i].load(std::memory_order_relaxed);
112+
if(cached) delete cached;
111113
});
112-
free(nbrCache);
114+
delete[] nbrCache;
113115
if(myHash) delete myHash;
114116
if(table) {
115117
table->del();
@@ -147,22 +149,24 @@ struct grid {
147149
}
148150
return false;};//todo, optimize
149151
int idx = bait - cells;
150-
if (nbrCache[idx]) {
151-
auto accum = nbrCache[idx];
152-
for (auto accum_i : *accum) {
152+
// Acquire ensures vector contents are visible if pointer is non-null
153+
auto cached = nbrCache[idx].load(std::memory_order_acquire);
154+
if (cached) {
155+
for (auto accum_i : *cached) {
153156
if(fWrap(accum_i)) break;
154157
}
155158
} else {
156-
// wait for other threads to do their thing then try again
157159
std::lock_guard<std::mutex> lock(cacheLocks[idx]);
158-
if (nbrCache[idx]) {
159-
auto accum = nbrCache[idx];
160-
for (auto accum_i : *accum) {
160+
cached = nbrCache[idx].load(std::memory_order_relaxed);
161+
if (cached) {
162+
for (auto accum_i : *cached) {
161163
if (fWrap(accum_i)) break;
162164
}
163165
} else {
164166
floatT hop = sqrt(dim + 3) * 1.0000001;
165-
nbrCache[idx] = tree->rangeNeighbor(bait, r * hop, fStop, fWrap, true, nbrCache[idx]);
167+
auto result = tree->rangeNeighbor(bait, r * hop, fStop, fWrap, true, (cellBuf*)nullptr);
168+
// Release ensures vector contents are fully written before pointer is visible
169+
nbrCache[idx].store(result, std::memory_order_release);
166170
}
167171
}
168172
}
@@ -176,22 +180,22 @@ struct grid {
176180
return false;
177181
};
178182
int idx = bait - cells;
179-
if (nbrCache[idx]) {
180-
auto accum = nbrCache[idx];
181-
for (auto accum_i : *accum) {
183+
auto cached = nbrCache[idx].load(std::memory_order_acquire);
184+
if (cached) {
185+
for (auto accum_i : *cached) {
182186
if (fWrap(accum_i)) break;
183187
}
184188
} else {
185-
// wait for other threads to do their thing then try again
186189
std::lock_guard<std::mutex> lock(cacheLocks[idx]);
187-
if (nbrCache[idx]) {
188-
auto accum = nbrCache[idx];
189-
for (auto accum_i : *accum) {
190+
cached = nbrCache[idx].load(std::memory_order_relaxed);
191+
if (cached) {
192+
for (auto accum_i : *cached) {
190193
if (fWrap(accum_i)) break;
191194
}
192195
} else {
193196
floatT hop = sqrt(dim + 3) * 1.0000001;
194-
nbrCache[bait-cells] = tree->rangeNeighbor(bait, r * hop, fStop, fWrap, true, nbrCache[idx]);
197+
auto result = tree->rangeNeighbor(bait, r * hop, fStop, fWrap, true, (cellBuf*)nullptr);
198+
nbrCache[idx].store(result, std::memory_order_release);
195199
}
196200
}
197201
}

0 commit comments

Comments
 (0)