Skip to content

Consolidate duplicated read() error handling and add spring-core interrupt test for SubscriberInputStream - #4

Draft
jaipilot[bot] wants to merge 1 commit into
jaipilot-v8/spring-37159from
jaipilot/pr-3-jm6Y2cRZ77pf
Draft

Consolidate duplicated read() error handling and add spring-core interrupt test for SubscriberInputStream#4
jaipilot[bot] wants to merge 1 commit into
jaipilot-v8/spring-37159from
jaipilot/pr-3-jm6Y2cRZ77pf

Conversation

@jaipilot

@jaipilot jaipilot Bot commented Aug 22, 2026

Copy link
Copy Markdown

Context

The PR fixes a busy-spin/interrupt-handling bug in the lock-free await() loop shared (as documented near-duplicates) by spring-web's org.springframework.http.client.SubscriberInputStream and spring-core's org.springframework.core.io.buffer.SubscriberInputStream, and rethrows IOException as-is from both read() and read(byte[], int, int) instead of wrapping it via Exceptions.propagate.

What changed here

  1. Test lock (behavior, no production change): spring-core's SubscriberInputStream had zero direct test coverage of the interrupt-while-awaiting-data fix (only spring-web's sibling class got a test in the PR). Added DataBufferUtilsTests.inputStreamSubscriberInterruptWhileAwaitingData(), mirroring the existing spring-web test against the reactivestreams-based DataBufferUtils.subscriberInputStream() API: a publisher that never emits, a reader thread that parks in read(), an interrupt() call, and assertions that the read returns via InterruptedIOException("Interrupted while awaiting data") with the interrupt status restored.
  2. Cleanup (no behavior change): In both SubscriberInputStream classes, the PR's new 3-line IOException-rethrow snippet was appended identically to the catch (Throwable ex) blocks of both read() and read(byte[], int, int), which already had an identical remainder (this.closed = true; requiredSubscriber().cancel(); cleanAndFinalize(); throw Exceptions.propagate(ex);). The two catch blocks were now fully identical duplicates within each class. Extracted a private handleReadError(Throwable ex) throws IOException helper per class and replaced both catch bodies with throw handleReadError(ex);. No change to thrown exception types, messages, ordering of closed/cancel/cleanAndFinalize, or the interrupt-status contract; both classes remain package-private and final with no subclassing/reflection use, so there is no external-compatibility impact.

Evidence

  • behavior_baseline and behavior_candidate: identical focused command (:spring-core:test --tests DataBufferUtilsTests + :spring-web:test --tests SubscriberInputStreamTests) passed before and after the cleanup edit.
  • full_build: :spring-core:check and :spring-web:check (compile for all Java release variants, Checkstyle, architecture check, JMH compile, full test suites) passed.

Not changed

  • The lock-free await()/parkedThread CAS-park-unpark protocol itself was left untouched: it is delicate, correctness-critical concurrency code, and no algorithmic or allocation improvement in the PR's changed lines could be justified with the required timing/operation-count evidence for what is a cold (interrupt/spurious-wakeup only) path.
  • No JDK/build/dependency version change: the PR's changed code only relies on JDK APIs already used elsewhere in the same classes.

Generated by JAIPilot Cloud for #3 from Anthropic session sesn_01Kf9h5j18YVjm6Y2cRZ77pf.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants