Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[servlet-dev] Non-blocking IO and ServletOutputStream.close()

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


Back to the top