On 23 October 2012 08:17, Shing Wai Chan <shing.wai.chan_at_oracle.com> wrote:
> I propose to change the behavior that there is no need to do (a) above.
> The WriteListener#onWritePossible will be invoked once for every write
> operation permitted.
Shing Wai,
so the behaviour would be as follows?
You signal that you want to be asynchronous with
servletOutputStream.setWriteListener(wlistener);
Immediately the implementation doesn't really know the output state,
because it has not tried to write anything, but it is a good guess
that a write is possible, so it should call
wlistener.isReady()
This triggers the application to attempt to write something, let's say it does:
servletOutputSream.write('H');
That completes OK (either buffered or actually send on the wire, we
don't care) and more writes are possible, so the implementation again
calls
wlistener.isReady()
which then wants to write some more:
servletOutputSream.write('e');
which completes:
wlistener.isReady();
which continues:
servletOutputSream.write('l');
etc.
wlistener.isReady();
servletOutputSream.write('l');
wlistener.isReady();
servletOutputSream.write('o');
Making the callback dependent on a call to canWrite was intended to
avoid callbacks unless they were really needed (because progress could
not be made) and thus really needed a dispatch when progress can be
made. So without this optimisation, we have the problem of what
thread is calling isReady in the case of a write that completes
without delay! Is it the thread that calls write? That would be
fast, but we would have a recursion problem as our stack can grow
deeper and deeper. So do we always dispatch a thread (and anoint it
with context goodness), which will work, but will be slow. Or do we
have some heuristic based hybrid (as does NIO.2). Whatever we allow,
we need to describe this in the javadoc/spec, which we currently do
not.
Having the callback dependent on a call to canWrite avoided this issue
(we really should document stuff like this so we don't forget).
So, after writing the 'o' the user works out that he is being
inefficient and wants to start writing more at once, so after the next
wlistener.isReady();
they write
servletOutputStream.write(" world!");
Let's say that there was only enough space left in the network buffers
to write 3 bytes, so only " wo" is actually written to the wire and
now the implementation must wait until the network says more writes
are possible. This is an asynchronous API, so we can't block the
write call and must return, but we don't call isReady() because we are
not ready.
Time passes and all our threads are off doing some useful work
elsewhere as they should be.
The network finally says that it can handle more data, so the
implementation writes the remaining data "rld!" and then calls
wlistener.isReady();
All good. But now the application decides it wants to write some bulk
data, so it does:
servletOutStream.write(myByteArray,0,1000000000);
This is handled like the string write before - the implementation will
return immediately from the write (we are non-blocking), but will not
call isReady until all the data is written. The implementation may
internally go through several cycles of waking up to write more data.
But meanwhile, the application has had it's thread returned to it
after the write. Is the thread free to reuse the myByteArray buffer?
If the answer is yes - that means that the implementation would
have had to copy 1000000000 bytes into a temporary buffer - that's an
unlimited memory commitment - ouch. A better solution is to say
that the buffer passed to write cannot be altered until isReady() is
called back. This also needs to be documented in the javadoc and
the spec.
Essentially this proposal is changing the write semantics to be much
the same as to NIO.2's
AsynchronousByteChannel.write(ByteBuffer src, A attachment,
CompletionHandler<Integer,? super A> handler)
Where WriteListener.isReady() == CompletionHandler.completed(...)
If we are going to have the same semantics - let's just have the same
API. But I thought we decided against going with NIO.2 because we
didn't like it's semantics and the need to deal with the recursive
callback issue.
So I'm not keen on the change.
--
Greg Wilkins <gregw_at_intalio.com>
http://www.webtide.com
Developer advice and support from the Jetty & CometD experts.