Copilot commented on code in PR #12487:
URL: https://github.com/apache/gluten/pull/12487#discussion_r3556377527


##########
backends-velox/src/main/scala/org/apache/gluten/execution/VeloxBroadcastBuildSideCache.scala:
##########
@@ -249,4 +262,43 @@ object VeloxBroadcastBuildSideCache
       HashJoinBuilder.clearHashTable(key, value.pointer)
     }
   }
+
+  private def registerLocalCleanupIfNeeded(
+      broadcastHashTableId: String,
+      enableLocalHashTableCleanup: Boolean): Unit = {
+    if (!enableLocalHashTableCleanup || !TaskResources.inSparkTask()) {
+      return
+    }
+
+    val resourceId = 
s"broadcast-hash-table-local-cleanup:$broadcastHashTableId"
+    TaskResources.addResourceIfNotRegistered(
+      resourceId,
+      () => {
+        localReferenceCounts
+          .computeIfAbsent(broadcastHashTableId, _ => new AtomicInteger(0))
+          .incrementAndGet()
+
+        new TaskResource {
+          override def release(): Unit = 
releaseLocalReference(broadcastHashTableId)
+
+          override def priority(): Int = 200
+
+          override def resourceName(): String = resourceId
+        }
+      }
+    )
+  }

Review Comment:
   `localReferenceCounts` is updated concurrently from multiple tasks, but the 
increment path isn’t synchronized with `releaseLocalReference`. Because 
`releaseLocalReference` uses `remove(key, refCount)` on the same 
`AtomicInteger` instance, a concurrent increment can still be lost and the 
entry can be invalidated even though another task has already increased the 
count. Synchronize the refcount increment with the decrement/invalidate path.



##########
backends-velox/src/main/scala/org/apache/gluten/execution/VeloxBroadcastBuildSideCache.scala:
##########
@@ -249,4 +262,43 @@ object VeloxBroadcastBuildSideCache
       HashJoinBuilder.clearHashTable(key, value.pointer)
     }
   }
+
+  private def registerLocalCleanupIfNeeded(
+      broadcastHashTableId: String,
+      enableLocalHashTableCleanup: Boolean): Unit = {
+    if (!enableLocalHashTableCleanup || !TaskResources.inSparkTask()) {
+      return
+    }
+
+    val resourceId = 
s"broadcast-hash-table-local-cleanup:$broadcastHashTableId"
+    TaskResources.addResourceIfNotRegistered(
+      resourceId,
+      () => {
+        localReferenceCounts
+          .computeIfAbsent(broadcastHashTableId, _ => new AtomicInteger(0))
+          .incrementAndGet()
+
+        new TaskResource {
+          override def release(): Unit = 
releaseLocalReference(broadcastHashTableId)
+
+          override def priority(): Int = 200
+
+          override def resourceName(): String = resourceId
+        }
+      }
+    )
+  }
+
+  private def releaseLocalReference(broadcastHashTableId: String): Unit = {
+    val refCount = localReferenceCounts.get(broadcastHashTableId)
+    if (refCount == null) {
+      return
+    }
+
+    val remaining = refCount.decrementAndGet()
+    if (remaining <= 0 && localReferenceCounts.remove(broadcastHashTableId, 
refCount)) {
+      logInfo(s"Cleaning up broadcast hash table with local fallback: 
$broadcastHashTableId")
+      invalidateBroadcastHashtable(broadcastHashTableId)
+    }
+  }

Review Comment:
   `releaseLocalReference` performs decrement / conditional map removal / cache 
invalidation without synchronization. With concurrent tasks, this can 
invalidate the broadcast hash table even if another task is in the middle of 
registering/incrementing the same `AtomicInteger` (since `remove(key, 
refCount)` only checks object identity). Wrap the whole decrement + invalidate 
decision in the same lock used by the increment path so the refcount logic is 
consistent.



##########
backends-velox/src/main/scala/org/apache/gluten/execution/VeloxBroadcastBuildSideCache.scala:
##########
@@ -249,4 +262,43 @@ object VeloxBroadcastBuildSideCache
       HashJoinBuilder.clearHashTable(key, value.pointer)
     }
   }
+
+  private def registerLocalCleanupIfNeeded(
+      broadcastHashTableId: String,
+      enableLocalHashTableCleanup: Boolean): Unit = {
+    if (!enableLocalHashTableCleanup || !TaskResources.inSparkTask()) {

Review Comment:
   The new task-local cleanup path (via `enableLocalHashTableCleanup`, 
TaskResources registration, and refcounted invalidation) changes lifecycle 
behavior for broadcast hash tables but isn’t covered by existing tests in the 
Velox backend. Adding a regression test that exercises the `executionId == 
null` path and asserts the cache is cleared at task completion would help 
prevent reintroducing the memory-leak / outstanding-pool failure this PR 
targets.



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