This discussion is archived
9 Replies Latest reply: Apr 24, 2012 5:48 AM by 810200 RSS

Do SourceDataLine.write() and TargetDataLine.read() mask interrupts?

845492 Newbie
Currently Being Moderated
I've come back to an old problem and have decided to understand it fully before continuing using my workaround solution. I have a loop that is reading from a TDL in a dedicated Thread. To terminate audio capture, I'd like to interrupt that Thread causing clean up (close lines, etc) and terminate that Thread. I've found that the interrupt works only intermittently. Adding diagnostics to the loop I can see that the interrupt flag is set before the call to TDL.read(), and then immediatly after returning from TDL.read() it is not set. (I'm using Thread.currentThread().isInterrupted() so as not to clear the flag with the query itself)
  while (true) {
    Thread.currentThread().interrupt(); // sets interrupt flag
    System.out.println("Interrupt flag is " + (Thread.currentThread().isInterrupted()?"":"not ") + "set before read()"); // does not modify interrupt flag
    mySourceDataLine.read(buffer,0,length);
    System.out.println("Interrupt flag is " + (Thread.currentThread().isInterrupted()?"":"not ") + "set after read()");  // does not modify interrupt flag
    if (Thread.interrupted()) { // clears interrupt flag if set
      System.out.println("I was interrupted");
    }
}
Based on the intermittent behavior, I conclude that I've not done something wrong (or that is always wrong - like interrupting the wrong Thread or querying the wrong thread to see if it is interrupted). I am guessing that in the read method, if reading from the SDL buffer is blocked, read waits until notification that there is data to read. I wonder if the interrupt happens during the wait, if it is discarded? That would explain the behavior I've seen (the only pausible explanation I can come up with). If that is true, surely others have run across this problem before; it seems like a common pattern to be used (as well as non-obvious and frustrating!)

My workaround is to use my own interrupt "flag" and test it in the loop. I did that months ago when under schedule pressure I ran across the same problem with TDL.write(). I want to understand the exact cause of this to be sure I'm not deluding myself into thinking my code is working correctly when it in fact is not and will bite me badly later. I've looked for source to verify my hypothesis but since TDL/SDL/DL are interfaces I can't seem to find source. I attribute that to the fact that the implementation is machine/sound card specific.

I preferred to use the interrupt method thinking it would be cleaner and more consistent by using existing functionality supported by the Java implementation.

Thanks.

Edited by: ags on Apr 23, 2012 5:32 PM
  • 1. Re: Do SourceDataLine.write() and TargetDataLine.read() mask interrupts?
    EJP Guru
    Currently Being Moderated
    It looks very much as though the read() method calls Thread.interrupted() internally for some reason, and then didn't react to it correctly, i.e. by throwing InterruptedIOException.
  • 2. Re: Do SourceDataLine.write() and TargetDataLine.read() mask interrupts?
    845492 Newbie
    Currently Being Moderated
    Yes, that is my hypothesis as well. Can you think of any way to validate that? I don't know where to begin to look for source (or something readable) for the AudioSystem implemetned on my machine. Where would this come from?

    Edited by: ags on Apr 23, 2012 7:15 PM
  • 3. Re: Do SourceDataLine.write() and TargetDataLine.read() mask interrupts?
    EJP Guru
    Currently Being Moderated
    I found this in com.sun.media.sound.MixerSourceLine extends AbstractDataLine implements SourceDataLine:
             if (totalBytesWritten < totalBytesToWrite) {
    
              synchronized(lock) {
                  try {
                   // $$kk: 08.17.99: need to make sure we never block forever here!
                   //
                   // ivg: Well, it does hang in some cases.
                   //      I can reproduce that with JMF by starting and closing
                   //      DataLines.  I suspect this lock is not released when
                   //      the DataLine is closed.  This is highly timing sensitive.
                   //     It doesn't look like we have a good case of concurrent
                   //      programming here.  I put in a time out value in the
                   //      wait so it will wake up itself to check.  Not optimal.
                   lock.wait(2000);
                  } catch (InterruptedException e) {
                  }
              }
             }
    Shocking piece of work but it seems to account for at least part of your problem. There is a similar thing in com.sun.media.sound.DirectAudioDevice.

    It looks like you can mitigate it by using a smaller buffer, so that (totalBytesWritten < totalBytesToWrite) happens less often. I was going to recommend doing that anyway; it means that you will spend more time in your own loop code rather than inside this thing, which gives you more opportunity to catch InterruptedException yourself.
  • 4. Re: Do SourceDataLine.write() and TargetDataLine.read() mask interrupts?
    845492 Newbie
    Currently Being Moderated
    Wow. I'm (almost) speachless. You have found the smoking gun. This is just terrible, terrible code. Catching an exception and doing nothing with it? Not understanding why a deadlock occurs and using a timeout as a solution? I taugh myself Java and don't consider myself a pro (in Java) but would not do that. The tutorial tells newbies not to do that. I wonder if C# has this type of garbage, but since source isnt' available it's hidden.

    I agree that making the buffer smaller will probably lessen the chance of this happening, but once lost the interrupt is gone for good. I can't risk a stochastic process, so I will use what I implemented as a temporary prototype/workaround (my own flag signalling an interrupt which won't be cleared behind-the-scenes without my knowledge). I want 4 days of my life back...

    I'm interested still in understanding where the machine/sound-card-specific AudioSystem implementations come from (does the vendor have to install them, are they downloaded automagically with the JVM?).

    Thanks very much for finding the proof.
  • 5. Re: Do SourceDataLine.write() and TargetDataLine.read() mask interrupts?
    EJP Guru
    Currently Being Moderated
    I'm interested still in understanding where the machine/sound-card-specific AudioSystem implementations come from (does the vendor have to install them, are they downloaded automagically with the JVM?).
    Are there any? Surely there is some native API that all the vendor code fits underneath, so all that javax.sound has to do is talk to that native layer, and the vendor just has to get his driver into the box somehow, probably at box build time. The code I referred to above is in the JDK but it doesn't look vendor specific in the slightest.
  • 6. Re: Do SourceDataLine.write() and TargetDataLine.read() mask interrupts?
    sabre150 Expert
    Currently Being Moderated
    ags wrote:
    You have found the smoking gun. This is just terrible, terrible code. Catching an exception and doing nothing with it? Not understanding why a deadlock occurs and using a timeout as a solution?
    I agree that that is terrible code that is designed to get round a bug in some other code BUT doing nothing with an InterruptedException is very common. Since a wait() can be interrupted even without a notify() or notifyAll() (I have had it happen on Windows systems) it is common to loop round it until a desired condition is met and to ignore (not even printing a stack trace) any InterruptedException. I use this approach in my current project which is a client-server system where the server waits for an event from the client and if not received within a system defined time-out period it closes the connection.
  • 7. Re: Do SourceDataLine.write() and TargetDataLine.read() mask interrupts?
    810200 Newbie
    Currently Being Moderated
    A couple of things I am curious about--

    1) Isn't it more usual to quit the while loop that contains the TDL or SDL read/write method via a boolean rather than directly interrupting the thread? That is the method for exiting this loop that is presented in the Java Tutorial (via setting a boolean that is consulted as a while condition). [http://docs.oracle.com/javase/tutorial/sound/playing.html]

    2) The source for com.sun.media was not included with the source code provided in my JDK. I remember getting it from java.net instead, but now I can't recall the site! Do you know where one can get the source code for this library? I remember it came with a "Research" license.

    3) I'd be really interested in seeing the Java 7 source for DirectAudio, but haven't been able to locate source code. When I went to the meeting at Oracle held for the introduction of Java 7, I asked if there had been changes to the javax.audio.sampled, and was told that it had been rewritten and improved. But I got no details, and I don't know if the rewrite extended into issues in this library.

    I'm just a beginner with concurrent programming, but found this quote from Brian Goetz: "Poorly behaved methods swallow the interrupt request, thus denying code further up the call stack the oopportunity to act on it." (pg 140) So, probably not a good idea to use the interrupt method unless you are confident a piece of code is well-behaved?

    Edited by: Phil Freihofner on Apr 24, 2012 5:10 AM
  • 8. Re: Do SourceDataLine.write() and TargetDataLine.read() mask interrupts?
    EJP Guru
    Currently Being Moderated
    1) Isn't it more usual to quit the while loop that contains the TDL or SDL read/write method via a boolean rather than directly interrupting the thread?
    Interrupting the thread interrupts sleeps and waits and interruptible I/O and sets a boolean. That's a lot stronger than just setting a boolean.
    Do you know where one can get the source code for this library?
    I got it either with the JDK or via the SCSL programme.
    So, probably not a good idea to use the interrupt method unless you are confident a piece of code is well-behaved?
    Probably not a good idea to write badly behaved code you mean. The catch block for InterruptedException should throw InterruptedIOException.
  • 9. Re: Do SourceDataLine.write() and TargetDataLine.read() mask interrupts?
    810200 Newbie
    Currently Being Moderated
    EJP wrote:
    1) Isn't it more usual to quit the while loop that contains the TDL or SDL read/write method via a boolean rather than directly interrupting the thread?
    Interrupting the thread interrupts sleeps and waits and interruptible I/O and sets a boolean. That's a lot stronger than just setting a boolean.
    What does "stronger" mean if it doesn't work predictably? If the "playing" boolean is set to volatile, the while loop will exit pretty close to immediately (on the next iteration), and reliably so.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points