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

async IO Write closed race #798

Closed
arhimondr opened this Issue Aug 1, 2016 · 11 comments

Comments

Projects
None yet
3 participants
@arhimondr
Contributor

arhimondr commented Aug 1, 2016

  1. IllegalStateException when setting write listener. HttpOutput is closed.

    javax.servlet.ServletException: Error setting write listener on response: HttpOutput@7a697036{CLOSED}
        at Main$AsyncServlet.doGet(Main.java:247)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:845)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1689)
        at Main$ErrorLogger.doFilter(Main.java:362)
        at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:581)
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1174)
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1106)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
        at org.eclipse.jetty.server.Server.handle(Server.java:518)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:314)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:253)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
        at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
        at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceAndRun(ExecuteProduceConsume.java:246)
        at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:156)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:654)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:572)
        at java.lang.Thread.run(Thread.java:745)
    Caused by: java.lang.IllegalStateException
        at org.eclipse.jetty.server.HttpOutput.setWriteListener(HttpOutput.java:844)
        at Main$AsyncServlet.doGet(Main.java:244)
    
  2. EofException: Async closed.

    On server:
    
    org.eclipse.jetty.io.EofException: Async closed
        at org.eclipse.jetty.server.HttpOutput.closed(HttpOutput.java:248)
        at org.eclipse.jetty.server.HttpOutput$AsyncWrite.onCompleteSuccess(HttpOutput.java:1131)
        at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:325)
        at org.eclipse.jetty.util.IteratingCallback.succeeded(IteratingCallback.java:365)
        at org.eclipse.jetty.server.HttpConnection$SendCallback.onCompleteSuccess(HttpConnection.java:788)
        at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:325)
        at org.eclipse.jetty.util.IteratingCallback.succeeded(IteratingCallback.java:365)
        at org.eclipse.jetty.io.WriteFlusher$PendingState.complete(WriteFlusher.java:269)
        at org.eclipse.jetty.io.WriteFlusher.completeWrite(WriteFlusher.java:394)
        at org.eclipse.jetty.io.SelectChannelEndPoint$3.run(SelectChannelEndPoint.java:107)
        at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceAndRun(ExecuteProduceConsume.java:246)
        at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:156)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:654)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:572)
        at java.lang.Thread.run(Thread.java:745)
    
    On client:
    
    java.io.EOFException: reached end of stream after reading 5000075 bytes; 10000150 bytes expected
        at com.google.common.io.ByteStreams.readFully(ByteStreams.java:643)
        at com.google.common.io.ByteStreams.readFully(ByteStreams.java:622)
        at Main.executeGet(Main.java:116)
        at Main.access$100(Main.java:57)
        at Main$ExecuteRequestTask.call(Main.java:177)
        at Main$ExecuteRequestTask.call(Main.java:146)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
    
  3. Read timeouts on the client side. Seems that server just stops to send response.

    Request failed: /async?584054021 60120ms
    java.net.SocketTimeoutException: Read timed out
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:170)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at java.io.BufferedInputStream.read1(BufferedInputStream.java:284)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
        at sun.net.www.MeteredStream.read(MeteredStream.java:134)
        at java.io.FilterInputStream.read(FilterInputStream.java:133)
        at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3336)
        at com.google.common.io.ByteStreams.read(ByteStreams.java:733)
        at com.google.common.io.ByteStreams.readFully(ByteStreams.java:641)
        at com.google.common.io.ByteStreams.readFully(ByteStreams.java:622)
        at Main.executeGet(Main.java:116)
        at Main.access$100(Main.java:57)
        at Main$ExecuteRequestTask.call(Main.java:177)
        at Main$ExecuteRequestTask.call(Main.java:146)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
    
  4. Sometimes server returns 500 with no stacktrace on the server side

    Request failed: /async?953102103 7ms
    java.io.IOException: Server returned HTTP response code: 500 for URL: http://127.0.0.1:8989/async?953102103
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1840)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
        at Main.executeGet(Main.java:112)
        at Main.access$100(Main.java:57)
        at Main$ExecuteRequestTask.call(Main.java:177)
        at Main$ExecuteRequestTask.call(Main.java:146)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
    
  5. Request lifecycle violation

    org.eclipse.jetty.io.EofException: request lifecycle violation
    at org.eclipse.jetty.server.HttpConnection$SendCallback.process(HttpConnection.java:703)
    at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241)
    at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:224)
    at org.eclipse.jetty.server.HttpConnection.send(HttpConnection.java:521)
    at org.eclipse.jetty.server.HttpChannel.sendResponse(HttpChannel.java:706)
    at org.eclipse.jetty.server.HttpChannel.write(HttpChannel.java:755)
    at org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:179)
    at org.eclipse.jetty.server.HttpOutput$AsyncWrite.process(HttpOutput.java:1076)
    at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241)
    at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:224)
    at org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:372)
    at Main$AsyncWriteListener.onWritePossible(Main.java:274)
    at org.eclipse.jetty.server.HttpOutput.run(HttpOutput.java:934)
    at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1286)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:427)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:253)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
    at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
    at java.lang.Thread.run(Thread.java:745)
    
  6. IllegalStateException, onCompleteSuccess

    2016-08-01 20:58:40.959:WARN:oejut.QueuedThreadPool:http-worker-21: 
    java.lang.IllegalStateException
    at org.eclipse.jetty.server.HttpOutput$AsyncICB.onCompleteSuccess(HttpOutput.java:993)
    at org.eclipse.jetty.server.HttpOutput$AsyncWrite.onCompleteSuccess(HttpOutput.java:1129)
    at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:325)
    at org.eclipse.jetty.util.IteratingCallback.succeeded(IteratingCallback.java:365)
    at org.eclipse.jetty.server.HttpConnection$SendCallback.onCompleteSuccess(HttpConnection.java:788)
    at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:325)
    at org.eclipse.jetty.util.IteratingCallback.succeeded(IteratingCallback.java:365)
    at org.eclipse.jetty.io.WriteFlusher$PendingState.complete(WriteFlusher.java:269)
    at org.eclipse.jetty.io.WriteFlusher.completeWrite(WriteFlusher.java:394)
    at org.eclipse.jetty.io.SelectChannelEndPoint$3.run(SelectChannelEndPoint.java:107)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
    at java.lang.Thread.run(Thread.java:745)
    2016-08-01 20:58:40.960:WARN:oejut.QueuedThreadPool:http-worker-21: Unexpected thread death: org.eclipse.jetty.util.thread.QueuedThreadPool$2@72ab6156 in http-worker{STARTED,20<=20<=200,i=11,q=2}
    

