diff --git a/src/main/java/org/qortal/block/BlockChain.java b/src/main/java/org/qortal/block/BlockChain.java index 8f6ddab7..95ecc41b 100644 --- a/src/main/java/org/qortal/block/BlockChain.java +++ b/src/main/java/org/qortal/block/BlockChain.java @@ -567,6 +567,7 @@ public class BlockChain { --height; orphanBlockData = repository.getBlockRepository().fromHeight(height); + repository.discardChanges(); // clear transaction status to prevent deadlocks Controller.getInstance().onNewBlock(orphanBlockData); } diff --git a/src/main/java/org/qortal/controller/BlockMinter.java b/src/main/java/org/qortal/controller/BlockMinter.java index aa80246d..46a29cf9 100644 --- a/src/main/java/org/qortal/controller/BlockMinter.java +++ b/src/main/java/org/qortal/controller/BlockMinter.java @@ -306,8 +306,10 @@ public class BlockMinter extends Thread { } if (newBlockMinted) { - BlockData newBlockData = newBlock.getBlockData(); // Notify Controller and broadcast our new chain to network + BlockData newBlockData = newBlock.getBlockData(); + + repository.discardChanges(); // clear transaction status to prevent deadlocks Controller.getInstance().onNewBlock(newBlockData); Network network = Network.getInstance(); diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java index 2683fb2d..406fda79 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -835,6 +835,12 @@ public class Controller extends Thread { } } + /** + * Callback for when we've received a new block. + *

+ * See WARNING for {@link EventBus#notify(Event)} + * to prevent deadlocks. + */ public void onNewBlock(BlockData latestBlockData) { // Protective copy BlockData blockDataCopy = new BlockData(latestBlockData); diff --git a/src/main/java/org/qortal/controller/Synchronizer.java b/src/main/java/org/qortal/controller/Synchronizer.java index 8dca5b05..5af2030d 100644 --- a/src/main/java/org/qortal/controller/Synchronizer.java +++ b/src/main/java/org/qortal/controller/Synchronizer.java @@ -410,6 +410,7 @@ public class Synchronizer { --ourHeight; orphanBlockData = repository.getBlockRepository().fromHeight(ourHeight); + repository.discardChanges(); // clear transaction status to prevent deadlocks Controller.getInstance().onNewBlock(orphanBlockData); } diff --git a/src/main/java/org/qortal/event/EventBus.java b/src/main/java/org/qortal/event/EventBus.java index e0014a20..63c80143 100644 --- a/src/main/java/org/qortal/event/EventBus.java +++ b/src/main/java/org/qortal/event/EventBus.java @@ -20,6 +20,21 @@ public enum EventBus { } } + /** + * WARNING: before calling this method, + * make sure repository holds no locks, e.g. by calling + * repository.discardChanges(). + *

+ * This is because event listeners might open a new + * repository session which will deadlock HSQLDB + * if it tries to CHECKPOINT. + *

+ * The HSQLDB deadlock occurs because the caller's + * repository session blocks the CHECKPOINT until + * their transaction is closed, yet event listeners + * new sessions are blocked until CHECKPOINT is + * completed, hence deadlock. + */ public void notify(Event event) { List clonedListeners;