Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
name = "Arrow"
uuid = "69666777-d1a9-59fb-9406-91d4454c9d45"
authors = ["quinnj <quinn.jacobd@gmail.com>"]
version = "2.8.1"
version = "2.9.0"

[deps]
ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd"
Expand Down
24 changes: 1 addition & 23 deletions src/cdatainterface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -77,36 +77,14 @@ const CDATA_FLAG_MAP_KEYS_SORTED = Int64(4)

Holds C-side pointers for an imported Arrow C Data Interface pair.
Call `Arrow.release_c_data` to release C resources explicitly.
If the handle is garbage-collected without being released, an error is logged
and `Arrow.UNRELEASED_HANDLE_COUNT` is incremented.
"""
mutable struct CDataHandle
schema_ptr::Ptr{ArrowSchema}
array_ptr::Ptr{ArrowArray}
released::Bool
end

# Counts CDataHandles that were GC'd without an explicit release_c_data call.
const UNRELEASED_HANDLE_COUNT = Threads.Atomic{Int}(0)

function _warn_unreleased(h::CDataHandle)
h.released && return
Threads.atomic_add!(UNRELEASED_HANDLE_COUNT, 1)
# Use jl_safe_printf since task switches are forbidden in finalizers.
ccall(
:jl_safe_printf,
Cvoid,
(Cstring,),
"Arrow.CDataHandle GC'd without explicit release_c_data — resource leak detected\n",
)
_release_cdata_handle(h)
end

function CDataHandle(sp::Ptr{ArrowSchema}, ap::Ptr{ArrowArray})
h = CDataHandle(sp, ap, false)
finalizer(_warn_unreleased, h)
return h
end
CDataHandle(sp::Ptr{ArrowSchema}, ap::Ptr{ArrowArray}) = CDataHandle(sp, ap, false)

function _release_cdata_handle(h::CDataHandle)
h.released && return
Expand Down
38 changes: 0 additions & 38 deletions test/cdatainterface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
# limitations under the License.

@testset "Arrow C Data Interface" begin
_initial_count = Arrow.UNRELEASED_HANDLE_COUNT[]

@testset "struct sizes" begin
@test sizeof(Arrow.ArrowSchema) == 9 * 8
@test sizeof(Arrow.ArrowArray) == 10 * 8
Expand Down Expand Up @@ -756,36 +754,6 @@
end
end

# ── Finalizer / leak counter ──────────────────────────────────────────────

@testset "finalizer: increments leak counter when not released" begin
before = Arrow.UNRELEASED_HANDLE_COUNT[]
redirect_stderr(devnull) do
let
av = to_arrow(Int32[1, 2, 3])
s_ref, a_ref = Arrow.to_c_data(av)
Arrow.from_c_data(_cptr(s_ref), _cptr(a_ref))
# CImportedArray and its CDataHandle go out of scope here
end
GC.gc(true)
GC.gc(true)
end
@test Arrow.UNRELEASED_HANDLE_COUNT[] == before + 1
end

@testset "finalizer: does NOT increment counter when released explicitly" begin
before = Arrow.UNRELEASED_HANDLE_COUNT[]
let
av = to_arrow(Int32[1, 2, 3])
s_ref, a_ref = Arrow.to_c_data(av)
col = Arrow.from_c_data(_cptr(s_ref), _cptr(a_ref))
Arrow.release_c_data(col)
end
GC.gc(true)
GC.gc(true)
@test Arrow.UNRELEASED_HANDLE_COUNT[] == before
end

# ── Empty arrays ─────────────────────────────────────────────────────────

@testset "round-trip: empty Int32 array" begin
Expand Down Expand Up @@ -829,10 +797,4 @@
@test isequal(collect(imported), data)
Arrow.release_c_data(imported)
end

@testset "no unexpected resource leaks" begin
GC.gc(true)
GC.gc(true)
@test Arrow.UNRELEASED_HANDLE_COUNT[] == _initial_count + 1 # +1 for the intentional leak test
end
end # @testset "Arrow C Data Interface"
Loading