From 0ecf4f8f12ddfb611c5ebe037c73fcc606c5ec7f Mon Sep 17 00:00:00 2001 From: K Pamnany Date: Wed, 25 Oct 2023 15:43:50 -0400 Subject: [PATCH] Add GC-safe regions around some `ccall`s Specifically, around `LZ4_compress_fast`, `LZ4_compress_destSize` and `LZ4_decompress_safe`. If these are called on large data (on the order of GBs), the calls could take multiple seconds during which GC cannot run without these GC-safe regions. --- src/headers/lz4.jl | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/headers/lz4.jl b/src/headers/lz4.jl index 374b3cd..76e96e1 100644 --- a/src/headers/lz4.jl +++ b/src/headers/lz4.jl @@ -46,7 +46,15 @@ This function never writes outside `dst` buffer, nor read outside `source` buffe Returns the number of bytes written into buffer `dst` (necessarily <= dstcapacity) """ function LZ4_compress_fast(src, dst, srcsize, dstcapacity, acceleration=1) - ret = ccall((:LZ4_compress_fast, liblz4), Cint, (Ptr{UInt8}, Ptr{UInt8}, Cint, Cint, Cint), src, dst, srcsize, dstcapacity, acceleration) + csrc = Base.cconvert(Ptr{UInt8}, src) + cdst = Base.cconvert(Ptr{UInt8}, dst) + GC.@preserve csrc cdst begin + # Allow Julia to GC while compressing + gc_state = @ccall(jl_gc_safe_enter()::Int8) + ret = ccall((:LZ4_compress_fast, liblz4), Cint, (Ptr{UInt8}, Ptr{UInt8}, Cint, Cint, Cint), Base.unsafe_convert(Ptr{UInt8}, csrc)::Ptr{UInt8}, Base.unsafe_convert(Ptr{UInt8}, cdst)::Ptr{UInt8}, srcsize, dstcapacity, acceleration) + # Leave GC-safe region, waiting for GC to complete if it's running + @ccall(jl_gc_safe_leave(gc_state::Int8)::Cvoid) + end check_compression_error(ret, "LZ4_compress_fast") end @@ -63,7 +71,16 @@ or fill `dst` buffer completely with as much data as possible from `src`. Returns number of bytes written into `dst` (necessarily <= dstcapacity) """ function LZ4_compress_destSize(src, dst, srcsize, dstcapacity) - ret = ccall((:LZ4_compress_destSize, liblz4), Cint, (Ptr{UInt8}, Ptr{UInt8}, Ptr{Cint}, Cint), src, dst, srcsize, dstcapacity) + csrc = Base.cconvert(Ptr{UInt8}, src) + cdst = Base.cconvert(Ptr{UInt8}, dst) + csrcsize = Base.cconvert(Ptr{Cint}, srcsize) + GC.@preserve csrc cdst csrcsize begin + # Allow Julia to GC while compressing + gc_state = @ccall(jl_gc_safe_enter()::Int8) + ret = ccall((:LZ4_compress_destSize, liblz4), Cint, (Ptr{UInt8}, Ptr{UInt8}, Ptr{Cint}, Cint), Base.unsafe_convert(Ptr{UInt8}, csrc)::Ptr{UInt8}, Base.unsafe_convert(Ptr{UInt8}, cdst)::Ptr{UInt8}, Base.unsafe_convert(Ptr{Cint}, csrcsize)::Ptr{Cint}, dstcapacity) + # Leave GC-safe region, waiting for GC to complete if it's running + @ccall(jl_gc_safe_leave(gc_state::Int8)::Cvoid) + end check_compression_error(ret, "LZ4_compress_destSize") end @@ -150,7 +167,15 @@ dstcapacity : is the size of destination buffer, which must be already allocated Returns the number of bytes decompressed into destination buffer (necessarily <= dstcapacity) """ function LZ4_decompress_safe(src, dst, cmpsize, dstcapacity) - ret = ccall((:LZ4_decompress_safe, liblz4), Cint, (Ptr{UInt8}, Ptr{UInt8}, Cint, Cint), src, dst, cmpsize, dstcapacity) + csrc = Base.cconvert(Ptr{UInt8}, src) + cdst = Base.cconvert(Ptr{UInt8}, dst) + GC.@preserve csrc cdst begin + # Allow Julia to GC while decompressing + gc_state = @ccall(jl_gc_safe_enter()::Int8) + ret = ccall((:LZ4_decompress_safe, liblz4), Cint, (Ptr{UInt8}, Ptr{UInt8}, Cint, Cint), Base.unsafe_convert(Ptr{UInt8}, csrc)::Ptr{UInt8}, Base.unsafe_convert(Ptr{UInt8}, cdst)::Ptr{UInt8}, cmpsize, dstcapacity) + # Leave GC-safe region, waiting for GC to complete if it's running + @ccall(jl_gc_safe_leave(gc_state::Int8)::Cvoid) + end check_decompression_error(ret, "LZ4_decompress_safe") end