viirya opened a new pull request, #59663:
URL: https://github.com/apache/spark/pull/59663

   ### What changes were proposed in this pull request?
   
   When another thread is invoking task listeners at the moment the task 
completes (e.g. a kill thread running failure listeners because an interrupt 
listener threw, or a non-task thread that called `markTaskFailed` through 
`Utils.tryWithSafeFinallyAndFailureCallbacks`), 
`TaskContextImpl.markTaskCompleted` returns without running the completion 
listeners, and the other thread only drains its own stack, so the completion 
listeners never run. Failure listeners queued while the completing thread runs 
completion listeners, and completion listeners added after completion while 
another thread holds the invocation, are dropped the same way.
   
   This PR keeps the completion listeners on the completing thread:
   
   - `markTaskCompleted` claims the listener invocation in the same 
synchronized block that sets `completed`. If another thread holds it, 
`markTaskCompleted` registers itself as the next invoker and waits; the holder 
hands the invocation over directly when it is done, so no third thread can take 
it in between.
   - When a listener throws, the failure listeners run nested in the current 
invocation instead of clearing and reclaiming `listenerInvocationThread`, which 
left a window in which another thread could take over the listeners.
   - Failure listeners left to the invoking thread run before its next 
listener, so failure cleanup (e.g. a state store `abort()`) runs before the 
completion listener that releases the store, also after a handover.
   - Before releasing the invocation, a thread runs the completion listeners 
other threads left to it, unless a thread waiting in `markTaskCompleted` takes 
them over. The release and the handover are in a `finally`.
   - The handover wait keeps interrupts (they are restored once it is done) and 
logs a warning periodically; the first one carries the holder's stack trace, 
taken outside the TaskContext lock. The interval is settable for tests.
   - The StreamingShuffleSuite test that documented the skipped completion 
listener now checks that it runs on the completing thread, and the comments in 
`ErrorNotifier` and `StreamingShuffleReader` are updated accordingly.
   
   This was split out of #59332 during its review, since it changes listener 
behavior for every task.
   
   ### Why are the changes needed?
   
   Completion listeners do the task's cleanup (closing readers, releasing state 
stores, freeing task-managed resources). When they are skipped, those resources 
leak; when they run late on another thread, they run after the executor has 
already released the task's memory. Failure listeners that are dropped skip 
failure cleanup such as aborting a state store update.
   
   Note on the wait: it has no time bound, so if the holder's listeners block, 
the completing task waits too. On the kill path this is not new: 
`TaskRunner.kill` runs `Task.kill` (and so the failure listeners) holding the 
`TaskRunner` lock, which the task thread needs anyway in 
`setTaskFinishedAndClearInterruptStatus` before the task can finish. The new 
wait only applies when a non-kill thread holds the invocation; 
`spark.task.reaper.enabled` with a positive `spark.task.reaper.killTimeout` 
bounds it in that case, and the periodic warning names the holder and shows its 
stack.
   
   Out of scope: post-status-update listeners can be dropped the same way if 
another thread holds the invocation when `invokePostStatusUpdateListeners` 
runs; this PR does not change that.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No API change. In the cases above, completion and failure listeners that 
were skipped now run, and `markTaskCompleted` waits for the other thread to 
finish its listeners first.
   
   ### How was this patch tested?
   
   New tests in `TaskContextSuite`:
   - a listener added from another thread as completion starts does not take 
over the listeners;
   - a failing listener does not let another thread take over the listeners;
   - completion waits for another thread invoking listeners (a kill whose 
interrupt listener throws) and runs the listeners on the completing thread;
   - a listener on the invoking thread that completes the task runs the 
completion listeners nested;
   - a failure listener left to the completing thread runs before its next 
completion listener, without and with a handover;
   - completion listeners left to a failure invoker run before it releases, a 
failure of such a listener reaches the invoker's caller, and the invocation is 
still released;
   - an interrupt does not stop the handover wait and is kept;
   - the handover wait warns with the holder's name and, on the first warning 
only, its stack trace.
   
   Updated test in `StreamingShuffleSuite`: "background markTaskFailed races 
with task thread markTaskCompleted".
   
   Each behavior was checked by reverting it alone and confirming that a test 
fails. All core and sql suites that call `markTaskFailed`, `markTaskCompleted` 
or `markInterrupted` pass locally (`TaskContextSuite`, `BasePythonRunnerSuite`, 
`StreamingShuffle{,Reader,Writer}Suite`, `PartiallySerializedBlockSuite`, 
`ShuffleBlockFetcherIteratorSuite`, `SupportsArchiveFormatSuite`, 
`JdbcTaskInterruptSuite`, `JsonV1Suite`, `JsonV2Suite`, 
`ParquetStorageFilterSuite`, `RocksDB*Suite`, `ValueStateSuite`), as well as 
`ExecutorSuite` and `JobCancellationSuite`.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Code (Claude Opus 5.5)
   
   This pull request and its description were written by Isaac.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to