Write blocked waiting on read

Hi all,

I have been experiencing difficulties with simultaneous read/write with AsyncIO. For my scenario that the client/server application is being developed, a client may send requests or status information at any given time. Hence continual monitoring on the incoming stream is required.

The server shall respond depending upon the status message requiring a write to the client (so it won't respond to all messages received). Likewise, the client in some instances will only send status messages depending on the last message it received from the server.

I've been experiencing difficulties with writes blocking because of reading. Essentially I would like to continually poll reading whilst allowing writes to be flushed immediately.

Using traditional blocking read/writes things hang indefinitely whilst attempting to write a message as reading has blocked waiting for input.

Using the IBM AsyncIO package (which is purported to be faster implementation of NIO), writing blocks for some time until reading (i assume) relinquishes the socket to allow writing to occur before resuming reading again. The lag time in this situation is significant.

Is someone able to provide an example using non-blocking R/W in which a server can sit reading (on one thread) whilst the writing is attempted on another thread that doesn't cause any lag?

Below is a basic overview of what is happening in my software:

publicclass MessageQueue{

private LinkedList<Message> queue;

/** Creates a new instance of MessageQueue */

public MessageQueue(){

queue =new LinkedList<Message>();

}

publicsynchronizedvoid put(Message message){

queue.add( message );

notifyAll();

}

publicsynchronizedboolean isEmpty(){

return queue.isEmpty();

}

publicsynchronized Message get(){

while( queue.isEmpty() ){

try{

wait();

}catch( InterruptedException ie ){

;

}

}

Message message = ( Message )queue.removeFirst();

return message;

}

publicsynchronizedvoid close(){

queue.clear();

queue =null;

}

}

publicclass InputReaderimplements Runnable{

private MessageQueue messages;

private AsyncSocketChannel async_channel;

...

public InputReader(MessageQueue messages, AsyncSocketChannel async_channel){

this.messages = messages;

this.async_channel = async_channel;

}

publiclong read(ByteBuffer b){

long bytes_read = 0;

helper =new AsyncSocketChannelHelper( this.async_channel );

future = channel.read(b);

bytes_read = future.getByteCount( );

return bytes_read;

}

publicvoid run(){

ByteBuffer b = ByteBuffer.allocateDirect(Message.SIZE);

boolean running =true;

while(running){

if (read(b) == 0)

running =false;

else

messages.put(new Message(b));

}

}

}

publicclass OutputWriterimplements Runnable{

private MessageQueue messages;

private AsyncSocketChannel async_channel;

...

public OutputWriter(MessageQueue messages, AsyncSocketChannel async_channel){

this.messages = messages;

this.async_channel = async_channel;

}

publiclong write(ByteBuffer b){

long bytes_written = 0;

try{

AsyncSocketChannelHelper helper =new AsyncSocketChannelHelper( this.async_channel );

IAsyncFuture future = helper.write(b, 20000);// write or timeout

// wait for completion of write, or for the timeout to happen

bytes_written = future.getByteCount( );

// THIS IS WHERE THE PROBLEM LIES. The write does not happen straight away because of the read operation. With traditional blocking IO this locks completely.

}catch ( AsyncTimeoutException ate){

System.err.println("Timed out after 20 seconds");

}

return bytes_written;

}

publicvoid run(){

boolean running =true;

while(running){

Message m = this.messages.get();

if (write(m.getByteBuffer()) == 0)

running =false;

else

messages.put(new Message(b));

}

}

}

publicclass Controller{

public Controller(AsyncSocketChannel async_channel){

MessageQueue in =new MessageQueue();

MessageQueue out =new MessageQueue();

InputReader ir =new InputReader(out, async_channel);

OutputWriter ow =new OutputWriter(out, async_channel);

new Thread(ow).start();

new Thread(ir).start();

boolean running =true;

Message m;

while(running){

m = in.get();

if (m.getStatus() =="REQUIRES_RESPONSE"))

out.put(m);// dummy example to demonstrate that once the right condition is met, a new message must be written

}

}

}

[8941 byte] By [MarkVa] at [2007-11-27 8:13:39]
# 1

Actually IBM AsyncIO isn't a 'faster implementation of NIO' at all, it is something quite different: an implementation of asynchronous I/O. NIO is an implementation of non-blocking and multiplexed I/O. Different things.

Your question really belongs in the AsyncIO forum on Alphaworks.

ejpa at 2007-7-12 19:58:05 > top of Java-index,Core,Core APIs...
# 2

Ok, I think I follow. So NIO would like better serve my purposes rather than the IBM AsyncIO which is more to do with allowing a program to execute other logic whilst waiting on a single read or write operation?

As such, would adoption of NIO solve the blocking problem I am encountering?

Ta,

Mark

MarkVa at 2007-7-12 19:58:05 > top of Java-index,Core,Core APIs...
# 3
Even standard Java sockets don't block each other when reading and writing. It's simplest just to use threads and java.net for network I/O. NIO will do what you want too but it's about 10 times as complex to use.
ejpa at 2007-7-12 19:58:05 > top of Java-index,Core,Core APIs...
# 4

That makes me wonder what the problem is I am experiencing then.

I initially had stock-standard java.net IO for socket reading and writing. The approach I took was to set up an input reader on its own thread and an output writer on its own thread.

When it came to writing data however, things locked up. Stepping through the code in debug mode allowed me to see that the write method was not completing because the read method was waiting for input to come in.

I tested it using traditional buffered output which made a call to flush() afterwards, but it was getting stuck on the write() call.

I came to the conclusion that the read must be blocking the write from completing because of the response to this thread: http://forum.java.sun.com/thread.jspa?forumID=536&threadID=750707

On further debugging, write() never locked up when I didn't allow the input reader to perform a simultaneous read() on the socket.

Hence my belief that the java.net socket does block when one operation is being performed.

After dealing with IBM's AsyncIO package I'd be willing to wager that their ibmaio package is 50x more complex to use than standard Java sockets (barely any documentation/examples) so 10x complexity seems positively lightweight ;-)

So ejp, to clarify, would NIO help solve this blocking problem or do you think something else is the culprit? It is hard to see what as to test things out I made two bare bones testing programs (one client and the other a server) so I don't feel it could be anything else in the code.

Thoughts?

MarkVa at 2007-7-12 19:58:05 > top of Java-index,Core,Core APIs...
# 5

I agree with what I wrote in that other thread in the last post. ;-) Socket reads and writes aren't synchronized against each other,(1) and a blocked write is most likely to mean that the reader at the other end is lagging way behind, or blocked doing something else, so the TCP window has closed and the write is blocked waiting for it to open again. I would solve this first rather than tackle NIO which will just pose a new set of problems (while admittedly solving this one).

Possibly your application protocol is prone to deadlock? You need to ensure there is a flush() every time you switch from writing to reading, or at the end of every set of writes if you're using separate read and write threads.

(1) Except for the (buggy) case where the Socket came from a SocketChannel in the first place. Then you are dealing with the streams coming from the java.nio.channel.Channels class and they do have synchronization issues.

ejpa at 2007-7-12 19:58:05 > top of Java-index,Core,Core APIs...
# 6
ejp, thanks for the advice. I implemented the NIO methods and things *appear* to be working without the deadlock-like problem I encountered previously. I've also taken on board the advice to ensure writing is flushed after each message is sent.Thanks,Mark
MarkVa at 2007-7-12 19:58:05 > top of Java-index,Core,Core APIs...