From 27e6aefd1538c735fefcdd1dc595e742902c8d24 Mon Sep 17 00:00:00 2001 From: BenWhitehead Date: Mon, 16 Dec 2024 23:21:37 +0000 Subject: [PATCH] fix: fix interrupt spiral in grpc ReadObject drainQueue If our thread is interrupted while attempting to drainQueue poll will throw an InterruptedException, instead of setting the flag back on the thread immediately we need to defer setting it until we complete our draining. If we don't defer setting it, we can never actually drain our queue. --- .../GapicUnbufferedReadableByteChannel.java | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedReadableByteChannel.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedReadableByteChannel.java index 2efc340c26..aa2bc9f60f 100644 --- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedReadableByteChannel.java +++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedReadableByteChannel.java @@ -234,35 +234,42 @@ public void close() throws IOException { } private void drainQueue() throws IOException { - IOException ioException = null; - while (queue.nonEmpty()) { - try { - java.lang.Object queueValue = queue.poll(); - if (queueValue instanceof ReadObjectResponse) { - ReadObjectResponse resp = (ReadObjectResponse) queueValue; - ResponseContentLifecycleHandle handle = rclm.get(resp); - handle.close(); - } else if (queueValue == EOF_MARKER || queueValue instanceof Throwable) { - break; - } - } catch (IOException e) { - if (ioException == null) { - ioException = e; - } else if (ioException != e) { - ioException.addSuppressed(e); + boolean shouldInterupt = false; + try { + IOException ioException = null; + while (queue.nonEmpty()) { + try { + java.lang.Object queueValue = queue.poll(); + if (queueValue instanceof ReadObjectResponse) { + ReadObjectResponse resp = (ReadObjectResponse) queueValue; + ResponseContentLifecycleHandle handle = rclm.get(resp); + handle.close(); + } else if (queueValue == EOF_MARKER || queueValue instanceof Throwable) { + break; + } + } catch (IOException e) { + if (ioException == null) { + ioException = e; + } else if (ioException != e) { + ioException.addSuppressed(e); + } + } catch (InterruptedException e) { + shouldInterupt = true; + if (ioException == null) { + ioException = new InterruptedIOException(); + } else { + ioException.addSuppressed(e); + } } - } catch (InterruptedException e) { + } + if (ioException != null) { + throw ioException; + } + } finally { + if (shouldInterupt) { Thread.currentThread().interrupt(); - if (ioException == null) { - ioException = new InterruptedIOException(); - } else { - ioException.addSuppressed(e); - } } } - if (ioException != null) { - throw ioException; - } } ApiFuture getResult() {