Tested on:

  • Jetty: 9.3.9.M1, 9.3.11.M0, 9.3.11.v20160721
  • Java:
    java -version java version "1.8.0_101" Java(TM) SE Runtime Environment (build 1.8.0_101-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)

Stress test source: https://github.com/arhimondr/jetty-async-io/blob/master/src/main/java/Main.java

When using synchronous IO in the same test - everything works fine.

@joakime joakime added the Bug label Aug 1, 2016

@joakime

This comment has been minimized.

Show comment
Hide comment
@joakime

joakime Aug 1, 2016

Member

The ISE origin - https://github.com/eclipse/jetty.project/blob/jetty-9.3.11.v20160721/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java#L844

Looks like the HttpOutput couldn't use setWriteListener() as the connection was no longer open.

We should make that ISE more clear as to why the ISE was thrown.

Member

joakime commented Aug 1, 2016

The ISE origin - https://github.com/eclipse/jetty.project/blob/jetty-9.3.11.v20160721/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java#L844

Looks like the HttpOutput couldn't use setWriteListener() as the connection was no longer open.

We should make that ISE more clear as to why the ISE was thrown.

@arhimondr

This comment has been minimized.

Show comment
Hide comment
@arhimondr

arhimondr Aug 1, 2016

Contributor

HttpOutput is CLOSED at that step. I wrapped ISE in custom RuntimeException that has HttpOutput.state in it's message.

Contributor

arhimondr commented Aug 1, 2016

HttpOutput is CLOSED at that step. I wrapped ISE in custom RuntimeException that has HttpOutput.state in it's message.

@gregw

This comment has been minimized.

Show comment
Hide comment
@gregw

gregw Aug 2, 2016

Contributor

The code looks pretty much OK. You don't need to copy the byte array before writing it as it is never updated and thus can be shared, but that is not an issue.

I've been able to reproduce a

