3
3
4
4
Garbage collection of Python objects.
5
5
6
- See ` disable` and `enable` .
6
+ See [`enable`](@ref), [` disable`](@ref) and [`gc`](@ref) .
7
7
"""
8
8
module GC
9
9
10
10
using .. C: C
11
11
12
- const ENABLED = Ref ( true )
13
- const QUEUE = C . PyPtr[]
12
+ const QUEUE = Channel {C.PyPtr} ( Inf )
13
+ const HOOK = WeakRef ()
14
14
15
15
"""
16
16
PythonCall.GC.disable()
17
17
18
- Disable the PythonCall garbage collector .
18
+ Do nothing .
19
19
20
- This means that whenever a Python object owned by Julia is finalized, it is not immediately
21
- freed but is instead added to a queue of objects to free later when `enable()` is called.
20
+ !!! note
22
21
23
- Like most PythonCall functions, you must only call this from the main thread.
22
+ Historically this would disable the PythonCall garbage collector. This was required
23
+ for safety in multi-threaded code but is no longer needed, so this is now a no-op.
24
24
"""
25
- function disable ()
26
- ENABLED[] = false
27
- return
28
- end
25
+ disable () = nothing
29
26
30
27
"""
31
28
PythonCall.GC.enable()
32
29
33
- Re-enable the PythonCall garbage collector .
30
+ Do nothing .
34
31
35
- This frees any Python objects which were finalized while the GC was disabled, and allows
36
- objects finalized in the future to be freed immediately.
32
+ !!! note
37
33
38
- Like most PythonCall functions, you must only call this from the main thread.
34
+ Historically this would enable the PythonCall garbage collector. This was required
35
+ for safety in multi-threaded code but is no longer needed, so this is now a no-op.
39
36
"""
40
- function enable ()
41
- ENABLED[] = true
42
- if ! isempty (QUEUE) && C. PyGILState_Check () == 1
43
- free_queue ()
44
- end
45
- return
46
- end
37
+ enable () = nothing
47
38
48
- function free_queue ()
49
- for ptr in QUEUE
50
- if ptr != C. PyNULL
51
- C. Py_DecRef (ptr)
52
- end
39
+ """
40
+ PythonCall.GC.gc()
41
+
42
+ Free any Python objects waiting to be freed.
43
+
44
+ These are objects that were finalized from a thread that was not holding the Python
45
+ GIL at the time.
46
+
47
+ Like most PythonCall functions, this must only be called from the main thread (i.e. the
48
+ thread currently holding the Python GIL.)
49
+ """
50
+ function gc ()
51
+ if C. CTX. is_initialized
52
+ unsafe_free_queue ()
53
53
end
54
- empty! (QUEUE)
55
54
nothing
56
55
end
57
56
58
- function gc ()
59
- if ENABLED[] && C. PyGILState_Check () == 1
60
- free_queue ()
61
- true
62
- else
63
- false
57
+ function unsafe_free_queue ()
58
+ if isready (QUEUE)
59
+ @lock QUEUE while isready (QUEUE)
60
+ ptr = take! (QUEUE)
61
+ if ptr != C. PyNULL
62
+ C. Py_DecRef (ptr)
63
+ end
64
+ end
64
65
end
66
+ nothing
65
67
end
66
68
67
69
function enqueue (ptr:: C.PyPtr )
68
70
if ptr != C. PyNULL && C. CTX. is_initialized
69
- if ENABLED[] && C. PyGILState_Check () == 1
71
+ if C. PyGILState_Check () == 1
70
72
C. Py_DecRef (ptr)
71
- isempty (QUEUE) || free_queue ()
73
+ unsafe_free_queue ()
72
74
else
73
- push ! (QUEUE, ptr)
75
+ put ! (QUEUE, ptr)
74
76
end
75
77
end
76
- return
78
+ nothing
77
79
end
78
80
79
81
function enqueue_all (ptrs)
80
- if C. CTX. is_initialized
81
- if ENABLED[] && C. PyGILState_Check () == 1
82
+ if any (ptr -> ptr != C . PYNULL, ptrs) && C. CTX. is_initialized
83
+ if C. PyGILState_Check () == 1
82
84
for ptr in ptrs
83
85
if ptr != C. PyNULL
84
86
C. Py_DecRef (ptr)
85
87
end
86
88
end
87
- isempty (QUEUE) || free_queue ()
89
+ unsafe_free_queue ()
88
90
else
89
- append! (QUEUE, ptrs)
91
+ for ptr in ptrs
92
+ put! (QUEUE, ptr)
93
+ end
90
94
end
91
95
end
92
- return
96
+ nothing
93
97
end
94
98
95
99
mutable struct GCHook
@@ -99,13 +103,17 @@ mutable struct GCHook
99
103
end
100
104
101
105
function _gchook_finalizer (x)
102
- gc ()
103
- finalizer (_gchook_finalizer, x)
106
+ if C. CTX. is_initialized
107
+ finalizer (_gchook_finalizer, x)
108
+ if isready (QUEUE) && C. PyGILState_Check () == 1
109
+ unsafe_free_queue ()
110
+ end
111
+ end
104
112
nothing
105
113
end
106
114
107
115
function __init__ ()
108
- GCHook ()
116
+ HOOK . value = GCHook ()
109
117
nothing
110
118
end
111
119
0 commit comments