I’ve been examining Folly’s implementation of RCU.h, particularly how half_sync
is used within the retire
function. The relevant snippet is shown as follows:
void retire(list_node* node) noexcept {
// ...
if (...) {
list_head finished;
{
std::lock_guard<std::mutex> g(syncMutex_);
half_sync(false, finished);
}
// ...
}
}
void half_sync(bool blocking, list_head& finished) {
auto curr = version_.load(std::memory_order_acquire);
auto next = curr + 1;
// ...
if (blocking) {
counters_.waitForZero(next & 1);
} else {
if (!counters_.epochIsClear(next & 1)) {
return;
}
}
// ...
}
From my understanding, half_sync
waits or checks (depending on blocking
) for all read-side critical sections to move to the current epoch and then increments the version_
, performing memory reclamation on items retired two epochs ago (by adding the items in the finished
pointer, allowing the reclamation to begin from other functions).
The retire
function calls half_sync(false)
(non-blocking), but the comments suggest that using half_sync(true)
(blocking) might cause a deadlock:
Note that it’s likely we hold a read lock here, so we can only
half_sync(false)
.half_sync(true)
or asynchronize()
call might block forever.
I haven’t encountered any issues when running tests with half_sync(true)
. Could someone provide a simple example that illustrates how this deadlock may occur?
RSIMB GO is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.