Error during async writeorg.eclipse.jetty.io.EofException: Async closed

    at org.eclipse.jetty.server.HttpOutput.closed(HttpOutput.java:248)
    at org.eclipse.jetty.server.HttpOutput$AsyncWrite.onCompleteSuccess(HttpOutput.java:1131)
    at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:325)
    at org.eclipse.jetty.util.IteratingCallback.succeeded(IteratingCallback.java:365)
    at org.eclipse.jetty.server.HttpConnection$SendCallback.onCompleteSuccess(HttpConnection.java:788)
    at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:325)
    at org.eclipse.jetty.util.IteratingCallback.succeeded(IteratingCallback.java:365)
    at org.eclipse.jetty.io.WriteFlusher$PendingState.complete(WriteFlusher.java:269)
    at org.eclipse.jetty.io.WriteFlusher.completeWrite(WriteFlusher.java:394)
    at org.eclipse.jetty.io.SelectChannelEndPoint$3.run(SelectChannelEndPoint.java:107)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$1.run(QueuedThreadPool.java:589)
    at java.lang.Thread.run(Thread.java:745)

Which in itself looks like it could be a bug. If we close the HttpOutput because all is written, it should not matter that the state is UNREADY. But I have to convince myself it is valid to be in UNREADY state if you have just completed writing the final write. So I'll dig a little deeper tomorrow.

Contributor

gregw commented Aug 2, 2016

The code looks pretty much OK. You don't need to copy the byte array before writing it as it is never updated and thus can be shared, but that is not an issue.

I've been able to reproduce a

Error during async writeorg.eclipse.jetty.io.EofException: Async closed

    at org.eclipse.jetty.server.HttpOutput.closed(HttpOutput.java:248)
    at org.eclipse.jetty.server.HttpOutput$AsyncWrite.onCompleteSuccess(HttpOutput.java:1131)
    at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:325)
    at org.eclipse.jetty.util.IteratingCallback.succeeded(IteratingCallback.java:365)
    at org.eclipse.jetty.server.HttpConnection$SendCallback.onCompleteSuccess(HttpConnection.java:788)
    at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:325)
    at org.eclipse.jetty.util.IteratingCallback.succeeded(IteratingCallback.java:365)
    at org.eclipse.jetty.io.WriteFlusher$PendingState.complete(WriteFlusher.java:269)
    at org.eclipse.jetty.io.WriteFlusher.completeWrite(WriteFlusher.java:394)
    at org.eclipse.jetty.io.SelectChannelEndPoint$3.run(SelectChannelEndPoint.java:107)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$1.run(QueuedThreadPool.java:589)
    at java.lang.Thread.run(Thread.java:745)

Which in itself looks like it could be a bug. If we close the HttpOutput because all is written, it should not matter that the state is UNREADY. But I have to convince myself it is valid to be in UNREADY state if you have just completed writing the final write. So I'll dig a little deeper tomorrow.

@arhimondr

This comment has been minimized.

Show comment
Hide comment
@arhimondr

arhimondr Aug 2, 2016

Contributor

The problem is that above failure happens not on the last chunk it the response. Somehow _complete filed in HttpOutput#AsyncWrite is set to true in a middle of the response. It can be set to true only, and only if HttpOutput.written == Response#contentLength(_channel.getResponse().isAllContentWritten(_written)). When debugger stops i can't see the values in written or in contentLength that can lead to complete flag to be true. Both Response#contentLength and HttpOutput.written are being accessed from different threads, and access to them is not protected with lock.

Contributor

arhimondr commented Aug 2, 2016

The problem is that above failure happens not on the last chunk it the response. Somehow _complete filed in HttpOutput#AsyncWrite is set to true in a middle of the response. It can be set to true only, and only if HttpOutput.written == Response#contentLength(_channel.getResponse().isAllContentWritten(_written)). When debugger stops i can't see the values in written or in contentLength that can lead to complete flag to be true. Both Response#contentLength and HttpOutput.written are being accessed from different threads, and access to them is not protected with lock.

@arhimondr

This comment has been minimized.

Show comment
Hide comment
@arhimondr

arhimondr Aug 2, 2016

Contributor

UPD:
Everything seems to be fine with Response#contentLength and HttpOutput.written.

Seems that SendCallback runs outdated _callback somehow. After i synchronized all the methods of SendCallback, failures with Async closed disappeared. However 500 with no stacktrace, and EOF before sending the entire data issues still there.

Contributor

arhimondr commented Aug 2, 2016

UPD:
Everything seems to be fine with Response#contentLength and HttpOutput.written.

Seems that SendCallback runs outdated _callback somehow. After i synchronized all the methods of SendCallback, failures with Async closed disappeared. However 500 with no stacktrace, and EOF before sending the entire data issues still there.

@arhimondr

This comment has been minimized.

Show comment
Hide comment
@arhimondr

arhimondr Aug 2, 2016

Contributor

