JDK-8266421 : Deadlock in Sound System
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.sound
  • Affected Version: 8u202,11.0.2,16,17
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: linux_ubuntu
  • CPU: x86_64
  • Submitted: 2021-05-02
  • Updated: 2022-03-08
  • Resolved: 2021-06-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 17
11.0.15Fixed 17 b26Fixed
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
JDK 16.0.01+9
Ubuntu 18.04.6

A DESCRIPTION OF THE PROBLEM :
Thread 1

Calls
clip.setMicrosecondPosition(0); 
to move clip to start
If this has the following stack trace holding the lock on the DirectAudioDevice object and waiting for the lock for attribute lock
As seen in the following stack trace
    
synchronized DirectAudioDevice.setMicrosecondPosition(long microseconds) {
    ...
    setFramePosition(frames);
    ...
}
    
DirectAudioDevice.setFramePosition(int frames) {
    ...
    flush();
    ...
}

DirectAudioDevice.flush() {
    ...
    synchronized(lock) {
       lock.notifyAll();   
    }
    ...
}

Daemon Thread [Direct Clip]

At the same time is running the clip writing data out the stream and holds the lock on the lock attribute and is waiting for the lock on the DirectAudioDevice object 
As seen in the following stack trace

DirectAudioDevice.run() {
    ...
    int written = write(audioData, clipBytePosition, toWriteBytes)
    ...
}

DirectAudioDevice.write(byte[] b, int off, int len) {
    ...
    synchronized(lock) {
        if (!isActive() && doIO) {
            // this is not exactly correct... would be nicer
            // if the native sub system sent a callback when IO really
            // starts
            setActive(true);
            setStarted(true);
        }
    }
    ...
}

DirectAudioDevice.setActive(boolean active) {
    synchronized (this) {
         if (this.active != active) {
                this.active = active;
                //sendEvents = true;
         }
    }
}

Is it wise / right to lock on the object and an attribute? Why do we not use a single semaphore?
If we do need two, the the order must be the same in all stack traces to avoid deadlock.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See stack trace demonstrating how deadlock can occur in description

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
System stops with deadlock
ACTUAL -
System stops with deadlock (It is my AWT Event Thread calling setMicrosecondPosition())

FREQUENCY : occasionally



Comments
Fix request (11u) This is the fix for a deadlock introduced in 11.0.2. The patch does not apply cleanly due to changes in the copyright and some changes in the context. The code of the patch is the same. The new test passed. The javax/sound tests are green. PR: https://github.com/openjdk/jdk11u-dev/pull/719
16-12-2021

Mail to submitter: ============= The issue has been resolved in build:26 of JDK version:17[1] Please share your feedback checking in the latest Early access build. [1] https://jdk.java.net/17/
29-06-2021

Changeset: f6f82c31 Author: Sergey Bylokhov <serb@openjdk.org> Date: 2021-06-09 00:14:40 +0000 URL: https://git.openjdk.java.net/jdk/commit/f6f82c31689e68f4263bbbe2314033d69f9049f5
09-06-2021

The "setMicrosecondPosition" method should not be synchronized since it calls just one non synchronised public method setFramePosition. Both getters are not synchronised as well getMicrosecondPosition and getFramePosition. The usage of "synchronized (this)" around "if (this.active != active) {this.active = active;" also can be replaced by the volatile keyword.
21-05-2021

I can take look if you wish.
21-05-2021

Checked with attached testapp in ubuntu 20.04 and Windows 10, i could reproduce the issue, deadlock is observed. Test Result: ========= 8u201: Pass 8u202: b01 Fail<- Regression 8u291: Fail 11: Pass 11.0.1 pass 11.0.2: b02 Fail <- Regression 11.0.11: Fail 16: Fail 17ea: Fail upon commenting the statement>>>clip.setMicrosecondPosition(0); , issue could not be reproduced, testcase terminates!. Test Result: ========= 8u291: Pass 11.0.11: Pass 16: Pass 17ea: Pass
20-05-2021

Additional information received from submitter: ==================================== A screen short of Eclipse IDE showing stacks of the program attached deadlocked. On the left you can see Daemon Thread [Direct Clip] has lock on object 32 and wants a lock on object 31. It also shows Thread 2 owns lock on object 21 and wants lock on object 32. The file attached in the program used to produce the error. I HAD TO RUN IT SEVERAL TIMES BEFORE I GOT THE DEADLOCK. The nature of deadlocks are hard to reproduce. It takes exact timing by chance to occur. I am using Linux Mint 19 Linux Kernel - 4.15.0-136-generic Produced deadlock using Java 11.0.10 However in the past I tried the latest version of Java and saw the same result Java 16.0.1+9 You need to pass in a sound file. I attach the sound file I used. ======================================================================== I have modified the Test Program and this code deadlocks immediately on my system. <attached_testapp_SoundTest.java>
17-05-2021

Mail to submitter: ============= Could you please share minimal reproducible standalone test case and reproducible steps to analyze the issue better.
03-05-2021