-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clarify behaviour after ServletOutputStream.setWriteListener #273
Comments
Currently the jetty implementation interprets both public void onWritePossible()
{
out.write(someContent);
out.close();
asyncContext.complete();
} which instead needs to be written like: public void onWritePossible()
{
while (out.isReady())
{
switch(state)
{
case WRITING:
state = WRITTEN;
out.write(someContent);
break;
case WRITTEN:
state = CLOSED;
out.close();
break;
case CLOSED:
asyncContext.complete();
return;
}
} The reason for this interpretation is that both operations may need to write to the network and thus may block if a write is pending. The write needed may included:
Thus if it is intended to have a fully asynchronous API, then both |
Undertow takes a more relaxed approach, by doing a non-blocking flush() or close() behind the scenes, so the first code example would work for Undertow. My reasoning was that the purpose of isReady() is to prevent the implementation from having to buffer an unbounded amount of data, and both flush() and close() do not add any data to the buffer. This was partly driven by our implementation choices, in the when you call write() if there is room in the buffer it will not actually attempt to write to the socket, so if you call close() there is no guarantee that all the data in the buffer can be immediately written out so we do an async close, and just call onError if there is a problem. |
@stuartwdouglas I think undertows is also a plausible interpretation of the spec - thus why we really need to narrow it down to one behaviour and to well describe it. Note that with undertows interpretation, some applications may be surprised that public void onWritePossible()
{
asyncContext.complete();
doSomethingAfterCompletion();
} I don't think that is very good app code, nor should we have to support it, but if the async close and complete interpretations are what we want, then we do need to javadoc it. I think there are 3 possible interpretations: 1. async (undertow), 2. illegal (jetty), or 3. blocking (would be strange but possible). Jetty is happy to change to another behaviour if that's common to other's... @markt-asf please tell us that tomcat does 1 or 2 and not 3 :) |
@stuartwdouglas also can you clarify how you handle something like: public void onWritePossible()
{
...
out.write(IlikeBigBuffers);
assert(isReady()==false);
out.close();
} Ie if Then same question for |
If Once close has been called we no longer call In terms of the race I think that if you have one thread writing data asynchronously, and another thread that may call My thinking is that:
I also think that if the user calls I really do not think that |
+1 +1 I'm not sure I agree that I have seen many applications where Consider one async thread that is writing content and then only "looks" for more content to write if isReady() returns. It could easily write then call isReady() and get false return. Meanwhile some other async event can happen that signals no more content will be available, so the application may call If we allow Even is we say that such races are only going to happen in bad applications, then we need to define what the behaviour should be - or at least document it. I was kind of looking favourably at the async close and complete interpretation, but now I'm very much leaning back towards jettys just make it illegal so you don't get these race problems. If you always guard with an |
Tomcat uses the async approach if complete() is called while an async read/write is in progress. I recently gave this code an overhaul in Tomcat. The updated state diagram that resulted might (or might not) be useful: https://github.com/apache/tomcat/blob/master/java/org/apache/coyote/AsyncStateMachine.java#L28 We generally see far more issues around error handling. I still have an in-depth review of that on my TODO list after our previous discussion on that topic. |
The current javadoc for
ServletOutputStream.setWriteListener
is minimal and does not well define the async behaviour, specifically in regards to which methods are affected. The 4.0 servlet specification says:As the specification is no longer available for clarifications, this behaviour needs to be codified in the javadoc and clarifications made there.
Specifically it would be desirable to define what is meant by "a write operation", as writes may be performed by methods like
ServletOutputStream.close()
,ServletResponse.flushBuffers()
,AsyncContext.complete()
,ServletResponse.sendError(...)
, etc.See also #256
The text was updated successfully, but these errors were encountered: