sarutak commented on code in PR #59961:
URL: https://github.com/apache/spark/pull/59961#discussion_r4445437083


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/v2/ffi/NativeDataSource.scala:
##########
@@ -858,9 +897,21 @@ class NativePartitionReader(
   override def close(): Unit = synchronized {
     if (!closed) {
       closed = true
-      if (!reading) {
+      // Another thread may be in next, or still read the batch that next 
returned to it.
+      if (!reading && consumerDone) {

Review Comment:
   The direction makes sense to me. One concern about the Cleaner: if every 
close leaves the last imported batch to it, every reader, including one that 
was read to the end, gives its native memory back only after a GC. Off-heap 
memory does not trigger a GC, and a batch that lived as long as the task has 
often been promoted, so it may wait for an old-generation collection. Could 
`close()` release the buffers right away when no batch can still be read, e.g. 
once `next()` has returned false or thrown, and keep the Cleaner only for a 
close while a batch that `next()` returned may still be in use?



##########
sql/core/src/test/scala/org/apache/spark/sql/execution/datasources/v2/ffi/NativeDataSourceSuite.scala:
##########
@@ -1256,6 +1256,81 @@ class NativeDataSourceSuite extends QueryTest with 
SharedSparkSession {
     }
   }
 
+  nativeTest("close a reader while another thread reads the batch that it 
returned") {
+    val session = newSession(defaultPackage)
+    val pool = ThreadUtils.newDaemonSingleThreadExecutor("native-reader")
+    val marker = new File(Utils.createTempDir(), "released")
+    // get_next does not wait: the file .continue exists.
+    new File(marker.getPath + ".continue").createNewFile()
+    val df = range(session, "end" -> "10", "partitions" -> "1", "slow_read" -> 
marker.getPath)
+    val batch = scan(df.select("id")).toBatch
+    val reader = 
batch.createReaderFactory().createColumnarReader(batch.planInputPartitions().head)
+    try {
+      def onPool[T](f: => T): T = pool.submit(new Callable[T] {
+        override def call(): T = f
+      }).get(1, TimeUnit.MINUTES)
+      assert(onPool(reader.next()))
+      // The other thread may still read the batch, whose buffers the reader 
keeps until that
+      // thread calls next again.
+      reader.close()
+      assert(!marker.isFile, "The stream was released.")
+      // Nor does a next or another close on this thread, which did not read 
the batch.
+      assert(!reader.next())
+      reader.close()
+      assert(!marker.isFile, "The stream was released.")
+      val rows = onPool((0 until 
reader.get().numRows()).map(reader.get().column(0).getLong))
+      assert(rows === (0L until 10L))
+      assert(!onPool(reader.next()))
+      assert(Files.readString(marker.toPath) == "after get_next")
+    } finally {
+      // Once the other thread ends, a close that the test did not reach 
releases the reader.
+      pool.shutdown()
+      pool.awaitTermination(1, TimeUnit.MINUTES)
+      Utils.tryLogNonFatalError(reader.close())
+    }
+  }
+
+  nativeTest("release a closed reader when the thread that read its batch 
closes it or ends") {
+    val session = newSession(defaultPackage)
+    def onPool[T](pool: ExecutorService)(f: => T): T = pool.submit(new 
Callable[T] {
+      override def call(): T = f
+    }).get(1, TimeUnit.MINUTES)
+    // Returns a reader closed while a thread of the pool may still read its 
batch, and the file
+    // that the library creates when the stream is released.
+    def closedReader(pool: ExecutorService) = {

Review Comment:
   The Scala linter fails here: "Public method must have explicit type".



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