Skip to content

Conversation

@leonard84
Copy link
Contributor

SingleLock uses a ForkJoinPool.ManagedBlocker for efficient blocking in a fork-join pool. In the isReleasable() method it calls tryLock() without remembering the outcome. The block method also unconditionally called lockInterruptibly() even if the lock was already acquired. The problem that can arise is that the lock is acquired multiple times, but only released once.

CompositeLockManagedBlocker is also updated, so that block() checks for acquired before locking.

Overview


I hereby agree to the terms of the JUnit Contributor License Agreement.


Definition of Done

@leonard84 leonard84 requested a review from marcphilipp August 26, 2024 11:47
@leonard84 leonard84 self-assigned this Aug 26, 2024
@leonard84 leonard84 added this to the 5.11.1 milestone Aug 26, 2024
@leonard84
Copy link
Contributor Author

leonard84 commented Aug 26, 2024

Spock started to experience strange deadlocks the thread dumps point to the locking infrastructure being the culprit.

I checked the JavaDoc of ManagedBlocker again, and it was updated, or we didn't implement it correctly back then.

class ManagedLocker implements ManagedBlocker {
  final ReentrantLock lock;
  boolean hasLock = false;
  ManagedLocker(ReentrantLock lock) { this.lock = lock; }
  public boolean block() {
    if (!hasLock)
      lock.lock();
    return true;
  }
  public boolean isReleasable() {
    return hasLock || (hasLock = lock.tryLock());
  }
}

The JavaDoc suggests that this isn't the issue, although the example implementation guards against repeated locking, so I think we should follow its guidance.

Toward this end, implementations of method isReleasable must be amenable to repeated invocation. Neither method is invoked after a prior invocation of isReleasable or block returns true.

@leonard84
Copy link
Contributor Author

I tried to test if this branch fixes the issue in Spock's CI jobs with the help of jitpack.io. Unfortunately, it fails to build.
As they only use Java 8 by default https://docs.jitpack.io/building/#java-version

@sormuras
Copy link
Member

sormuras commented Aug 26, 2024

There was a time we used to provide a custom .jitpack.yml file...

@sormuras
Copy link
Member

sormuras commented Aug 26, 2024

I went ahead and (temporarily) added such a configuration file to your branch in order to fix the build, right? Or does it have to be present in the main branch? Let's find out.

.jitpack.yml could read:

jdk:
  - openjdk21
before_install:
  - sdk install java 21.0.2-open
  - sdk use java 21.0.2-open
install:
  - ./gradlew --version
  - ./gradlew publishToMavenLocal -x test

@sormuras
Copy link
Member

sormuras commented Aug 26, 2024

https://jitpack.io/com/github/junit-team/junit5/leo~fix-potential-lock-isssue-SNAPSHOT/build.log now reports:

Git error. Retrying.
Git error. Max repo size 500MB exceeded

Progress... now the Gradle Daemon "stalls" or so:
https://jitpack.io/com/github/junit-team/junit5/leo~fix-potential-lock-isssue-r5.11.0-ge71983c-15/build.log

[...]
Build tool exit code: 0
Looking for artifacts...
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 -Dhttps.protocols=TLSv1.2
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 -Dhttps.protocols=TLSv1.2
Could not write standard input to Gradle build daemon.
java.io.IOException: Stream closed
	at java.lang.ProcessBuilder$NullOutputStream.write(ProcessBuilder.java:433)
	at java.io.OutputStream.write(OutputStream.java:116)
[...]

@leonard84
Copy link
Contributor Author

leonard84 commented Aug 27, 2024

The actual build was successful:

BUILD SUCCESSFUL in 2m 43s
441 actionable tasks: 299 executed, 140 from cache, 2 up-to-date
Configuration cache entry stored.
Build tool exit code: 0

I think this comes from jitpack

Looking for artifacts...

as well as the actual error a bit further down

Unrecognized option: --add-exports
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

From the help message it looks like they use an ancient gradle version to do something with the artifacts.

https://docs.gradle.org/4.8.1/userguide/gradle_daemon.html

Maybe it would help if we reset Java back to 8 after building.

@leonard84
Copy link
Contributor Author

Back to

Git error. Retrying.
Git error. Max repo size 500MB exceeded

did you do something to fix it?

@sormuras
Copy link
Member

Mh, nothing special. Let me touch the file to trigger a recompilation...

@sormuras
Copy link
Member

@leonard84
Copy link
Contributor Author

Yeah, I don't have sdk man, so I was just using whatever version Gemini suggested for Java 8.

Copy link
Member

@marcphilipp marcphilipp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing that! I wonder why this is only surfacing now... 🤷

`SingleLock` uses a `ForkJoinPool.ManagedBlocker` for efficient blocking
in a fork-join pool. In the `isReleasable()` method it calls `tryLock()`
without remembering the outcome. The `block` method also unconditionally
called `lockInterruptibly()` even if the lock was already acquired.
The problem that can arise is that the lock is acquired multiple times,
but only released once.

`CompositeLockManagedBlocker` is also updated, so that `block()` checks
for `acquired` before locking.
@leonard84 leonard84 force-pushed the leo/fix-potential-lock-isssue branch from 7746959 to 98b4de9 Compare September 6, 2024 10:56
@leonard84 leonard84 merged commit 40bc1c4 into main Sep 6, 2024
@leonard84 leonard84 deleted the leo/fix-potential-lock-isssue branch September 6, 2024 11:03
@sbrannen

This comment was marked as outdated.

@sbrannen
Copy link
Member

Reopening to backport to the 5.10.x branch:

Oops. This is a PR which cannot be reopened.

See #3988 instead.

@sbrannen sbrannen modified the milestones: 5.10.4, 5.11.1 Sep 13, 2024
@sbrannen sbrannen removed this from the 5.11.1 milestone Sep 13, 2024
@sbrannen sbrannen added this to the 5.12 M1 milestone Sep 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants