-
Notifications
You must be signed in to change notification settings - Fork 78
Expand file tree
/
Copy pathGIL.jl
More file actions
137 lines (113 loc) · 3.3 KB
/
GIL.jl
File metadata and controls
137 lines (113 loc) · 3.3 KB
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.GIL
Handling the Python Global Interpreter Lock.
See [`lock`](@ref), [`@lock`](@ref), [`unlock`](@ref) and [`@unlock`](@ref).
"""
module GIL
using ..C: C
# Ensure that only one Julia thread tries to acquire the Python GIL
# PyGILState_Ensure and PyGILState_Release may not be thread safe.
# https://github.com/JuliaPy/PythonCall.jl/issues/627
const _jl_gil_lock = ReentrantLock()
"""
hasgil()
Returns `true` if the current thread has the GIL or `false` otherwise.
"""
hasgil() = C.PyGILState_Check() == Cint(1)
"""
lock(f)
Lock the GIL, compute `f()`, unlock the GIL, then return the result of `f()`.
Use this to run Python code from threads that do not currently hold the GIL, such as new
threads. Since the main Julia thread holds the GIL by default, you will need to
[`unlock`](@ref) the GIL before using this function.
See [`@lock`](@ref) for the macro form.
"""
function lock(f)
Base.lock(_jl_gil_lock)
try
state = C.PyGILState_Ensure()
try
f()
finally
C.PyGILState_Release(state)
end
finally
Base.unlock(_jl_gil_lock)
end
end
"""
@lock expr
Lock the GIL, compute `expr`, unlock the GIL, then return the result of `expr`.
Use this to run Python code from threads that do not currently hold the GIL, such as new
threads. Since the main Julia thread holds the GIL by default, you will need to
[`@unlock`](@ref) the GIL before using this function.
The macro equivalent of [`lock`](@ref).
"""
macro lock(expr)
quote
Base.lock(_jl_gil_lock)
try
state = C.PyGILState_Ensure()
try
$(esc(expr))
finally
C.PyGILState_Release(state)
end
finally
Base.unlock(_jl_gil_lock)
end
end
end
"""
unlock(f)
Unlock the GIL, compute `f()`, re-lock the GIL, then return the result of `f()`.
Use this to run non-Python code with the GIL unlocked, so allowing another thread to run
Python code. That other thread can be a Julia thread, which must lock the GIL using
[`lock`](@ref).
See [`@unlock`](@ref) for the macro form.
"""
function unlock(f)
_locked = Base.islocked(_jl_gil_lock)
_locked && Base.unlock(_jl_gil_lock)
try
state = C.PyEval_SaveThread()
try
f()
finally
C.PyEval_RestoreThread(state)
end
finally
_locked && Base.lock(_jl_gil_lock)
end
end
"""
@unlock expr
Unlock the GIL, compute `expr`, re-lock the GIL, then return the result of `expr`.
Use this to run non-Python code with the GIL unlocked, so allowing another thread to run
Python code. That other thread can be a Julia thread, which must lock the GIL using
[`@lock`](@ref).
The macro equivalent of [`unlock`](@ref).
"""
macro unlock(expr)
quote
_locked = Base.islocked(_jl_gil_lock)
_locked && Base.unlock(_jl_gil_lock)
try
state = C.PyEval_SaveThread()
try
$(esc(expr))
finally
C.PyEval_RestoreThread(state)
end
finally
_locked && Base.lock(_jl_gil_lock)
end
end
end
# If the main thread already has the GIL, we should lock _jl_gil_lock.
function __init__()
if hasgil()
Base.lock(_jl_gil_lock)
end
end
end