4

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

Nathan Schwermann
  • 31,285
  • 16
  • 80
  • 91

3 Answers3

5

I have experienced the same problem and resolved it by overriding the default PIPE_SIZE in the PipedInputStream(int) constructor. The method PipedOutputStream.write(byte[], int, int) blocks until all the bytes are written to the output stream. This might be a problem with the default PIPE_SIZE.

After all, size does matter ;-)

Draško Kokić
  • 1,280
  • 1
  • 19
  • 34
0

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();
                }
            }
        }
    }
}
Thomas Mueller
  • 48,905
  • 14
  • 116
  • 132
  • Still not working I edited my code above to reflect what I have now, log output says: trying to write trying to read and then nothing, its blocking on the read and the write. – Nathan Schwermann Sep 24 '10 at 19:28
  • The problem was what you pointed out, as well as write(byte[]) is not defined by PipedOutputStream so it doesn't work, you have to use the (byte[], int, int) one – Nathan Schwermann Sep 24 '10 at 20:47
  • This doesnt work like I thought it did, once I read the data from the the mTail how to I clear out the data, it reads the same data over and over again. – Nathan Schwermann Sep 24 '10 at 21:17
  • You don't need to clear the data, you just need to make sure you don't write the same data over and over again into mAudioTrack and mHead. I updated the code: you need to use the "len" when writing to the audio: mAudioTrack.write(mByteSlave, 0, len); - also I added EOF detection: if (len < 0) break - so you could also get rid of if (!mStop) in the WriteTask, but you need to close mHead. – Thomas Mueller Sep 25 '10 at 07:53
  • can't get rid of mStop this is for an internet radio stream so there is never an EOF. Is there a better class to use for this type of situation? – Nathan Schwermann Sep 25 '10 at 08:08
  • mStop is OK. The question is, of course, if it's possible to just directly write to the mAudioTrack after decoding. – Thomas Mueller Sep 25 '10 at 09:27
  • The decode library gives me the wrong byte order so I need to have sort of a double buffer one decoded, and one decoded with the right byte order. – Nathan Schwermann Sep 25 '10 at 16:20
0

Just get rid of the test involving available(). The read will block anyway, and you have nothing better to do when there is no data.

user207421
  • 305,947
  • 44
  • 307
  • 483