diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.4.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.4.adoc index bb4452fcdd5c..072648f73b47 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.4.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.10.4.adoc @@ -15,7 +15,8 @@ on GitHub. ==== Bug Fixes -* ❓ +* Fixed potential locking issue with `ExclusiveResource` in the + `HierarchicalTestExecutorService`, which could lead to deadlocks in certain scenarios. ==== Deprecations and Breaking Changes diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/CompositeLock.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/CompositeLock.java index 18b628b0fdde..3718d82a64fc 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/CompositeLock.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/CompositeLock.java @@ -28,7 +28,7 @@ class CompositeLock implements ResourceLock { // for tests only List getLocks() { - return locks; + return this.locks; } @Override @@ -38,9 +38,9 @@ public ResourceLock acquire() throws InterruptedException { } private void acquireAllLocks() throws InterruptedException { - List acquiredLocks = new ArrayList<>(locks.size()); + List acquiredLocks = new ArrayList<>(this.locks.size()); try { - for (Lock lock : locks) { + for (Lock lock : this.locks) { lock.lockInterruptibly(); acquiredLocks.add(lock); } @@ -53,7 +53,7 @@ private void acquireAllLocks() throws InterruptedException { @Override public void release() { - release(locks); + release(this.locks); } private void release(List acquiredLocks) { @@ -64,20 +64,20 @@ private void release(List acquiredLocks) { private class CompositeLockManagedBlocker implements ForkJoinPool.ManagedBlocker { - private boolean acquired; + private volatile boolean acquired; @Override public boolean block() throws InterruptedException { - if (!acquired) { + if (!this.acquired) { acquireAllLocks(); - acquired = true; + this.acquired = true; } return true; } @Override public boolean isReleasable() { - return acquired; + return this.acquired; } } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleLock.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleLock.java index b5f6e80d8089..e568b9a8f5c0 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleLock.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleLock.java @@ -26,7 +26,7 @@ class SingleLock implements ResourceLock { // for tests only Lock getLock() { - return lock; + return this.lock; } @Override @@ -37,25 +37,25 @@ public ResourceLock acquire() throws InterruptedException { @Override public void release() { - lock.unlock(); + this.lock.unlock(); } private class SingleLockManagedBlocker implements ForkJoinPool.ManagedBlocker { - private boolean acquired; + private volatile boolean acquired; @Override public boolean block() throws InterruptedException { - if (!acquired) { - lock.lockInterruptibly(); - acquired = true; + if (!this.acquired) { + SingleLock.this.lock.lockInterruptibly(); + this.acquired = true; } return true; } @Override public boolean isReleasable() { - return acquired || (acquired = lock.tryLock()); + return this.acquired || (this.acquired = SingleLock.this.lock.tryLock()); } }