[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
Re: [servlet-dev] Non-blocking IO and ServletOutputStream.close()
|
On 07/01/2023 06:57, Greg Wilkins wrote:
Mark,
Conceptually both close and complete could be thought of as write
operations that need to wait until a true isReady() return before being
called.
However, unlike a write operation, they cannot carry additional data to
be queued, nor be called multiple times, so there is no unbounded data
commitment if we let them be called whilst a write completion is still
pending. In this interpretation, they are state changes rather than writes.
So since the spec has never said otherwise, i think we must use the
second interpretation.
Thanks for the review Greg. There is a comment in the Tomcat code to the
effect that complete()/dispatch() are not allowed while async IO is in
progress. I need to do some research to see if I can find where that
comment originated as that point is rather fundamental to this discussion.
Mark
Furthermore, completion is orthogonal to output close. A response can be
sent and the output closed before the entire request is read. Also
computation can continue after both request and response bodies are
complete and delaying a call to complete can create back pressure to
prevent the next request being read (at least on some versions of
HTTP). So i do not think associating complete with output close is
correct, nor do I think the container should ever complete for the
application. >
Thus i think that both close and complete can be called irrespective of
isReady status. Both are state changes: close prevents any subsequent
calls to write and will terminate response framing when any pending
write completes; complete implies a close and will also arrange for the
request cycle to be ended once any out standing calls to service,
onDataAvailable or onWritePossible return.
I'm away from a proper computer at the moment, but will run through your
scenario in more detail once i am back at a keyboard.
Cheers
On Thu, 5 Jan 2023, 23:42 Mark Thomas, <markt@xxxxxxxxxx
<mailto:markt@xxxxxxxxxx>> wrote:
Hi all,
I am working on the changes required to Tomcat to implement the changes
to ServletOutputStream.close() made as part of PR #485 [1].
I have a question regarding the following scenario:
1. HttpServletRequest.startAsync() is called to switch to async mode.
2. ServletOutputStream.setWriteListener() is called to switch to
non-blocking IO.
3. There are then callbacks to WriteListener.onWritePossible()
4. At some point during one of those callbacks
ServletOutputStream.isReady() returns false and
ServletOutputStream.close() is called.
My question is who is responsible for calling AsyncContext.complete()
and when does that call occur?
If we assume that the application is expected to call
AsyncContext.complete() then my expectation is that once the
non-blocking write completes (and onWritePossible()) would normally be
called, the application is required to call complete() at that
point. In
fact onWritePossible() needs to be called so the application knows when
to call AsyncContext.complete().
But that begs the question why allow ServletOutputStream.close() when
ServletOutputStream.isReady() returns false? It would be much simpler
not to allow this and the application can call
ServletOutputStream.close() followed by AsyncContext.complete() on the
next onWritePossible() call back. The end result is the same and the
semantics are a lot simpler.
If we assume that the container is expected to call
AsyncContext.complete() once the blocking write has finished
(instead of
the onWritePossible() callback) then that is effectively saying that
calling ServletOutputStream.close() is another way to terminate async
processing.
That seems to be quite a significant change to the specification.
Currently, there are multiple places where it is stated an explicit
call
to complete() or one of the dispatch() methods is required to end async
processing.
This could result in multiple calls to complete() which will trigger
ISEs. I think this could be addressed by having the container call
complete() if the application hasn't already called
complete()/dispatch(). I think that implies that complete()/dispatch()
can also be called while a non-blocking write is in progress.
Given that ServletOutputStream is Closeable I am also a little worried
about unexpected / unintended consequences if we go this route.
Whatever we do, I think some further clarification is required.
I can see at least the following options:
1. Change ServletOutputStream.close() so it can only be called after
ServletOutputStream.isReady() has returned true (use the same language
as for the write methods).
2. Change ServletOutputStream.close() so the container writes out any
remaining data and then calls AsyncContext.complete() (or calls
complete() immediately if there is no data to write) unless the
application has already done so.
3. Something else I haven't thought of yet.
Thoughts?
Mark
[1] https://github.com/jakartaee/servlet/pull/485
<https://github.com/jakartaee/servlet/pull/485>
_______________________________________________
servlet-dev mailing list
servlet-dev@xxxxxxxxxxx <mailto:servlet-dev@xxxxxxxxxxx>
To unsubscribe from this list, visit
https://www.eclipse.org/mailman/listinfo/servlet-dev
<https://www.eclipse.org/mailman/listinfo/servlet-dev>
_______________________________________________
servlet-dev mailing list
servlet-dev@xxxxxxxxxxx
To unsubscribe from this list, visit https://www.eclipse.org/mailman/listinfo/servlet-dev