@@ -268,7 +268,31 @@ The above input outputs this:
268268
269269 (:printf, :Cvoid, [:Cstring, :Cuint], ["%d", :value])
270270"""
271- function ccall_macro_parse (expr:: Expr )
271+ function ccall_macro_parse (exprs)
272+ gc_safe = false
273+ expr = nothing
274+ if exprs isa Expr
275+ expr = exprs
276+ elseif length (exprs) == 1
277+ expr = exprs[1 ]
278+ elseif length (exprs) == 2
279+ gc_expr = exprs[1 ]
280+ expr = exprs[2 ]
281+ if gc_expr. head == :(= ) && gc_expr. args[1 ] == :gc_safe
282+ if gc_expr. args[2 ] == true
283+ gc_safe = true
284+ elseif gc_expr. args[2 ] == false
285+ gc_safe = false
286+ else
287+ throw (ArgumentError (" gc_safe must be true or false" ))
288+ end
289+ else
290+ throw (ArgumentError (" @ccall option must be `gc_safe=true` or `gc_safe=false`" ))
291+ end
292+ else
293+ throw (ArgumentError (" @ccall needs a function signature with a return type" ))
294+ end
295+
272296 # setup and check for errors
273297 if ! isexpr (expr, :(:: ))
274298 throw (ArgumentError (" @ccall needs a function signature with a return type" ))
@@ -328,12 +352,11 @@ function ccall_macro_parse(expr::Expr)
328352 pusharg! (a)
329353 end
330354 end
331-
332- return func, rettype, types, args, nreq
355+ return func, rettype, types, args, gc_safe, nreq
333356end
334357
335358
336- function ccall_macro_lower (convention, func, rettype, types, args, nreq)
359+ function ccall_macro_lower (convention, func, rettype, types, args, gc_safe, nreq)
337360 statements = []
338361
339362 # if interpolation was used, ensure the value is a function pointer at runtime.
@@ -351,9 +374,15 @@ function ccall_macro_lower(convention, func, rettype, types, args, nreq)
351374 else
352375 func = esc (func)
353376 end
377+ cconv = nothing
378+ if convention isa Tuple
379+ cconv = Expr (:cconv , (convention... , gc_safe), nreq)
380+ else
381+ cconv = Expr (:cconv , (convention, UInt16 (0 ), gc_safe), nreq)
382+ end
354383
355384 return Expr (:block , statements... ,
356- Expr (:call , :ccall , func, Expr ( : cconv, convention, nreq) , esc (rettype),
385+ Expr (:call , :ccall , func, cconv, esc (rettype),
357386 Expr (:tuple , map (esc, types)... ), map (esc, args)... ))
358387end
359388
@@ -404,9 +433,16 @@ Example using an external library:
404433
405434The string literal could also be used directly before the function
406435name, if desired `"libglib-2.0".g_uri_escape_string(...`
436+
437+ It's possible to declare the ccall as `gc_safe` by using the `gc_safe = true` option:
438+ @ccall gc_safe=true strlen(s::Cstring)::Csize_t
439+ This allows the garbage collector to run concurrently with the ccall, which can be useful whenever
440+ the `ccall` may block outside of julia.
441+ WARNING: This option should be used with caution, as it can lead to undefined behavior if the ccall
442+ calls back into the julia runtime. (`@cfunction`/`@ccallables` are safe however)
407443"""
408- macro ccall (expr )
409- return ccall_macro_lower (:ccall , ccall_macro_parse (expr )... )
444+ macro ccall (exprs ... )
445+ return ccall_macro_lower (( :ccall ) , ccall_macro_parse (exprs )... )
410446end
411447
412448macro ccall_effects (effects:: UInt16 , expr)
0 commit comments