Skip to content

Commit

Permalink
Fixes 841: Incorrect reference used in compareAndSet in CTrie.cleanTo…
Browse files Browse the repository at this point in the history
…mb. (#842)

* Fixes 841: cleanTomb used compareAndSet to update a reference, but
incorrectly re-fetched the 'original' instead of using the version that
was used to make the copy.

* Fixed possible race condition in cleanTomb that could result in the removal of a live node
  • Loading branch information
hylkevds authored Aug 17, 2024
1 parent c713730 commit 9e1fed1
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 9 deletions.
1 change: 1 addition & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Version 0.18-SNAPSHOT:
[fix] Incorrect reference used in compareAndSet in CTrie.cleanTomb. (#841)
[feature] Implement response-information property for request-response flow. (#840)
[fix] Optimised page file opening for disk-based queues. (#837)
[feature] Manage payload format indicator property, when set verify payload format. (#826)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ public void add(INode newINode) {
}
}

public void remove(INode node) {
public INode remove(INode node) {
int idx = findIndexForToken(node.mainNode().token);
this.children.remove(idx);
return this.children.remove(idx);
}

private List<Subscription> sharedSubscriptions() {
Expand Down
19 changes: 12 additions & 7 deletions broker/src/main/java/io/moquette/broker/subscriptions/CTrie.java
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,7 @@ private Action remove(String clientId, Topic topic, INode inode, INode iParent,
}
}
if (cnode instanceof TNode) {
// this inode is a tomb, has no clients and should be cleaned up
// Because we implemented cleanTomb below, this should be rare, but possible
// Consider calling cleanTomb here too
return Action.OK;
return cleanTomb(inode, iParent);
}
if (cnode.containsOnly(clientId) && topic.isEmpty() && cnode.allChildren().isEmpty()) {
// last client to leave this node, AND there are no downstream children, remove via TNode tomb
Expand Down Expand Up @@ -393,9 +390,17 @@ private Action remove(String clientId, Topic topic, INode inode, INode iParent,
* @return REPEAT if this method wasn't successful or OK.
*/
private Action cleanTomb(INode inode, INode iParent) {
CNode updatedCnode = iParent.mainNode().copy();
updatedCnode.remove(inode);
return iParent.compareAndSet(iParent.mainNode(), updatedCnode) ? Action.OK : Action.REPEAT;
CNode origCnode = iParent.mainNode();
CNode updatedCnode = origCnode.copy();
INode removed = updatedCnode.remove(inode);
if (removed == inode) {
return iParent.compareAndSet(origCnode, updatedCnode) ? Action.OK : Action.REPEAT;
} else {
// The node removed (from the copy!) was not the node we expected to remove.
// Probably because another thread replaced the TNode with a live node, so
// we don't need to clean it and can return success.
return Action.OK;
}
}

public int size() {
Expand Down

0 comments on commit 9e1fed1

Please sign in to comment.