users@servlet-spec.java.net

[servlet-spec users] [jsr340-experts] Re: Re: Re: Re: NIO specification clarification

From: Mark Thomas <markt_at_apache.org>
Date: Wed, 22 May 2013 10:20:05 +0100

On 30/04/2013 17:26, Mark Thomas wrote:
> On 30/04/2013 15:33, Rémy Maucherat wrote:
>> On 04/30/2013 12:21 PM, Mark Thomas wrote:
>
>>> My question with auto blocking is how does the Servlet container know
>>> if a read or write is meant to be blocking or not? The user code (the
>>> WebSocket implementation) knows but there is no API that enables it to
>>> tell the container. I had originally thought that flush() could be
>>> used to do this (i.e. block if flush() is called) but as you pointed
>>> out, that might work in an HTTP upgrade scenario but not in a Servlet
>>> one. Using flush(0 in this way would work for WebSocket since it used
>>> HTTp upgrade but having the same method behave differently for HTYTP
>>> Upgrade and async servlets doesn't seem right either.
>
>> It should be possible to make the decision about blocking or not by
>> using isReady. If isReady is used when doing the reads/writes, it will
>> not block. If isReady is not used, and things occurs inside a container
>> thread, it blocks (I never thought it would be a good idea to block an
>> async thread).
>
> Ah. That sounds like it might work. I'll see if I can find some time to
> experiment.

I was getting close the the point where I was going to have some time to
work on this so I have been mulling it over some more. My conclusion is
that auto-blocking isn't going to solve the current "can't implement
WebSocket 1.0 on Servlet 3.1" problem.

The issue is not the writes in the WebSocket API that explicitly block -
auto-blocking should work for those. (Or rather, I can't see any reason
why it wouldn't work right now.)

The current sticking point is the non-blocking writes that return a
Future. Consider the following sequence:

- Container receives WebSocket message
- Container thread triggers onMessage processing in app
- app (still on container thread) sends a message in response using the
  Future style
- app calls Future.get()

At this point the container thread (A) is blocked inside Future.get().
Since this is in the WebSocket implementation the Servlet implementation
has no knowledge that the thread is blocked. Unblocking thread A
requires a second container thread to call onWritePossible().

Auto-blocking doesn't help here because the WebSocket implementation
doesn't know that the write is effectively going to be blocking until
after the write has been initiated.

At the moment, my current solution of allowing a maximum of one read
thread (i.e. in response to the socket signalling it has data to read)
and one write thread (i.e. in response to the socket signalling it is
ready to receive data) per request rather than a maximum of one thread
per request looks like the best option.

Other options I have considered:
1 Change the non-blocking aspects of the API in Servlet 3.1 to use the
  same style as WebSocket.

2 Expose the object being used for the one thread per request lock
  through the Servlet API so the WebSocket API can use it rather than
  its own lock to implement blocking. This could enable the Servlet API
  to detect when a container thread was blocked and therefore allow
  another thread to do some work on the same request.

3 Give up and use some the container internals directly.

I assume that option 1 is a non-starter.

Option 2 exposes a very low level object. Users would *really* need to
know what they were doing to use it. Because it is so low level
different containers are going to have taken very different approaches.
Finding a common API that works for all - without significant effort for
some - could be tricky.

I really, really don't want to do 3.

Thoughts? Comments?

Mark