You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Split out of #11 review discussion so it can get its own design and review.
Problem
Context.closeAsync() awaits every in-flight publish before finalizing. If work owned by a ContextPublisher calls closeAsync() (or close()) and only completes its transport future after that close settles, the two futures wait on each other and neither ever completes. The context is left permanently closing.
Reproduced by @Pedro-Revez-Silva with a publisher that starts a worker thread, returns an incomplete transport future, has the worker call context.closeAsync(), and completes the transport only once close finishes. An executor callback reaches the same state. The direct same-thread variant (publisher calls closeAsync() inline) is the degenerate case of the same cycle.
This is pre-existing on main, not introduced by #11.
Why the obvious fix does not work
A same-thread guard was attempted on #11 and reverted (436ba37). Rejecting close when the calling thread is inside publish() only catches the inline case; any delegated thread walks straight past it. Chasing the worker thread moves the hole rather than closing it, and the tolerance machinery built around it (same-thread exclusion, reopening closed_/closing_ from the publish failure path, pending-count rechecks before finalization) produced its own defects: close completing successfully while isClosed() == false with events queued, a late publisher failure resurrecting an already-closed context and emitting a second Close, and restoration clearing closing_ letting a concurrent close overwrite closingFuture_.
Suggested direction
Attack the dependency rather than the callback, per @Pedro-Revez-Silva: redesign the close/publish dependency so publisher-owned asynchronous work cannot form this cycle.
Close only awaits in-flight publishes because a failed publish restores its batch (pendingCount_.addAndGet(...) in flush()'s failure handler) and close must not finalize with events pending. Break that edge and no thread — inline, worker, or executor — can form the cycle:
have restoration treat events as undeliverable once close has finalized, rather than requiring close to wait; or
give close a bounded wait, after which it finalizes and reports the undelivered batch honestly.
Either removes the need for a contract, for reentrancy detection, and for the failure modes above.
Constraints
Java 1.6 source/bytecode floor (Android); java8.util.concurrent.CompletableFuture, anonymous inner classes only in main/java.
Split out of #11 review discussion so it can get its own design and review.
Problem
Context.closeAsync()awaits every in-flight publish before finalizing. If work owned by aContextPublishercallscloseAsync()(orclose()) and only completes its transport future after that close settles, the two futures wait on each other and neither ever completes. The context is left permanentlyclosing.Reproduced by @Pedro-Revez-Silva with a publisher that starts a worker thread, returns an incomplete transport future, has the worker call
context.closeAsync(), and completes the transport only once close finishes. An executor callback reaches the same state. The direct same-thread variant (publisher callscloseAsync()inline) is the degenerate case of the same cycle.This is pre-existing on
main, not introduced by #11.Why the obvious fix does not work
A same-thread guard was attempted on #11 and reverted (
436ba37). Rejecting close when the calling thread is insidepublish()only catches the inline case; any delegated thread walks straight past it. Chasing the worker thread moves the hole rather than closing it, and the tolerance machinery built around it (same-thread exclusion, reopeningclosed_/closing_from the publish failure path, pending-count rechecks before finalization) produced its own defects: close completing successfully whileisClosed() == falsewith events queued, a late publisher failure resurrecting an already-closed context and emitting a secondClose, and restoration clearingclosing_letting a concurrent close overwriteclosingFuture_.Suggested direction
Attack the dependency rather than the callback, per @Pedro-Revez-Silva: redesign the close/publish dependency so publisher-owned asynchronous work cannot form this cycle.
Close only awaits in-flight publishes because a failed publish restores its batch (
pendingCount_.addAndGet(...)inflush()'s failure handler) and close must not finalize with events pending. Break that edge and no thread — inline, worker, or executor — can form the cycle:Either removes the need for a contract, for reentrancy detection, and for the failure modes above.
Constraints
java8.util.concurrent.CompletableFuture, anonymous inner classes only inmain/java.Contextmust never end up closed with events pending, and close must never hang.