Skip to content

Commit 0c5e5e5

Browse files
committed
Set finalizer on underlying Memory object in Julia 1.11+
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
1 parent becf00d commit 0c5e5e5

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-1
lines changed

src/mmap.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ function _mmap(::Type{Array{T}}, dims::NTuple{N,Integer},
8080
ptr = _sys_mmap(C_NULL, mmaplen, prot, flags, fd, Int64(offset) - page_pad)
8181
aptr = convert(Ptr{T}, ptr + page_pad)
8282
array = unsafe_wrap(Array{T,N}, aptr, dims)
83-
finalizer(_ -> _sys_unmap!(ptr, mmaplen), array)
83+
finalizer(_ -> _sys_unmap!(ptr, mmaplen),
84+
@static VERSION >= v"1.11.0-DEV" && hasfield(Array, :ref) ? array.ref.mem : array)
8485
return array
8586
end
8687
function _mmap(::Type{Array{T}}, len::Int, prot::MmapProtection, flags::MmapFlags,

test/runtests.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,33 @@ end
248248
@test A == UnixMmap.msync!(A, UnixMmap.MS_ASYNC)
249249
end
250250
end
251+
252+
@testset "Julia Memory-backed arrays" begin
253+
# See JuliaLang/julia#54128 and JuliaLang/julia#54210
254+
mktempdir() do tdir
255+
cd(tdir) do
256+
# generate and store some random data in a file
257+
open("rand.dat", "w") do io
258+
write(io, rand(UInt8, 1024))
259+
end
260+
261+
# read the contents of the file twice
262+
# 1st: read as a plain array
263+
plain = open("rand.dat", "r") do io
264+
read(io)
265+
end
266+
# 2nd: read via a memory mapped array, wrapped in an IOBuffer.
267+
# The IOBuffer is important in order to lose the original Array wrapper of
268+
# the underlying Memory on Julia 1.11+
269+
mapbuf = open("rand.dat", "r") do io
270+
IOBuffer(mmap(io, Vector{UInt8}))
271+
end
272+
273+
# Force a garbage collection
274+
GC.gc(true)
275+
# Then check that the plain array matches what can be retrieved from the IOBuffer
276+
# If the underlying memory map has been freed, this should result in a segfault.
277+
@test plain == take!(mapbuf)
278+
end
279+
end
280+
end

0 commit comments

Comments
 (0)