New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8266421: Deadlock in Sound System #4141
Conversation
|
/remove label 2d |
@mrserb Unknown command |
/label remove 2d |
@mrserb |
Webrevs
|
if (this.started != started) { | ||
this.started = started; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt that this type of synchronization may help something - the fields are volatile and we use sendEvents flag after synchronization block, so I removed it as well. Any thoughts?
These fields are volatile, but the comparison and assignment is not atomic.
So I believe it is possible the case when we will have sendEvents == true
in two threads, hence we will send a duplicate event.
So we probably might want to use AtomicBoolean
if you want to get rid of synchronized
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could make sense only if the "sendEvents" flag will be used under some kind of synchronization as well, otherwise, it is possible to get sendEvents=true twice, it was not changed after this fix:
synchronized (this) {
if (this.started != started) {
this.started = started;
sendEvents = true;
}
}
<<< here the other thread could set "started" to the opposite state and we post events twice.
if (sendEvents) {
.....
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The case with setting opposite state you are describing is a different one. We will post two different events like start and stop.
I am talking about calling setStarted()
with same parameter from different threads.
For example we have two threads T1 and T2 calling setStarted(true)
.
Before the fix only one of thread will set sendEvents
to true
due to synchronized
block.
After the fix following is possible:
// this.started == false, started == true
if (this.started != started) { //T1 passed this check
// <<<<< at this moment T2 checks the statement above
this.started = started; // T1 and T2 assigns same new value to this.started
sendEvents = true; // both threads sets sendEvents to true.
}
// sending two start events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My example was about the thread-safety of the method, after synchronized block, the other thread may change the state, then post event, and then this thread will post event. So we will post the event twice in the wrong order. So this method as-is is not thread-safe, we should not call it in parallel. I'll post data for its usage here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- The changes in the AbstractDataLine.setActive() are fine, we do not post events there.
- The changes in the AbstractDataLine.setStarted() are used and syncronised:
used in AbstractDataLine:343- the setEOM() dead unused non-public method
used in AbstractDataLine:205 - syncronised on synchronized(mixer); Also dead code, the setStarted() is never executed, since the line is stopped already a few lines above in the implStop();
used in DirectAudioDevice:533 - synchronized on synchronized(lock) and synchronized(mixer)
used in DirectAudioDevice:560 - synchronized on synchronized(lock) and synchronized(mixer)
used in DirectAudioDevice:707 - synchronized on synchronized(lock)
used in DirectAudioDevice:932 - synchronized on synchronized(lock) - The changes in the AbstractLine.setOpen() are used and synchronised:
used in AbstractDataLine:118 - syncronised on synchronized(mixer);
used in AbstractDataLine:374 - syncronised on synchronized(mixer);
used in AbstractMixer:288 - syncronised on this;
used in AbstractMixer:377 - syncronised on this;
used in PortMixer:277 - syncronised on synchronized(mixer);
used in PortMixer:293 - syncronised on synchronized(mixer);
So every object uses its own high-level synchronization when it calls the methods in question.
System.out.println("Thread " + thread + " Play " | ||
+ System.currentTimeMillis() % 100000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This println
spams a lot.
It took a ~1 second less to complete the test when these lines are commented(~8s vs ~9s, I believe it will took more on slower machines).
So we probably could comment out these lines to reduce test execution time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is updated.
@mrserb This change now passes all automated pre-integration checks. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 230 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.
|
/integrate |
@mrserb Since your change was applied there have been 270 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit f6f82c3. |
In the fix for JDK-8207150 I have updated the synchronization of some code paths under one "lock", before that code was synchronized only on some threads and missing on others. Old review:
http://mail.openjdk.java.net/pipermail/sound-dev/2018-August/000650.html
That fix introduced this order of locks: "lock"->"synchronized(this)", I have checked other places and did not found where we use the opposite order. Unfortunately, one such place exists in the private subclass DirectClip.
I have checked both usages of synchronized which caused deadlock:
I doubt that this type of synchronization may help something - the fields are volatile and we use sendEvents flag after synchronisation block, so I removed it as well. Any thoughts?
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/4141/head:pull/4141
$ git checkout pull/4141
Update a local copy of the PR:
$ git checkout pull/4141
$ git pull https://git.openjdk.java.net/jdk pull/4141/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 4141
View PR using the GUI difftool:
$ git pr show -t 4141
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/4141.diff