Skip to content

Commit 2578dc2

Browse files
committed
Allow the GC to run during ObjectiveC calls.
1 parent ffbbc30 commit 2578dc2

File tree

1 file changed

+57
-10
lines changed

1 file changed

+57
-10
lines changed

src/syntax.jl

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,51 @@ function render_c_arg(io, obj, typ)
121121
end
122122
end
123123

124+
# ensure that the GC can run during a ccall. this is only safe if callbacks
125+
# into Julia transition back to GC-unsafe, which is the case on Julia 1.10+.
126+
#
127+
# doing so is tricky, because no GC operations are allowed after the transition,
128+
# meaning we have to do our own argument conversion instead of relying on ccall.
129+
#
130+
# TODO: replace with JuliaLang/julia#49933 once merged
131+
function make_gcsafe(ex)
132+
# decode the ccall
133+
if !Meta.isexpr(ex, :call) || ex.args[1] != :ccall
134+
error("Can only make ccall expressions GC-safe")
135+
end
136+
target = ex.args[2]
137+
rettyp = ex.args[3]
138+
argtypes = ex.args[4].args
139+
args = ex.args[5:end]
140+
141+
code = quote
142+
end
143+
144+
# assign argument values to variables
145+
vars = [Symbol("arg$i") for i in 1:length(args)]
146+
for (var, arg) in zip(vars, args)
147+
push!(code.args, :($var = $arg))
148+
end
149+
150+
# convert the arguments
151+
converted = [Symbol("converted_arg$i") for i in 1:length(args)]
152+
for (converted, argtyp, var) in zip(converted, argtypes, vars)
153+
push!(code.args, :($converted = Base.unsafe_convert($argtyp, Base.cconvert($argtyp, $var))))
154+
end
155+
156+
# emit a gcsafe ccall
157+
append!(code.args, (quote
158+
GC.@preserve $(vars...) begin
159+
gc_state = ccall(:jl_gc_safe_enter, Int8, ())
160+
ret = ccall($target, $rettyp, ($(argtypes...),), $(converted...))
161+
ccall(:jl_gc_safe_leave, Cvoid, (Int8,), gc_state)
162+
ret
163+
end
164+
end).args)
165+
166+
return code
167+
end
168+
124169
function class_message(class_name, msg, rettyp, argtyps, argvals)
125170
quote
126171
class = Class($(String(class_name)))
@@ -136,18 +181,19 @@ function class_message(class_name, msg, rettyp, argtyps, argvals)
136181
end
137182
ret = $(
138183
if ABI.use_stret(rettyp)
139-
# we follow Julia's ABI implementation, so ccall will handle the sret box
140-
:(
184+
# we follow Julia's ABI implementation,
185+
# so ccall will handle the sret box
186+
make_gcsafe(:(
141187
ccall(:objc_msgSend_stret, $rettyp,
142188
(Ptr{Cvoid}, Ptr{Cvoid}, $(map(esc, argtyps)...)),
143189
class, sel, $(map(esc, argvals)...))
144-
)
190+
))
145191
else
146-
:(
192+
make_gcsafe(:(
147193
ccall(:objc_msgSend, $rettyp,
148194
(Ptr{Cvoid}, Ptr{Cvoid}, $(map(esc, argtyps)...)),
149195
class, sel, $(map(esc, argvals)...))
150-
)
196+
))
151197
end
152198
)
153199
@static if $tracing
@@ -178,18 +224,19 @@ function instance_message(instance, typ, msg, rettyp, argtyps, argvals)
178224
end
179225
ret = $(
180226
if ABI.use_stret(rettyp)
181-
# we follow Julia's ABI implementation, so ccall will handle the sret box
182-
:(
227+
# we follow Julia's ABI implementation,
228+
# so ccall will handle the sret box
229+
make_gcsafe(:(
183230
ccall(:objc_msgSend_stret, $rettyp,
184231
(id{Object}, Ptr{Cvoid}, $(map(esc, argtyps)...)),
185232
$instance, sel, $(map(esc, argvals)...))
186-
)
233+
))
187234
else
188-
:(
235+
make_gcsafe(:(
189236
ccall(:objc_msgSend, $rettyp,
190237
(id{Object}, Ptr{Cvoid}, $(map(esc, argtyps)...)),
191238
$instance, sel, $(map(esc, argvals)...))
192-
)
239+
))
193240
end
194241
)
195242
@static if $tracing

0 commit comments

Comments
 (0)