Skip to content

Commit

Permalink
Merge pull request #43 from JuliaInterop/tb/gcsafe
Browse files Browse the repository at this point in the history
Allow the GC to run during ObjectiveC calls.
  • Loading branch information
maleadt authored Sep 19, 2024
2 parents 6f034f7 + 2578dc2 commit e2ebc4a
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false
matrix:
version: ['1.6', '1.7', '1.8', '1.9', '1.10', 'pre', 'nightly']
version: ['1.10', 'pre', 'nightly']
os: ['macOS-latest']
arch: [x64]
steps:
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Preferences = "21216c6a-2e73-6563-6e65-726566657250"
[compat]
CEnum = "0.5"
Preferences = "1"
julia = "1.6"
julia = "1.10"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
89 changes: 65 additions & 24 deletions src/syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,51 @@ function render_c_arg(io, obj, typ)
end
end

# ensure that the GC can run during a ccall. this is only safe if callbacks
# into Julia transition back to GC-unsafe, which is the case on Julia 1.10+.
#
# doing so is tricky, because no GC operations are allowed after the transition,
# meaning we have to do our own argument conversion instead of relying on ccall.
#
# TODO: replace with JuliaLang/julia#49933 once merged
function make_gcsafe(ex)
# decode the ccall
if !Meta.isexpr(ex, :call) || ex.args[1] != :ccall
error("Can only make ccall expressions GC-safe")
end
target = ex.args[2]
rettyp = ex.args[3]
argtypes = ex.args[4].args
args = ex.args[5:end]

code = quote
end

# assign argument values to variables
vars = [Symbol("arg$i") for i in 1:length(args)]
for (var, arg) in zip(vars, args)
push!(code.args, :($var = $arg))
end

# convert the arguments
converted = [Symbol("converted_arg$i") for i in 1:length(args)]
for (converted, argtyp, var) in zip(converted, argtypes, vars)
push!(code.args, :($converted = Base.unsafe_convert($argtyp, Base.cconvert($argtyp, $var))))
end

# emit a gcsafe ccall
append!(code.args, (quote
GC.@preserve $(vars...) begin
gc_state = ccall(:jl_gc_safe_enter, Int8, ())
ret = ccall($target, $rettyp, ($(argtypes...),), $(converted...))
ccall(:jl_gc_safe_leave, Cvoid, (Int8,), gc_state)
ret
end
end).args)

return code
end

function class_message(class_name, msg, rettyp, argtyps, argvals)
quote
class = Class($(String(class_name)))
Expand All @@ -136,18 +181,19 @@ function class_message(class_name, msg, rettyp, argtyps, argvals)
end
ret = $(
if ABI.use_stret(rettyp)
# we follow Julia's ABI implementation, so ccall will handle the sret box
:(
# we follow Julia's ABI implementation,
# so ccall will handle the sret box
make_gcsafe(:(
ccall(:objc_msgSend_stret, $rettyp,
(Ptr{Cvoid}, Ptr{Cvoid}, $(map(esc, argtyps)...)),
class, sel, $(map(esc, argvals)...))
)
))
else
:(
make_gcsafe(:(
ccall(:objc_msgSend, $rettyp,
(Ptr{Cvoid}, Ptr{Cvoid}, $(map(esc, argtyps)...)),
class, sel, $(map(esc, argvals)...))
)
))
end
)
@static if $tracing
Expand Down Expand Up @@ -178,18 +224,19 @@ function instance_message(instance, typ, msg, rettyp, argtyps, argvals)
end
ret = $(
if ABI.use_stret(rettyp)
# we follow Julia's ABI implementation, so ccall will handle the sret box
:(
# we follow Julia's ABI implementation,
# so ccall will handle the sret box
make_gcsafe(:(
ccall(:objc_msgSend_stret, $rettyp,
(id{Object}, Ptr{Cvoid}, $(map(esc, argtyps)...)),
$instance, sel, $(map(esc, argvals)...))
)
))
else
:(
make_gcsafe(:(
ccall(:objc_msgSend, $rettyp,
(id{Object}, Ptr{Cvoid}, $(map(esc, argtyps)...)),
$instance, sel, $(map(esc, argvals)...))
)
))
end
)
@static if $tracing
Expand Down Expand Up @@ -512,12 +559,10 @@ macro objcproperties(typ, ex)
end

# finally, call our parent's `getproperty`
final = :(invoke(getproperty, Tuple{supertype($(esc(typ))), Symbol}, object, field))
if VERSION >= v"1.8"
push!(current.args, :(@inline $final))
else
push!(current.args, :($final))
end
final = :(@inline invoke(getproperty,
Tuple{supertype($(esc(typ))), Symbol},
object, field))
push!(current.args, final)
getproperties_ex = quote
# XXX: force const-prop on field, without inlining everything?
function Base.getproperty(object::$(esc(typ)), field::Symbol)
Expand All @@ -543,14 +588,10 @@ macro objcproperties(typ, ex)
end

# finally, call our parent's `setproperty!`
final = :(invoke(setproperty!,
Tuple{supertype($(esc(typ))), Symbol, Any},
object, field, value))
if VERSION >= v"1.8"
push!(current.args, :(@inline $final))
else
push!(current.args, :($final))
end
final = :(@inline invoke(setproperty!,
Tuple{supertype($(esc(typ))), Symbol, Any},
object, field, value))
push!(current.args, final)
setproperties_ex = quote
# XXX: force const-prop on field, without inlining everything?
function Base.setproperty!(object::$(esc(typ)), field::Symbol, value::Any)
Expand Down

0 comments on commit e2ebc4a

Please sign in to comment.