Bug 55715

Summary: RemoteEndpoint.Async#sendText(String, SendHandler) can cause StackOverflowErrors and contradicts Oracle's JavaDoc
Product: Tomcat 8 Reporter: Konstantin Preißer <kpreisser>
Component: WebSocketAssignee: Tomcat Developers Mailing List <dev>
Severity: normal    
Priority: P2    
Version: 8.0.x-trunk   
Target Milestone: ----   
Hardware: PC   
OS: All   

Description Konstantin Preißer 2013-10-28 15:34:56 UTC
See thread on users list [1]:

Tomcat's current implementation of RemoteEndpoint.Async#sendText(String, SendHandler) can cause StackOverflowErrors (and seems to contradict Oracle's JavaDoc).

In Tomcat, Async#sendText(...) seems to implemented so that when it could send all of the data immediately, then it directly calls SendHandler#onResult(...); whereas when it couldn't send the data immediately, the SendHandler will be called from another thread.

Oracle's javadoc for RemoteEndpoint.Async says:

"The completion handlers for the asynchronous methods are always called with a different thread from that which initiated the send."

Now, imagine the case that you want to send 10000 very small text messages to a client (very unlikely, but possibly could happen). With synchronous I/O (RemoteEndpoint.Basic), you would do this in this way:

        RemoteEndpoint.Basic basic = session.getBasicRemote();
        for (int i = 0; i < 10000; i++) {
            basic.sendText("Hi, Count: " + i);

In this case, there is no problem.

Now imagine you want to do this asynchronously (using a Callback to be informed when sending is completed), then you could do it this way (e.g. put the following code in onOpen() method of Tomcat's EchoEndpoint example):

        final AtomicInteger aint = new AtomicInteger();
        final RemoteEndpoint.Async async = session.getAsyncRemote();
        SendHandler handler = new SendHandler() {
            public void onResult(SendResult result) {
                int nextVal = aint.incrementAndGet();
                if (nextVal < 10000) {
                    async.sendText("Hi, Count: " + nextVal, this);
        async.sendText("Hi, Count: " + aint.get(), handler);

The problem here is that because the messages are very short, Tomcat will be able to send them immediately, calling the SendHandler#onResult() directly from Async.sendText(), which will eventually cause a StackOverflowError.

I think if SendHandler#onResult() was always called from a different thread than the one which calls Async.send... (like the Javadoc says), then although the performance would probably be worse, StackOverflowErrors shouldn't occur.

[1] http://markmail.org/message/gpxzdwtxtrpynvux
Comment 1 Mark Thomas 2013-10-30 11:24:40 UTC
Thanks for the report.

This has been fixed in trunk for 8.0.0-RC6 and 7.0.x for 7.0.48.