Search code examples
javaandroidinputstreamoutputstreamblocked-threads

why is my PipedOutputStream deadlocking?


I am trying to implement a threaded circular buffer with PipedInputStream & PipedOutputStream but it is locking everytime when I get to mHead.write in the Decoder runnable. I thought there was no chance for deadlocks when using separate threads.

    private class DecoderTask implements Runnable{

    @Override
    public void run() {
        while(!mStop){
            try {
                    Log.d(TAG,"trying to write");
        mHead.write(decode( 0, 1000));
            mHead.flush();
            Log.d(TAG,"Decoded");
            } catch (DecoderException e) {
                Log.e(TAG,e.toString());
            } catch (IOException e) {
                Log.e(TAG,e.toString());
            }
        }
    }

}
private class WriteTask implements Runnable{

    @Override
    public void run() {
        while(!mStop){
            try {
                                 Log.d(TAG,"trying to read");
                 int read = mTail.read(mByteSlave, 0, mByteSlave.length);
                 mAudioTrack.flush();
                                 mAudioTrack.write(mByteSlave,0,read);
                                 Log.d(TAG,"read");                 
            } catch (IOException e) {
                Log.e(TAG,e.toString());
            }
        }
    }

}


//in some function
mTail = new PipedInputStream();
mHead = new PipedOutputStream(mTail);
mByteSlave = new byte[BUF];
mT1 = new Thread(new DecoderTask(), "Reader");
mT2 = new Thread(new WriteTask(), "Writer");
mT1.start();
mT2.start();
return;

edit: here is the full source for my service http://pastie.org/1179792

logcat prints out :

trying to read
trying to write


Solution

  • The program doesn't block, it's just very very slow and inefficient. It uses 100% CPU. The problem is if (mTail.available() >= mByteSlave.length) - this will return false in most cases, and so you get a busy loop in this thread. If you can get rid of this, do it. Then this problem is solved. If you can't, it gets more complicated...

    There is another problem: PipedInputStream.read returns an int. You need to use:

    int len = mTail.read(mByteSlave, 0, mByteSlave.length);
    mAudioTrack.write(mByteSlave, 0, len);
    

    Other than that, I couldn't find anything wrong in this code. My complete test case looks like this:

    import java.io.*;
    public class Test2 {
        PipedOutputStream mHead;
        PipedInputStream mTail;
        byte[] mByteSlave = new byte[1024];
        boolean mStop;
        public static void main(String... ar) throws Exception {
            new Test2().run();
        }
        void run() throws Exception {
            mTail = new PipedInputStream();
            mHead = new PipedOutputStream(mTail);
            Thread mT1 = new Thread(new DecoderTask(), "Reader");
            Thread mT2 = new Thread(new WriteTask(), "Writer");
            mT1.start();
            mT2.start();
        }
        class DecoderTask implements Runnable {
            public void run() {
                while (!mStop) {
                    try {
                        mHead.write(new byte[3000]);
                        mHead.flush();
                        System.out.println("decoded 3000");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        class WriteTask implements Runnable {
            public void run() {
                while (!mStop) {
                    try {
                        int len = mTail.read(mByteSlave, 0, mByteSlave.length);
                        if (len < 0) break; // EOF
                        // mAudioTrack.write(mByteSlave, 0, len);
                        // mAudioTrack.flush();
                        System.out.println("written " + len);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }