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


##########
core/src/main/scala/org/apache/spark/TaskContextImpl.scala:
##########
@@ -412,21 +566,61 @@ private[spark] class TaskContextImpl(
           //    [[markTaskFailed]] here will have no effect and we simply 
resume running the
           //    remaining completion handlers.
           try {
-            listenerInvocationThread = None
-            markTaskFailed(e)
+            markTaskFailed(e, Nested)
           } catch {
             case t: Throwable => e.addSuppressed(t)
-          } finally {
-            synchronized {
-              if (listenerInvocationThread.isEmpty) {
-                listenerInvocationThread = Some(Thread.currentThread())
-              }
-            }
           }
           listenerExceptions += e
           logError(log"Error in ${MDC(LISTENER, name)}", e)
       }
     }
+
+    def runLeftListeners(invoke: () => Unit): Unit = {
+      try {
+        invoke()
+      } catch {
+        // The nested invocation has logged each listener error.
+        case e: Throwable => listenerExceptions += e
+      }
+    }
+
+    // Chooses the next thing to do under the TaskContext lock, to be done 
outside of it.
+    def nextStep(): Option[() => Unit] = synchronized {
+      if (!invokesFailureListeners && failureCauseOpt.isDefined && 
!onFailureCallbacks.empty()) {
+        val cause = failureCauseOpt.get
+        Some(() => runLeftListeners(() => invokeTaskFailureListeners(cause, 
Nested)))
+      } else if (!listeners.empty()) {
+        val listener = listeners.pop()
+        Some(() => runListener(listener))
+      } else if (mode != Nested && nextListenerInvocationThread.isEmpty && 
completed &&

Review Comment:
   **5. [Low] The post-status-update and kill-thread invocations now run 
leftover listeners without the task's `TaskContext`, and no test covers it**
   
   `invokePostStatusUpdateListeners` (L204-208) claims like any other 
invocation, so it now also runs pending failure listeners (L589-591) and 
leftover completion listeners (here). It runs on the task thread after 
`Task.run` has called `TaskContext.unset()` (`Task.scala:166`) and after the 
executor has sent `FINISHED` (`Executor.scala:1051`), and a kill thread that 
holds the invocation has no `TaskContext` either. Some completion listeners 
read `TaskContext.get()` instead of their argument, e.g. 
`TungstenAggregationIterator.scala:497` and 
`ObjectAggregationIterator.scala:91`, and a helper thread with the task's 
`TaskContext`, such as `PipedRDD`'s stdin writer, can register them lazily 
after completion. Left to one of these invocations, they now fail with an NPE 
where master dropped them. On the kill path that failure then escapes 
`markInterrupted` (L641) to the kill caller, as a failing failure listener 
already could.
   
   The SPARK-57491 test only checks a context that never completes, so nothing 
covers these branches outside `markTaskCompleted`. Also, the new scaladoc at 
L483-484 ("they queue them and leave them to this thread") does not hold for 
post-status-update listeners, which are still dropped when another thread holds 
the invocation, as the description says.
   
   Could a test cover the post-status-update invocation of a completed context, 
and could the scaladoc mention both points?



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