Skip to content

Commit

Permalink
Set finalizer on underlying Memory object in Julia 1.11+
Browse files Browse the repository at this point in the history
In Julia 1.11+ with the new `Memory` object, the memory is not actually
owned by the constructed `Array` and therefore the finalizer may
trigger too early when the underlying memory is transfered from an
`Array` to a different kind of owner (such as `IOBuffer`).

Fix this by conditionally checking for the Julia 1.11+ behavior, and
instead setting the finalizer on the `Memory`, if necessary.

Duplicates the improvement made to the Mmap stdlib's implementation:
JuliaLang/julia#54210
  • Loading branch information
jmert committed Apr 25, 2024
1 parent becf00d commit a95270e
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/mmap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ function _mmap(::Type{Array{T}}, dims::NTuple{N,Integer},
ptr = _sys_mmap(C_NULL, mmaplen, prot, flags, fd, Int64(offset) - page_pad)
aptr = convert(Ptr{T}, ptr + page_pad)
array = unsafe_wrap(Array{T,N}, aptr, dims)
finalizer(_ -> _sys_unmap!(ptr, mmaplen), array)
finalizer(_ -> _sys_unmap!(ptr, mmaplen),
@static VERSION >= v"1.11.0-DEV" && hasfield(Array, :ref) ? array.ref.mem : array)
return array
end
function _mmap(::Type{Array{T}}, len::Int, prot::MmapProtection, flags::MmapFlags,
Expand Down
30 changes: 30 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,33 @@ end
@test A == UnixMmap.msync!(A, UnixMmap.MS_ASYNC)
end
end

@testset "Julia Memory-backed arrays" begin
# See JuliaLang/julia#54128 and JuliaLang/julia#54210
mktempdir() do tdir
cd(tdir) do
# generate and store some random data in a file
open("rand.dat", "w") do io
write(io, rand(UInt8, 1024))
end

# read the contents of the file twice
# 1st: read as a plain array
plain = open("rand.dat", "r") do io
read(io)
end
# 2nd: read via a memory mapped array, wrapped in an IOBuffer.
# The IOBuffer is important in order to lose the original Array wrapper of
# the underlying Memory on Julia 1.11+
mapbuf = open("rand.dat", "r") do io
IOBuffer(mmap(io, Vector{UInt8}))
end

# Force a garbage collection
GC.gc(true)
# Then check that the plain array matches what can be retrieved from the IOBuffer
# If the underlying memory map has been freed, this should result in a segfault.
@test plain == take!(mapbuf)
end
end
end

0 comments on commit a95270e

Please sign in to comment.