Skip to content

Commit f9ab25a

Browse files
committed
Fix optimistic locking in parallel B-tree find
- takes a root lease before reading root - takes a node lease before reading keys, numElements, and children - validates the node lease before returning found/not-found - validates the parent after taking the child lease - restarts if a child pointer is null from an invalidated snapshot
1 parent 3f1b771 commit f9ab25a

1 file changed

Lines changed: 87 additions & 1 deletion

File tree

  • src/include/souffle/datastructure

src/include/souffle/datastructure/BTree.h

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1026,7 +1026,7 @@ class btree {
10261026
node* volatile root;
10271027

10281028
// a lock to synchronize update operations on the root pointer
1029-
lock_type root_lock;
1029+
mutable lock_type root_lock;
10301030
#else
10311031
// a pointer to the root node of this tree
10321032
node* root;
@@ -1574,6 +1574,91 @@ class btree {
15741574
* referencing its position. If not found, an end-iterator will be returned.
15751575
*/
15761576
iterator find(const Key& k, operation_hints& hints) const {
1577+
#ifdef IS_PARALLEL
1578+
node* cur = nullptr;
1579+
lock_type::Lease cur_lease;
1580+
1581+
auto checkHints = [&](node* last_find_end) {
1582+
if (!last_find_end) return false;
1583+
1584+
auto hint_lease = last_find_end->lock.start_read();
1585+
if (!covers(last_find_end, k)) return false;
1586+
if (!last_find_end->lock.validate(hint_lease)) return false;
1587+
1588+
cur = last_find_end;
1589+
cur_lease = hint_lease;
1590+
return true;
1591+
};
1592+
1593+
// test last location searched (temporal locality)
1594+
if (hints.last_find_end.any(checkHints)) {
1595+
// register it as a hit
1596+
hint_stats.contains.addHit();
1597+
} else {
1598+
// register it as a miss
1599+
hint_stats.contains.addMiss();
1600+
}
1601+
1602+
if (!cur) {
1603+
do {
1604+
auto root_lease = root_lock.start_read();
1605+
cur = root;
1606+
1607+
if (cur == nullptr) {
1608+
if (root_lock.end_read(root_lease)) {
1609+
return end();
1610+
}
1611+
continue;
1612+
}
1613+
1614+
cur_lease = cur->lock.start_read();
1615+
1616+
if (root_lock.end_read(root_lease)) {
1617+
break;
1618+
}
1619+
} while (true);
1620+
}
1621+
1622+
while (true) {
1623+
auto a = &(cur->keys[0]);
1624+
auto b = &(cur->keys[cur->numElements]);
1625+
1626+
auto pos = search(k, a, b, comp);
1627+
1628+
if (pos < b && equal(*pos, k)) {
1629+
if (!cur->lock.validate(cur_lease)) {
1630+
return find(k, hints);
1631+
}
1632+
hints.last_find_end.access(cur);
1633+
return iterator(cur, static_cast<field_index_type>(pos - a));
1634+
}
1635+
1636+
if (!cur->inner) {
1637+
if (!cur->lock.validate(cur_lease)) {
1638+
return find(k, hints);
1639+
}
1640+
hints.last_find_end.access(cur);
1641+
return end();
1642+
}
1643+
1644+
// continue search in child node
1645+
auto next = cur->getChild(pos - a);
1646+
if (next == nullptr) {
1647+
if (cur->lock.validate(cur_lease)) {
1648+
assert(false && "B-tree inner node has null child");
1649+
}
1650+
return find(k, hints);
1651+
}
1652+
1653+
auto next_lease = next->lock.start_read();
1654+
if (!cur->lock.end_read(cur_lease)) {
1655+
return find(k, hints);
1656+
}
1657+
1658+
cur = next;
1659+
cur_lease = next_lease;
1660+
}
1661+
#else
15771662
if (empty()) {
15781663
return end();
15791664
}
@@ -1617,6 +1702,7 @@ class btree {
16171702
// continue search in child node
16181703
cur = cur->getChild(pos - a);
16191704
}
1705+
#endif
16201706
}
16211707

16221708
/**

0 commit comments

Comments
 (0)