-
Notifications
You must be signed in to change notification settings - Fork 67
/
Copy pathGC.jl
137 lines (112 loc) · 2.96 KB
/
GC.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""
module PythonCall.GC
Garbage collection of Python objects.
See [`enable`](@ref), [`disable`](@ref) and [`gc`](@ref).
"""
module GC
using ..C: C
const QUEUE = C.PyPtr[]
const QUEUE_LOCK = Threads.SpinLock()
const HOOK = WeakRef()
"""
PythonCall.GC.disable()
Do nothing.
!!! note
Historically this would disable the PythonCall garbage collector. This was required
for safety in multi-threaded code but is no longer needed, so this is now a no-op.
"""
disable() = nothing
"""
PythonCall.GC.enable()
Do nothing.
!!! note
Historically this would enable the PythonCall garbage collector. This was required
for safety in multi-threaded code but is no longer needed, so this is now a no-op.
"""
enable() = nothing
"""
PythonCall.GC.gc()
Free any Python objects waiting to be freed.
These are objects that were finalized from a thread that was not holding the Python
GIL at the time.
Like most PythonCall functions, this must only be called from the main thread (i.e. the
thread currently holding the Python GIL.)
"""
function gc()
if C.CTX.is_initialized
unsafe_free_queue()
end
nothing
end
function unsafe_free_queue()
lock(QUEUE_LOCK)
for ptr in QUEUE
if ptr != C.PyNULL
C.Py_DecRef(ptr)
end
end
empty!(QUEUE)
unlock(QUEUE_LOCK)
nothing
end
function enqueue(ptr::C.PyPtr)
if ptr != C.PyNULL && C.CTX.is_initialized
if C.PyGILState_Check() == 1
C.Py_DecRef(ptr)
if !isempty(QUEUE)
unsafe_free_queue()
end
else
lock(QUEUE_LOCK)
push!(QUEUE, ptr)
unlock(QUEUE_LOCK)
end
end
nothing
end
function enqueue_all(ptrs)
if any(ptr -> ptr != C.PYNULL, ptrs) && C.CTX.is_initialized
if C.PyGILState_Check() == 1
for ptr in ptrs
if ptr != C.PyNULL
C.Py_DecRef(ptr)
end
end
if !isempty(QUEUE)
unsafe_free_queue()
end
else
lock(QUEUE_LOCK)
append!(QUEUE, ptrs)
unlock(QUEUE_LOCK)
end
end
nothing
end
"""
GCHook()
An immortal object which frees any pending Python objects when Julia's GC runs.
This works by creating it but not holding any strong reference to it, so it is eligible
to be finalized by Julia's GC. The finalizer empties the PythonCall GC queue if
possible. The finalizer also re-attaches itself, so the object does not actually get
collected and so the finalizer will run again at next GC.
"""
mutable struct GCHook
function GCHook()
finalizer(_gchook_finalizer, new())
end
end
function _gchook_finalizer(x)
if C.CTX.is_initialized
finalizer(_gchook_finalizer, x)
if !isempty(QUEUE) && C.PyGILState_Check() == 1
unsafe_free_queue()
end
end
nothing
end
function __init__()
HOOK.value = GCHook()
nothing
end
end # module GC