sunchao commented on code in PR #5613:
URL: https://github.com/apache/datafusion-comet/pull/5613#discussion_r3928405605


##########
native/core/src/execution/memory_pools/fair_pool.rs:
##########
@@ -60,26 +100,102 @@ impl CometFairMemoryPool {
         task_memory_manager_handle: Arc<Global<JObject<'static>>>,
         pool_size: usize,
     ) -> CometFairMemoryPool {
+        Self::with_bridge(
+            Box::new(JniTaskMemoryBridge {
+                task_memory_manager_handle,
+            }),
+            pool_size,
+        )
+    }
+
+    fn with_bridge(bridge: Box<dyn TaskMemoryBridge>, pool_size: usize) -> 
CometFairMemoryPool {
         Self {
-            task_memory_manager_handle,
+            bridge,
             pool_size,
-            state: Mutex::new(CometFairPoolState { used: 0, num: 0 }),
+            state: Mutex::new(CometFairPoolState {
+                used: 0,
+                num: 0,
+                jvm_held: 0,
+                pending_acquires: 0,
+                deferred_release: 0,
+                paying_deferred: false,
+            }),
+            deferred_done: Condvar::new(),
         }
     }
 
     fn acquire(&self, additional: usize) -> CometResult<i64> {
-        let handle = self.task_memory_manager_handle.as_obj();
-        JVMClasses::with_env(|env| unsafe {
-            jni_call!(env,
-              comet_task_memory_manager(handle).acquire_memory(additional as 
i64) -> i64)
-        })
+        self.bridge.acquire(additional)
     }
 
     fn release(&self, size: usize) -> CometResult<()> {
-        let handle = self.task_memory_manager_handle.as_obj();
-        JVMClasses::with_env(|env| unsafe {
-            jni_call!(env, 
comet_task_memory_manager(handle).release_memory(size as i64) -> ())
-        })
+        self.bridge.release(size)
+    }
+
+    /// Debits a release from the JVM-side balance and returns how much to 
hand back now. A
+    /// release that would zero the balance while acquires are in flight keeps 
one byte back,
+    /// because Spark drops the task's accounting entry at zero and a parked 
acquire then indexes
+    /// the missing entry. Blocking instead could deadlock: the waiter may 
need this very memory.
+    /// The n-1 bytes freed here still wake Spark's waiter; the single held 
byte only matters
+    /// in a pool small enough that one byte decides the fair-share threshold, 
and even there
+    /// the deferred payoff releases it as soon as in-flight acquires drain.
+    fn plan_release(state: &mut CometFairPoolState, bytes: usize) -> usize {
+        state.jvm_held = state
+            .jvm_held
+            .checked_sub(bytes)
+            .expect("released more bytes than the JVM side holds");
+        if bytes > 0 && state.jvm_held == 0 && state.pending_acquires > 0 {

Review Comment:
   [P1] Avoid retaining the byte a waiting acquire may need
   
   Could you avoid withholding one byte until `finish_acquire`? This can 
deadlock the default off-heap pool. In a 1 GiB Spark execution pool, let 
another task hold 900 MiB, let this task hold 100 MiB, and let a second native 
consumer for this task request 100 MiB. Spark parks that request because this 
task is below its 1/(2N) minimum share. When the holder frees 100 MiB, this 
branch sends only 100 MiB - 1. On wake, Spark computes `toGrant = 100 MiB - 1`; 
because that is short of the request and `curMem + toGrant = 100 MiB` is still 
below 256 MiB, it waits again. The deferred byte is paid only by 
`finish_acquire`, which cannot run while this acquire is waiting.
   
   I reproduced this against unmodified Spark 4.1.3 `ExecutionMemoryPool`: 
retaining one byte left the grower in `WAITING`, and freeing one additional 
byte from the other task let it complete. The new stub test misses this because 
it grants the full request after any release without reapplying Spark's 
free-memory and minimum-share checks. Could you preserve the task entry without 
withholding capacity needed by the waiter?



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