|
63 | 63 |
|
64 | 64 | ######################################################################### |
65 | 65 |
|
| 66 | +const _finalized = Ref(false) |
| 67 | +# This flag is set via `Py_AtExit` to avoid calling `pydecref_` after |
| 68 | +# Python is finalized. |
| 69 | + |
| 70 | +function _set_finalized() |
| 71 | + # This function MUST NOT invoke any Python APIs. |
| 72 | + # https://docs.python.org/3/c-api/sys.html#c.Py_AtExit |
| 73 | + _finalized[] = true |
| 74 | + return nothing |
| 75 | +end |
| 76 | + |
| 77 | +function Py_Finalize() |
| 78 | + ccall(@pysym(:Py_Finalize), Cvoid, ()) |
| 79 | +end |
| 80 | + |
66 | 81 | function __init__() |
67 | 82 | # sanity check: in Pkg for Julia 0.7+, the location of Conda can change |
68 | 83 | # if e.g. you checkout Conda master, and we'll need to re-build PyCall |
@@ -155,4 +170,25 @@ function __init__() |
155 | 170 | end |
156 | 171 | end |
157 | 172 | end |
| 173 | + |
| 174 | + # Configure finalization steps. |
| 175 | + # |
| 176 | + # * In julia/PyCall, `julia` needs to call `Py_Finalize` to |
| 177 | + # finalize Python runtime to invoke Python functions registered |
| 178 | + # in Python's exit hook. This is done by Julia's `atexit` exit |
| 179 | + # hook. |
| 180 | + # |
| 181 | + # * In PyJulia, `python` needs to call `jl_atexit_hook` in its |
| 182 | + # exit hook instead. |
| 183 | + # |
| 184 | + # In both cases, it is important to not invoke GC of the finalized |
| 185 | + # runtime. This is ensured by: |
| 186 | + @pycheckz ccall((@pysym :Py_AtExit), Cint, (Ptr{Cvoid},), |
| 187 | + @cfunction(_set_finalized, Cvoid, ())) |
| 188 | + if !already_inited |
| 189 | + # Once `_set_finalized` is successfully registered to |
| 190 | + # `Py_AtExit`, it is safe to call `Py_Finalize` during |
| 191 | + # finalization of this Julia process. |
| 192 | + atexit(Py_Finalize) |
| 193 | + end |
158 | 194 | end |
0 commit comments