FWIW: Just wondering. SendCallback is the member of HttpConnection. That fact that synchronization helped, means that SendCallback is being accessed from the multiple threads at once, right? Isn't it strange that single HttpConnection is being accesses in such manner? Maybe this is the root of evil?

Contributor

arhimondr commented Aug 2, 2016

FWIW: Just wondering. SendCallback is the member of HttpConnection. That fact that synchronization helped, means that SendCallback is being accessed from the multiple threads at once, right? Isn't it strange that single HttpConnection is being accesses in such manner? Maybe this is the root of evil?

@gregw

This comment has been minimized.

Show comment
Hide comment
@gregw

gregw Aug 3, 2016

Contributor

HttpConnection should only ever have 1 request active at once, so reusing SendCallback should not be a problem and it should not be accessed by several threads at once - only by the thread that is writing. The atomic state in HttpOutput should be enough to both stop 2 threads writing at once and to act as a memory barrier between different writes.

So still pondering and testing....

Contributor

gregw commented Aug 3, 2016

HttpConnection should only ever have 1 request active at once, so reusing SendCallback should not be a problem and it should not be accessed by several threads at once - only by the thread that is writing. The atomic state in HttpOutput should be enough to both stop 2 threads writing at once and to act as a memory barrier between different writes.

So still pondering and testing....

@gregw

This comment has been minimized.

Show comment
Hide comment
@gregw

gregw Aug 3, 2016

Contributor

It is definitely something strange with _complete and/or _written. For a total content length of 10,000,150 I'm getting the failure in closed at 3000045, so that is after 6 writes (3 big , 3 small), but with 7 more writes to go. So it doesn't look like a problem with state left over from a prior requests, as the first 6 writes are not affected????

Contributor

gregw commented Aug 3, 2016

It is definitely something strange with _complete and/or _written. For a total content length of 10,000,150 I'm getting the failure in closed at 3000045, so that is after 6 writes (3 big , 3 small), but with 7 more writes to go. So it doesn't look like a problem with state left over from a prior requests, as the first 6 writes are not affected????

@gregw

This comment has been minimized.

Show comment
Hide comment
@gregw

gregw Aug 3, 2016

Contributor

I believe I have found the problem: AsyncWrite.onCompleteSuccess() calls super and then if complete ==last write) and calls closed(). But if the thread calling closed() gets pre-empted, then the call to super.onCompleteSuccess, can allows handling of subsequent requests to continue, only for the preempted thread to suddenly call closed() at a random time, resulting in the random failures.

Working on a fix....

Contributor

gregw commented Aug 3, 2016

I believe I have found the problem: AsyncWrite.onCompleteSuccess() calls super and then if complete ==last write) and calls closed(). But if the thread calling closed() gets pre-empted, then the call to super.onCompleteSuccess, can allows handling of subsequent requests to continue, only for the preempted thread to suddenly call closed() at a random time, resulting in the random failures.

Working on a fix....

gregw added a commit that referenced this issue Aug 3, 2016

@gregw gregw changed the title from Sporadic failures when using asynchronous IO in Servlet to async IO Write closed race Aug 3, 2016

@gregw

This comment has been minimized.

Show comment
Hide comment
@gregw

gregw Aug 3, 2016

Contributor

The fix was to move move the final boolean _complete from AsyncWrite to the abstract AsyncICB (and renamed to _last which is a better name). The AsyncICB.onCompleteSuccess() implementation now updates the state, calls closed() if _last==true and only then notifies the callback.

I also tidied up the _written field a little bit, as it was vulnerable to manipulation by failed calls, but it was not really part of the problem. Your test now runs for me without failure.

Contributor

gregw commented Aug 3, 2016

The fix was to move move the final boolean _complete from AsyncWrite to the abstract AsyncICB (and renamed to _last which is a better name). The AsyncICB.onCompleteSuccess() implementation now updates the state, calls closed() if _last==true and only then notifies the callback.

I also tidied up the _written field a little bit, as it was vulnerable to manipulation by failed calls, but it was not really part of the problem. Your test now runs for me without failure.

@arhimondr

This comment has been minimized.

Show comment
Hide comment
@arhimondr

arhimondr Aug 3, 2016

Contributor

Everything works perfect after your fix. Thanks for being so responsive and figuring this out.

Contributor

arhimondr commented Aug 3, 2016

Everything works perfect after your fix. Thanks for being so responsive and figuring this out.

@arhimondr arhimondr closed this Aug 3, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment