This is an automated email from the ASF dual-hosted git repository.

pan3793 pushed a commit to branch branch-0.6
in repository https://gitbox.apache.org/repos/asf/celeborn.git


The following commit(s) were added to refs/heads/branch-0.6 by this push:
     new d54462e48 [CELEBORN-2433] Fix Utils.tryWithResources evaluating 
by-name resource expression twice
d54462e48 is described below

commit d54462e483aa78445df3e5a5d2b5a6c76808dccf
Author: yangjie01 <[email protected]>
AuthorDate: Thu Aug 20 22:20:19 2026 +0800

    [CELEBORN-2433] Fix Utils.tryWithResources evaluating by-name resource 
expression twice
    
    ### What changes were proposed in this pull request?
    
    `Utils.tryWithResources` declared its resource parameter by-name (`f: => 
R`) but referenced it twice: `val res = f` evaluated the caller's expression 
once, then `func(f)` evaluated it a *second* time. The instance handed to 
`func` was therefore never the one closed in `finally`. This PR passes the 
already-evaluated `res` to `func` (one line), and adds a guard test to 
`UtilsSuite` covering both halves of the helper's contract: the expression 
evaluates exactly once and the instance rece [...]
    
    ### Why are the changes needed?
    
    Every call with a non-idempotent resource expression opened the resource 
twice and leaked one handle. Current callers affected (CLI): `CliConfigManager` 
opens its config file twice per read and leaks one `FileInputStream` (which 
also blocks file deletion on Windows); `CliVersionProvider` leaks one 
`BufferedSource` per lookup. Concise repro against current main:
    
    ```scala
    val evaluations = new AtomicInteger(0)
    val received = Utils.tryWithResources {
      evaluations.incrementAndGet()
      new Closeable { var closed = false; def close() = closed = true }
    }(res => res)
    // main: evaluations == 2, received.closed == false (leaked)
    ```
    
    ### Does this PR resolve a correctness bug?
    
    - [ ] Yes
    
    ### Does this PR introduce _any_ user-facing change?
    
    - [ ] Yes
    
    No: internal utility fix; the only observable effect is that the CLI no 
longer opens files twice / leaks handles.
    
    ### How was this patch tested?
    
    - Guard test verified red/green: against the unfixed tree the assertions 
fail (`2 did not equal 1`, `received.closed == false`); they pass with the fix.
    
    Closes #3814 from LuciferYang/fix-trywithresources-double-eval.
    
    Authored-by: yangjie01 <[email protected]>
    Signed-off-by: Cheng Pan <[email protected]>
    (cherry picked from commit ba4fa59f0279bee81e7cfa838e083a603d331ff2)
    Signed-off-by: Cheng Pan <[email protected]>
---
 .../org/apache/celeborn/common/util/Utils.scala    |  2 +-
 .../apache/celeborn/common/util/UtilsSuite.scala   | 33 ++++++++++++++++++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/common/src/main/scala/org/apache/celeborn/common/util/Utils.scala 
b/common/src/main/scala/org/apache/celeborn/common/util/Utils.scala
index 109fdc326..55a0db173 100644
--- a/common/src/main/scala/org/apache/celeborn/common/util/Utils.scala
+++ b/common/src/main/scala/org/apache/celeborn/common/util/Utils.scala
@@ -1107,7 +1107,7 @@ object Utils extends Logging {
   def tryWithResources[R <: Closeable, U](f: => R)(func: R => U): U = {
     val res = f
     try {
-      func(f)
+      func(res)
     } finally {
       if (null != res) {
         res.close()
diff --git 
a/common/src/test/scala/org/apache/celeborn/common/util/UtilsSuite.scala 
b/common/src/test/scala/org/apache/celeborn/common/util/UtilsSuite.scala
index 7cadaf07e..f212428b0 100644
--- a/common/src/test/scala/org/apache/celeborn/common/util/UtilsSuite.scala
+++ b/common/src/test/scala/org/apache/celeborn/common/util/UtilsSuite.scala
@@ -17,8 +17,10 @@
 
 package org.apache.celeborn.common.util
 
+import java.io.Closeable
 import java.util
 import java.util.Collections
+import java.util.concurrent.atomic.AtomicInteger
 
 import org.scalatest.matchers.must.Matchers.contain
 import org.scalatest.matchers.should.Matchers.convertToAnyShouldWrapper
@@ -278,4 +280,35 @@ class UtilsSuite extends CelebornFunSuite {
       celebornConf)
     assert(testInstance.isInstanceOf[DefaultIdentityProvider])
   }
+
+  test("tryWithResources should evaluate the resource expression once and 
close the resource given to the function") {
+    val evaluations = new AtomicInteger(0)
+
+    class Resource extends Closeable {
+      var closed: Boolean = false
+      override def close(): Unit = closed = true
+    }
+
+    val received = Utils.tryWithResources {
+      evaluations.incrementAndGet()
+      new Resource
+    }(res => res)
+
+    assert(evaluations.get() == 1)
+    assert(received.closed)
+
+    var thrownReceived: Resource = null
+    intercept[RuntimeException] {
+      Utils.tryWithResources {
+        evaluations.incrementAndGet()
+        new Resource
+      } { res =>
+        thrownReceived = res
+        throw new RuntimeException("func failed")
+      }
+    }
+    assert(evaluations.get() == 2)
+    assert(thrownReceived.closed)
+    assert(thrownReceived ne received)
+  }
 }

Reply via email to