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


##########
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 rework is in d56b02ddb24. `close()` now frees everything at once if 
`next()` was called again since it returned the batch: at the end of the 
stream, or after a failed `next()`. Otherwise it releases the stream, the 
dictionaries and the task at once, and a Cleaner frees the last batch's buffers 
and the allocator once the reader is unreachable. Each `NativeColumnVector` 
refers to its reader, so this only happens once the batch and its column 
vectors are unreachable too. A thread that is inside `next()` still gets the 
release once `get_next` returns, as before. A `next()` called after `close()` 
now throws an `IllegalStateException` rather than return false. The consumer 
tracking and the `releaser` pool are gone.
   
   On the two points I had to check:
   1. The C stream interface says that the lifetimes of what `get_schema` and 
`get_next` return are not tied to that of the stream.
   2. The header already requires that the release of an array or a schema not 
use the stream or the task (`SparkDsTaskContext`). The Rust SDK exports each 
batch with `FFI_ArrowArray::new`, whose `ArrayData` is `'static` and so cannot 
borrow the `'task` context. The C++ SDK uses `arrow::ExportRecordBatch`, whose 
release only drops the references to the buffers. I wrote this into the 
`SupportsArrowStreamRead` contract. The header now also says that Spark itself 
may release the last array after the stream, from a JVM Cleaner thread.
   
   The class doc notes the GC trade-off. For the tests, `free the buffers of a 
reader closed at the end of its stream without a GC` is the end-of-stream test, 
with a reader whose `next()` failed as well. `close a reader while another 
thread reads the batch that it returned` now checks four things: the stream and 
the task are released at once, the other thread still reads valid values, 
`next()` fails on both threads, and a GC frees the buffers. 
`NativeDataSourceSuite` also waits in `afterAll` until no reader allocator is 
left, so that later suites in the same JVM don't see that memory freed while 
they run.
   
   <sub>Automated reply (Opus)</sub>
   



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