From 840b83de350160724aa10bfc8dad5a2979409aaf Mon Sep 17 00:00:00 2001 From: Ulf Adams Date: Fri, 22 Feb 2019 10:57:30 +0100 Subject: [PATCH] Complete channel initialization in the event loop If addLast is called outside an event loop, then the handler is added in the 'pending' state. Sending an event to the pipeline does not send it to the last handler, but to the last _non-pending_ handler. We therefore have to make sure to involve the event loop _before_ marking the channel as ready to be used. Thanks to @Reflexe who pointed me in the right direction. Fixes #7464. --- .../remote/blobstore/http/HttpBlobStore.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStore.java b/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStore.java index 1b84c966841a3b..6c0c623c1338bb 100644 --- a/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStore.java +++ b/src/main/java/com/google/devtools/build/lib/remote/blobstore/http/HttpBlobStore.java @@ -271,7 +271,16 @@ private Channel acquireUploadChannel() throws InterruptedException { p.addLast(new HttpUploadHandler(creds)); } - channelReady.setSuccess(ch); + if (!ch.eventLoop().inEventLoop()) { + // If addLast is called outside an event loop, then it doesn't complete until the + // event loop is run again. In that case, a message sent to the last handler gets + // delivered to the last non-pending handler, which will most likely end up + // throwing UnsupportedMessageTypeException. Therefore, we only complete the + // promise in the event loop. + ch.eventLoop().execute(() -> channelReady.setSuccess(ch)); + } else { + channelReady.setSuccess(ch); + } } catch (Throwable t) { channelReady.setFailure(t); } @@ -332,7 +341,16 @@ private Future acquireDownloadChannel() { p.addLast(new HttpDownloadHandler(creds)); } - channelReady.setSuccess(ch); + if (!ch.eventLoop().inEventLoop()) { + // If addLast is called outside an event loop, then it doesn't complete until the + // event loop is run again. In that case, a message sent to the last handler gets + // delivered to the last non-pending handler, which will most likely end up + // throwing UnsupportedMessageTypeException. Therefore, we only complete the + // promise in the event loop. + ch.eventLoop().execute(() -> channelReady.setSuccess(ch)); + } else { + channelReady.setSuccess(ch); + } } catch (Throwable t) { channelReady.setFailure(t); }