jsr340-experts@servlet-spec.java.net

[jsr340-experts] Re: NIO specification clarification

From: Mark Thomas <markt_at_apache.org>
Date: Fri, 12 Apr 2013 08:55:25 -0400

On 12/04/2013 04:43, Rémy Maucherat wrote:
> On 04/12/2013 02:15 AM, Mark Thomas wrote:
>> The WebSocket use case requires switching back and forth between
>> blocking and non-blocking writes. (The API defines some writes as
>> non-blocking and others as blocking and they can be called in any order).
>>
>> I am building on top of the Servlet 3.1 HTTP upgrade so I am using the
>> ServletOutputStream in non-blocking mode so I can handle the
>> non-blocking writes. For the blocking writes I have to make the
>> non-blocking write appear to be blocking. For this I am using a Latch
>> so the thread that initiates the write isn't released until the write
>> completes. If it is a large write the container may need to call
>> onWritePossible() several times before the write completes and the
>> Latch can count down and the initiating thread released. This only
>> works if multiple container threads can access the WriteListener.
>>
>> Remy's explanation of why we have this restriction makes sense. The
>> general case is a lot trickier than the WebSocket case above (and even
>> in the WebSocket case I had to be pretty careful with the multiple
>> threads). I am heading rapidly towards the conclusion that the
>> WebSocket API can't be implemented in a container neutral manner
>> (which I think is a real shame).
> Not sure I understand why.

If the restriction is one container thread per request then there is a
problem. The container thread that triggers the blocking write has to
block until the write completes and a second container thread (for the
same request) has to trigger the onWritePossible() so you have two
container threads working with the same request. The restrictions as you
described them do not allow this.

Going in to a bit more detail, the container thread that triggers a
blocking write will always have been triggered by data being read from
the client (i.e. it is using the ReadListener). The second thread is
using the WriteListener so we never have more than two threads with one
thread working with the ReadListener and one with the WriteListener.

If the restriction in 3.7 / 5.3 is no more than one container thread to
use a [Read|Write]Listener at a time (i.e. two concurrent container
threads, one calling the ReadListener, one calling the WriteListener is
permitted) then I think (I'd need to do some testing to check) all is good.

> With the way it is built, I would say websockets has to use a single big
> queue for both its "async" and "blocking" RemoteEndpoint(s), so the
> onWritePossible will basically trigger the actual sending of the queue
> content.

Correct.

> I don't see why you would need multiple concurrent onWritePossible
> notifications to do that [which I have no idea how it could happen
> anyway on a single socket]. Did I miss something ?

The multiple calls aren't concurrent, they are sequential (assuming that
the data to write is big enough that it takes several call backs before
the write is complete).

Mark