μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
gil.h
Go to the documentation of this file.
1/*
2 pybind11/gil.h: RAII helpers for managing the GIL
3
4 Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
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#include "detail/internals.h"
14
16
18
19// forward declarations
20PyThreadState *get_thread_state_unchecked();
21
23
24#if defined(WITH_THREAD) && !defined(PYPY_VERSION)
25
26/* The functions below essentially reproduce the PyGILState_* API using a RAII
27 * pattern, but there are a few important differences:
28 *
29 * 1. When acquiring the GIL from an non-main thread during the finalization
30 * phase, the GILState API blindly terminates the calling thread, which
31 * is often not what is wanted. This API does not do this.
32 *
33 * 2. The gil_scoped_release function can optionally cut the relationship
34 * of a PyThreadState and its associated thread, which allows moving it to
35 * another thread (this is a fairly rare/advanced use case).
36 *
37 * 3. The reference count of an acquired thread state can be controlled. This
38 * can be handy to prevent cases where callbacks issued from an external
39 * thread would otherwise constantly construct and destroy thread state data
40 * structures.
41 *
42 * See the Python bindings of NanoGUI (http://github.com/wjakob/nanogui) for an
43 * example which uses features 2 and 3 to migrate the Python thread of
44 * execution to another thread (to run the event loop on the original thread,
45 * in this case).
46 */
47
49public:
51 auto &internals = detail::get_internals();
52 tstate = (PyThreadState *) PYBIND11_TLS_GET_VALUE(internals.tstate);
53
54 if (!tstate) {
55 /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if
56 calling from a Python thread). Since we use a different key, this ensures
57 we don't create a new thread state and deadlock in PyEval_AcquireThread
58 below. Note we don't save this state with internals.tstate, since we don't
59 create it we would fail to clear it (its reference count should be > 0). */
60 tstate = PyGILState_GetThisThreadState();
61 }
62
63 if (!tstate) {
64 tstate = PyThreadState_New(internals.istate);
65# if !defined(NDEBUG)
66 if (!tstate) {
67 pybind11_fail("scoped_acquire: could not create thread state!");
68 }
69# endif
70 tstate->gilstate_counter = 0;
72 } else {
73 release = detail::get_thread_state_unchecked() != tstate;
74 }
75
76 if (release) {
77 PyEval_AcquireThread(tstate);
78 }
79
80 inc_ref();
81 }
82
83 void inc_ref() { ++tstate->gilstate_counter; }
84
85 PYBIND11_NOINLINE void dec_ref() {
86 --tstate->gilstate_counter;
87# if !defined(NDEBUG)
88 if (detail::get_thread_state_unchecked() != tstate) {
89 pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
90 }
91 if (tstate->gilstate_counter < 0) {
92 pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
93 }
94# endif
95 if (tstate->gilstate_counter == 0) {
96# if !defined(NDEBUG)
97 if (!release) {
98 pybind11_fail("scoped_acquire::dec_ref(): internal error!");
99 }
100# endif
101 PyThreadState_Clear(tstate);
102 if (active) {
103 PyThreadState_DeleteCurrent();
104 }
105 PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate);
106 release = false;
107 }
108 }
109
115 PYBIND11_NOINLINE void disarm() { active = false; }
116
118 dec_ref();
119 if (release) {
120 PyEval_SaveThread();
121 }
122 }
123
124private:
125 PyThreadState *tstate = nullptr;
126 bool release = true;
127 bool active = true;
128};
129
130class gil_scoped_release {
131public:
132 explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) {
133 // `get_internals()` must be called here unconditionally in order to initialize
134 // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
135 // initialization race could occur as multiple threads try `gil_scoped_acquire`.
136 auto &internals = detail::get_internals();
137 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
138 tstate = PyEval_SaveThread();
139 if (disassoc) {
140 // Python >= 3.7 can remove this, it's an int before 3.7
141 // NOLINTNEXTLINE(readability-qualified-auto)
142 auto key = internals.tstate;
144 }
145 }
146
152 PYBIND11_NOINLINE void disarm() { active = false; }
153
155 if (!tstate) {
156 return;
157 }
158 // `PyEval_RestoreThread()` should not be called if runtime is finalizing
159 if (active) {
160 PyEval_RestoreThread(tstate);
161 }
162 if (disassoc) {
163 // Python >= 3.7 can remove this, it's an int before 3.7
164 // NOLINTNEXTLINE(readability-qualified-auto)
165 auto key = detail::get_internals().tstate;
166 PYBIND11_TLS_REPLACE_VALUE(key, tstate);
167 }
168 }
169
170private:
171 PyThreadState *tstate;
172 bool disassoc;
173 bool active = true;
174};
175#elif defined(PYPY_VERSION)
176class gil_scoped_acquire {
177 PyGILState_STATE state;
178
179public:
180 gil_scoped_acquire() { state = PyGILState_Ensure(); }
181 ~gil_scoped_acquire() { PyGILState_Release(state); }
182 void disarm() {}
183};
184
185class gil_scoped_release {
186 PyThreadState *state;
187
188public:
189 gil_scoped_release() { state = PyEval_SaveThread(); }
190 ~gil_scoped_release() { PyEval_RestoreThread(state); }
191 void disarm() {}
192};
193#else
195 void disarm() {}
196};
198 void disarm() {}
199};
200#endif
201
gil_scoped_acquire()
Definition: gil.h:217
void disarm()
Definition: gil.h:195
gil_scoped_release()
Definition: gil.h:228
void disarm()
Definition: gil.h:198
PYBIND11_NOINLINE void pybind11_fail(const char *reason)
Thrown when pybind11::cast or.
Definition: common.h:992
#define PYBIND11_NOINLINE
Definition: common.h:129
#define PYBIND11_NAMESPACE_END(name)
Definition: common.h:21
#define PYBIND11_NAMESPACE_BEGIN(name)
Definition: common.h:20
#define PYBIND11_TLS_GET_VALUE(key)
Definition: internals.h:84
#define PYBIND11_TLS_DELETE_VALUE(key)
Definition: internals.h:93
#define PYBIND11_TLS_REPLACE_VALUE(key, value)
Definition: internals.h:94
PyThreadState * get_thread_state_unchecked()
Internal data structure used to track registered instances and types.
Definition: internals.h:150