1/*
2 pybind11/gil.h: RAII helpers for managing the GIL
3
4 Copyright (c) 2016 Wenzel Jakob <[email protected]>
5
6 All rights reserved. Use of this source code is governed by a
7 BSD-style license that can be found in the LICENSE file.
8*/
9
10#pragma once
11
12#include "detail/common.h"
13
14#if defined(WITH_THREAD) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
15# include "detail/internals.h"
16#endif
17
18PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
19
20PYBIND11_NAMESPACE_BEGIN(detail)
21
22// forward declarations
23PyThreadState *get_thread_state_unchecked();
24
25PYBIND11_NAMESPACE_END(detail)
26
27#if defined(WITH_THREAD)
28
29# if !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
30
31/* The functions below essentially reproduce the PyGILState_* API using a RAII
32 * pattern, but there are a few important differences:
33 *
34 * 1. When acquiring the GIL from an non-main thread during the finalization
35 * phase, the GILState API blindly terminates the calling thread, which
36 * is often not what is wanted. This API does not do this.
37 *
38 * 2. The gil_scoped_release function can optionally cut the relationship
39 * of a PyThreadState and its associated thread, which allows moving it to
40 * another thread (this is a fairly rare/advanced use case).
41 *
42 * 3. The reference count of an acquired thread state can be controlled. This
43 * can be handy to prevent cases where callbacks issued from an external
44 * thread would otherwise constantly construct and destroy thread state data
45 * structures.
46 *
47 * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an
48 * example which uses features 2 and 3 to migrate the Python thread of
49 * execution to another thread (to run the event loop on the original thread,
50 * in this case).
51 */
52
53class gil_scoped_acquire {
54public:
55 PYBIND11_NOINLINE gil_scoped_acquire() {
56 auto &internals = detail::get_internals();
57 tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
58
59 if (!tstate) {
60 /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if
61 calling from a Python thread). Since we use a different key, this ensures
62 we don't create a new thread state and deadlock in PyEval_AcquireThread
63 below. Note we don't save this state with internals.tstate, since we don't
64 create it we would fail to clear it (its reference count should be > 0). */
65 tstate = PyGILState_GetThisThreadState();
66 }
67
68 if (!tstate) {
69 tstate = PyThreadState_New(internals.istate);
70# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
71 if (!tstate) {
72 pybind11_fail("scoped_acquire: could not create thread state!");
73 }
74# endif
75 tstate->gilstate_counter = 0;
76 PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
77 } else {
78 release = detail::get_thread_state_unchecked() != tstate;
79 }
80
81 if (release) {
82 PyEval_AcquireThread(tstate);
83 }
84
85 inc_ref();
86 }
87
88 gil_scoped_acquire(const gil_scoped_acquire &) = delete;
89 gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
90
91 void inc_ref() { ++tstate->gilstate_counter; }
92
93 PYBIND11_NOINLINE void dec_ref() {
94 --tstate->gilstate_counter;
95# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
96 if (detail::get_thread_state_unchecked() != tstate) {
97 pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
98 }
99 if (tstate->gilstate_counter < 0) {
100 pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
101 }
102# endif
103 if (tstate->gilstate_counter == 0) {
104# if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
105 if (!release) {
106 pybind11_fail("scoped_acquire::dec_ref(): internal error!");
107 }
108# endif
109 PyThreadState_Clear(tstate);
110 if (active) {
111 PyThreadState_DeleteCurrent();
112 }
113 PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate);
114 release = false;
115 }
116 }
117
118 /// This method will disable the PyThreadState_DeleteCurrent call and the
119 /// GIL won't be acquired. This method should be used if the interpreter
120 /// could be shutting down when this is called, as thread deletion is not
121 /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
122 /// protect subsequent code.
123 PYBIND11_NOINLINE void disarm() { active = false; }
124
125 PYBIND11_NOINLINE ~gil_scoped_acquire() {
126 dec_ref();
127 if (release) {
128 PyEval_SaveThread();
129 }
130 }
131
132private:
133 PyThreadState *tstate = nullptr;
134 bool release = true;
135 bool active = true;
136};
137
138class gil_scoped_release {
139public:
140 explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) {
141 // `get_internals()` must be called here unconditionally in order to initialize
142 // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
143 // initialization race could occur as multiple threads try `gil_scoped_acquire`.
144 auto &internals = detail::get_internals();
145 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
146 tstate = PyEval_SaveThread();
147 if (disassoc) {
148 // Python >= 3.7 can remove this, it's an int before 3.7
149 // NOLINTNEXTLINE(readability-qualified-auto)
150 auto key = internals.tstate;
151 PYBIND11_TLS_DELETE_VALUE(key);
152 }
153 }
154
155 gil_scoped_release(const gil_scoped_acquire &) = delete;
156 gil_scoped_release &operator=(const gil_scoped_acquire &) = delete;
157
158 /// This method will disable the PyThreadState_DeleteCurrent call and the
159 /// GIL won't be acquired. This method should be used if the interpreter
160 /// could be shutting down when this is called, as thread deletion is not
161 /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and
162 /// protect subsequent code.
163 PYBIND11_NOINLINE void disarm() { active = false; }
164
165 ~gil_scoped_release() {
166 if (!tstate) {
167 return;
168 }
169 // `PyEval_RestoreThread()` should not be called if runtime is finalizing
170 if (active) {
171 PyEval_RestoreThread(tstate);
172 }
173 if (disassoc) {
174 // Python >= 3.7 can remove this, it's an int before 3.7
175 // NOLINTNEXTLINE(readability-qualified-auto)
176 auto key = detail::get_internals().tstate;
177 PYBIND11_TLS_REPLACE_VALUE(key, tstate);
178 }
179 }
180
181private:
182 PyThreadState *tstate;
183 bool disassoc;
184 bool active = true;
185};
186
187# else // PYBIND11_SIMPLE_GIL_MANAGEMENT
188
189class gil_scoped_acquire {
190 PyGILState_STATE state;
191
192public:
193 gil_scoped_acquire() : state{PyGILState_Ensure()} {}
194 gil_scoped_acquire(const gil_scoped_acquire &) = delete;
195 gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
196 ~gil_scoped_acquire() { PyGILState_Release(state); }
197 void disarm() {}
198};
199
200class gil_scoped_release {
201 PyThreadState *state;
202
203public:
204 gil_scoped_release() : state{PyEval_SaveThread()} {}
205 gil_scoped_release(const gil_scoped_release &) = delete;
206 gil_scoped_release &operator=(const gil_scoped_acquire &) = delete;
207 ~gil_scoped_release() { PyEval_RestoreThread(state); }
208 void disarm() {}
209};
210
211# endif // PYBIND11_SIMPLE_GIL_MANAGEMENT
212
213#else // WITH_THREAD
214
215class gil_scoped_acquire {
216public:
217 gil_scoped_acquire() {
218 // Trick to suppress `unused variable` error messages (at call sites).
219 (void) (this != (this + 1));
220 }
221 gil_scoped_acquire(const gil_scoped_acquire &) = delete;
222 gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete;
223 void disarm() {}
224};
225
226class gil_scoped_release {
227public:
228 gil_scoped_release() {
229 // Trick to suppress `unused variable` error messages (at call sites).
230 (void) (this != (this + 1));
231 }
232 gil_scoped_release(const gil_scoped_release &) = delete;
233 gil_scoped_release &operator=(const gil_scoped_acquire &) = delete;
234 void disarm() {}
235};
236
237#endif // WITH_THREAD
238
239PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
240