|
1 | 1 | # utilities for calling foreign functionality more conveniently
|
2 | 2 |
|
3 |
| -export @checked, with_workspace, with_workspaces, |
4 |
| - @debug_ccall, @gcsafe_ccall |
5 |
| - |
6 |
| - |
7 |
| -## function wrapper for checking the return value of a function |
8 |
| - |
9 |
| -""" |
10 |
| - @checked function foo(...) |
11 |
| - rv = ... |
12 |
| - return rv |
13 |
| - end |
14 |
| -
|
15 |
| -Macro for wrapping a function definition returning a status code. Two versions of the |
16 |
| -function will be generated: `foo`, with the function execution wrapped by an invocation of |
17 |
| -the `check` function (to be implemented by the caller of this macro), and `unchecked_foo` |
18 |
| -where no such invocation is present and the status code is returned to the caller. |
19 |
| -""" |
20 |
| -macro checked(ex) |
21 |
| - # parse the function definition |
22 |
| - @assert Meta.isexpr(ex, :function) |
23 |
| - sig = ex.args[1] |
24 |
| - @assert Meta.isexpr(sig, :call) |
25 |
| - body = ex.args[2] |
26 |
| - @assert Meta.isexpr(body, :block) |
27 |
| - |
28 |
| - # make sure these functions are inlined |
29 |
| - pushfirst!(body.args, Expr(:meta, :inline)) |
30 |
| - |
31 |
| - # generate a "safe" version that performs a check |
32 |
| - safe_body = quote |
33 |
| - @inline |
34 |
| - check() do |
35 |
| - $body |
36 |
| - end |
37 |
| - end |
38 |
| - safe_sig = Expr(:call, sig.args[1], sig.args[2:end]...) |
39 |
| - safe_def = Expr(:function, safe_sig, safe_body) |
40 |
| - |
41 |
| - # generate a "unchecked" version that returns the error code instead |
42 |
| - unchecked_sig = Expr(:call, Symbol("unchecked_", sig.args[1]), sig.args[2:end]...) |
43 |
| - unchecked_def = Expr(:function, unchecked_sig, body) |
44 |
| - |
45 |
| - return esc(:($safe_def, $unchecked_def)) |
46 |
| -end |
47 |
| - |
| 3 | +export with_workspace, with_workspaces |
48 | 4 |
|
49 | 5 | ## wrapper for foreign functionality that requires a workspace buffer
|
50 | 6 |
|
@@ -138,99 +94,3 @@ function with_workspaces(f::Base.Callable,
|
138 | 94 | end
|
139 | 95 | end
|
140 | 96 | end
|
141 |
| - |
142 |
| - |
143 |
| -## version of ccall that prints the ccall, its arguments and its return value |
144 |
| - |
145 |
| -macro debug_ccall(ex) |
146 |
| - @assert Meta.isexpr(ex, :(::)) |
147 |
| - call, ret = ex.args |
148 |
| - @assert Meta.isexpr(call, :call) |
149 |
| - target, argexprs... = call.args |
150 |
| - args = map(argexprs) do argexpr |
151 |
| - @assert Meta.isexpr(argexpr, :(::)) |
152 |
| - argexpr.args[1] |
153 |
| - end |
154 |
| - |
155 |
| - ex = Expr(:macrocall, Symbol("@ccall"), __source__, ex) |
156 |
| - |
157 |
| - # avoid task switches |
158 |
| - io = :(Core.stdout) |
159 |
| - |
160 |
| - quote |
161 |
| - print($io, $(string(target)), '(') |
162 |
| - for (i, arg) in enumerate(($(map(esc, args)...),)) |
163 |
| - i > 1 && print($io, ", ") |
164 |
| - render_arg($io, arg) |
165 |
| - end |
166 |
| - print($io, ')') |
167 |
| - |
168 |
| - rv = $(esc(ex)) |
169 |
| - |
170 |
| - println($io, " = ", rv) |
171 |
| - for (i, arg) in enumerate(($(map(esc, args)...),)) |
172 |
| - if arg isa Base.RefValue |
173 |
| - println($io, " $i: ", arg[]) |
174 |
| - end |
175 |
| - end |
176 |
| - rv |
177 |
| - end |
178 |
| -end |
179 |
| - |
180 |
| -render_arg(io, arg) = print(io, arg) |
181 |
| -render_arg(io, arg::AbstractArray) = summary(io, arg) |
182 |
| -render_arg(io, arg::Base.RefValue{T}) where {T} = print(io, "Ref{", T, "}") |
183 |
| - |
184 |
| - |
185 |
| -## version of ccall that calls jl_gc_safe_enter|leave around the inner ccall |
186 |
| - |
187 |
| -# TODO: replace with JuliaLang/julia#49933 once merged |
188 |
| - |
189 |
| -function ccall_macro_lower(func, rettype, types, args, nreq) |
190 |
| - # instead of re-using ccall or Expr(:foreigncall) to perform argument conversion, |
191 |
| - # we need to do so ourselves in order to insert a jl_gc_safe_enter|leave |
192 |
| - # just around the inner ccall |
193 |
| - |
194 |
| - cconvert_exprs = [] |
195 |
| - cconvert_args = [] |
196 |
| - for (typ, arg) in zip(types, args) |
197 |
| - var = gensym("$(func)_cconvert") |
198 |
| - push!(cconvert_args, var) |
199 |
| - push!(cconvert_exprs, :($var = Base.cconvert($(esc(typ)), $(esc(arg))))) |
200 |
| - end |
201 |
| - |
202 |
| - unsafe_convert_exprs = [] |
203 |
| - unsafe_convert_args = [] |
204 |
| - for (typ, arg) in zip(types, cconvert_args) |
205 |
| - var = gensym("$(func)_unsafe_convert") |
206 |
| - push!(unsafe_convert_args, var) |
207 |
| - push!(unsafe_convert_exprs, :($var = Base.unsafe_convert($(esc(typ)), $arg))) |
208 |
| - end |
209 |
| - |
210 |
| - call = quote |
211 |
| - $(unsafe_convert_exprs...) |
212 |
| - |
213 |
| - gc_state = @ccall(jl_gc_safe_enter()::Int8) |
214 |
| - ret = ccall($(esc(func)), $(esc(rettype)), $(Expr(:tuple, map(esc, types)...)), |
215 |
| - $(unsafe_convert_args...)) |
216 |
| - @ccall(jl_gc_safe_leave(gc_state::Int8)::Cvoid) |
217 |
| - ret |
218 |
| - end |
219 |
| - |
220 |
| - quote |
221 |
| - @inline |
222 |
| - $(cconvert_exprs...) |
223 |
| - GC.@preserve $(cconvert_args...) $(call) |
224 |
| - end |
225 |
| -end |
226 |
| - |
227 |
| -""" |
228 |
| - @gcsafe_ccall ... |
229 |
| -
|
230 |
| -Call a foreign function just like `@ccall`, but marking it safe for the GC to run. This is |
231 |
| -useful for functions that may block, so that the GC isn't blocked from running, but may also |
232 |
| -be required to prevent deadlocks (see JuliaGPU/CUDA.jl#2261). |
233 |
| -""" |
234 |
| -macro gcsafe_ccall(expr) |
235 |
| - ccall_macro_lower(Base.ccall_macro_parse(expr)...) |
236 |
| -end |
0 commit comments