zhangfengcdt opened a new issue, #5664:
URL: https://github.com/apache/datafusion-comet/issues/5664

   ### Describe the bug
   
   #4734 (fix for #4725) makes the global tokio runtime releasable and calls 
release_runtime() from CometDriverPlugin.shutdown() / 
CometExecutorPlugin.shutdown(). That fixes the hang whenever 
SparkContext.stop() runs. It does not fix the case where an application simply 
returns from main (or a PySpark driver exits) without calling spark.stop() — 
which is a very common shape for batch jobs. In that case the JVM still hangs 
forever in DestroyJavaVM, exactly as described in #4725.
   
   The reason is an ordering problem that #4734 cannot get around on its own:
   
   1. Tokio runtime threads that call back into the JVM (memory pool 
acquireMemory, ScanExec fed from a JVM iterator, CometScalarSubquery, …) attach 
lazily and permanently via jni-rs, using AttachCurrentThread. That makes them 
non-daemon JVM threads.
   2. When main returns, the launcher calls DestroyJavaVM. HotSpot's 
Threads::destroy_vm() waits until it is the last non-daemon thread before it 
runs shutdown hooks (invoke_shutdown_hooks() comes after the wait).
   3. Spark's ShutdownHookManager is what would call SparkContext.stop() → 
plugin shutdown() → release_runtime(). So the runtime can only be released by a 
hook that can only run after the runtime's threads have exited. Circular; the 
JVM waits forever.
   
   Without an explicit SparkContext.stop() the release in #4734 is unreachable.
   
   
   ### Steps to reproduce
   
   The precondition is simply "at least one tokio runtime thread has called 
into the JVM", which any workload with JVM callbacks from spawned work 
satisfies. Sketch (upstream-only triggers):
   
   ```
   // spark-submit / plain `java` main; Comet plugin + native exec enabled;
   // spark.memory.offHeap.enabled=true (unified pool → JVM acquireMemory 
callbacks)
   val spark = SparkSession.builder().master("local[2]")
     .config("spark.plugins", "org.apache.spark.CometPlugin")
     .config("spark.sql.extensions", 
"org.apache.comet.CometSparkSessionExtensions")
     .config("spark.comet.enabled", "true")
     .config("spark.comet.exec.enabled", "true")
     .config("spark.memory.offHeap.enabled", "true")
     .config("spark.memory.offHeap.size", "256m")
     .getOrCreate()
   spark.range(1000000).write.mode("overwrite").parquet(path)
   val df = spark.read.parquet(path)
     .selectExpr("sum(id)")
     .where("1 <= (SELECT max(id) FROM parquet.`" + path + "`)") // scalar 
subquery → JVM call from native
   df.collect()
   require(df.queryExecution.executedPlan.toString.contains("Comet"))
   println("main returning")
   // intentionally NO spark.stop()
   ```
   
   Run it as a real process and wait: the process prints main returning and 
never exits. jstack shows DestroyJavaVM waiting plus N unnamed non-daemon 
threads ("Thread-30", "Thread-31", … — one per runtime worker) with no Java 
frames; /proc/<pid>/task/*/comm identifies them as tokio-rt-worker.
   
   
   ### Expected behavior
   
   A Spark application that returns from main without SparkContext.stop() 
exits, the same way it does with Comet disabled. Spark's own thread pools are 
all daemon precisely so that this works; Comet's runtime threads are currently 
the exception.
   
   
   ### Additional context
   
   - Comet main after #4734 (1.0.0); jni 0.22.4; observed on Linux (JDK 17, 
Spark 4.1) and macOS (JDK 11, Spark 3.5).
   - The upstream development.md threading section already documents that 
attachment is cached until the worker exits and that "the tokio runtime has to 
be shut down for the JVM to be able to exit" — this issue is the case where 
that shutdown can never be initiated.
   


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