dongjoon-hyun commented on code in PR #59663:
URL: https://github.com/apache/spark/pull/59663#discussion_r4339138469


##########
core/src/main/scala/org/apache/spark/TaskContextImpl.scala:
##########
@@ -217,12 +239,45 @@ private[spark] class TaskContextImpl(
     // writer that adds listeners lazily while it pulls its input. And since 
the completion
     // listeners only run after `completed` is set, they run after every 
pre-completion listener,
     // even if another thread is invoking listeners at this point.
+    //
+    // The completion listeners then run on this thread too, and this method 
returns only after
+    // they have run. If it returned while another thread is invoking 
listeners (e.g. a kill thread
+    // running failure listeners), that thread would not run them, so cleanup 
would be skipped,
+    // and the task would release its memory and report its result anyway. So 
the listener
+    // invocation is claimed together with setting `completed`, which also 
keeps a thread that adds
+    // a completion listener at that moment from running them; if another 
thread holds it, that
+    // thread hands it over to this one when done. Failure listeners pending 
at that point run
+    // first (see `invokeListeners()`). A completion listener added after this 
method has run the
+    // others runs on the thread adding it, or, if another thread is invoking 
listeners at that
+    // moment, on that thread before it stops (see `invokeListeners()`).
+    //
+    // The wait for the other thread has no time bound: if its listeners 
block, so does this task.
+    // It logs a warning periodically, the first one with that thread's stack 
trace. A kill thread
+    // runs the failure listeners holding the lock of the executor's 
TaskRunner, which the task

Review Comment:
   **1. [Medium] The handover wait now comes before the task releases its block 
locks and memory, so the kill path can deadlock**
   
   "This only adds a wait for other threads" holds for the `TaskRunner` lock, 
but on master the task thread blocks on that lock only after its cleanup. When 
a kill thread holds the invocation, master's `markTaskCompleted` returns at 
once, `Task.run` releases the unroll memory in its `finally` 
(`Task.scala:152-153`), and the `TaskRunner` releases the task's block locks 
and memory (`Executor.scala:916-917`). Only then does the task thread block in 
`setTaskFinishedAndClearInterruptStatus` (`Executor.scala:746`). With this PR, 
it waits at L292-293, before all of that.
   
   So a failure listener on the kill thread that waits for anything released in 
that cleanup now deadlocks. For example, a third-party interrupt listener 
throws, so `Task.kill` runs the failure listeners on the kill thread while it 
holds the `TaskRunner` lock and the JVM-wide `TaskContext` lock 
(`Task.scala:235`). One of them calls `blockManager.removeBlock(b)`, whose 
`lockForWriting` waits for the read lock that the killed task still holds on 
the cached block `b` until `Executor.scala:916`. On master this finishes as 
soon as the task thread reaches its cleanup. Now neither thread moves, and 
since the kill thread keeps the JVM-wide lock, every later `Task.kill`, 
`interruptible()` and `createResourceUninterruptibly` on the executor blocks 
too. On a reaper thread, `kill()` never returns, so the JVM kill does not fire 
either.
   
   In-tree listeners don't do this (RocksDB's failure listener only aborts, and 
the JDBC interrupt listeners catch `NonFatal`), so it takes third-party code. 
Still, the description's "On the kill path this is not new" and this comment 
say that the kill path is not affected. Could they say instead that the task 
now waits before it releases its block locks and memory, so a failure listener 
on a kill thread must not wait for either?



-- 
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