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 
14 #if defined(WITH_THREAD) && !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
15 # include "detail/internals.h"
16 #endif
17 
19 
21 
22 // forward declarations
23 PyThreadState *get_thread_state_unchecked();
24 
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 
53 class gil_scoped_acquire {
54 public:
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 {
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;
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  }
114  release = false;
115  }
116  }
117 
123  PYBIND11_NOINLINE void disarm() { active = false; }
124 
126  dec_ref();
127  if (release) {
128  PyEval_SaveThread();
129  }
130  }
131 
132 private:
133  PyThreadState *tstate = nullptr;
134  bool release = true;
135  bool active = true;
136 };
137 
138 class gil_scoped_release {
139 public:
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`.
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;
152  }
153  }
154 
155  gil_scoped_release(const gil_scoped_release &) = delete;
157 
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;
178  }
179  }
180 
181 private:
182  PyThreadState *tstate;
183  bool disassoc;
184  bool active = true;
185 };
186 
187 # else // PYBIND11_SIMPLE_GIL_MANAGEMENT
188 
189 class gil_scoped_acquire {
190  PyGILState_STATE state;
191 
192 public:
193  gil_scoped_acquire() : state{PyGILState_Ensure()} {}
194  gil_scoped_acquire(const gil_scoped_acquire &) = delete;
196  ~gil_scoped_acquire() { PyGILState_Release(state); }
197  void disarm() {}
198 };
199 
200 class gil_scoped_release {
201  PyThreadState *state;
202 
203 public:
204  gil_scoped_release() : state{PyEval_SaveThread()} {}
205  gil_scoped_release(const gil_scoped_release &) = 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 
216 public:
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;
223  void disarm() {}
224 };
225 
227 public:
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;
234  void disarm() {}
235 };
236 
237 #endif // WITH_THREAD
238 
get_thread_state_unchecked
PyThreadState * get_thread_state_unchecked()
Definition: type_caster_base.h:486
internals
Definition: internals.h:168
gil_scoped_release::disarm
void disarm()
Definition: gil.h:234
gil_scoped_acquire::operator=
gil_scoped_acquire & operator=(const gil_scoped_acquire &)=delete
PYBIND11_NAMESPACE_END
#define PYBIND11_NAMESPACE_END(name)
Definition: wrap/pybind11/include/pybind11/detail/common.h:80
internals.h
PYBIND11_NOINLINE
#define PYBIND11_NOINLINE
Definition: wrap/pybind11/include/pybind11/detail/common.h:186
detail
Definition: testSerializationNonlinear.cpp:70
PYBIND11_TLS_GET_VALUE
#define PYBIND11_TLS_GET_VALUE(key)
Definition: internals.h:101
get_internals
PYBIND11_NOINLINE internals & get_internals()
Return a reference to the current internals data.
Definition: internals.h:467
PYBIND11_NAMESPACE_BEGIN
#define PYBIND11_NAMESPACE_BEGIN(name)
Definition: wrap/pybind11/include/pybind11/detail/common.h:76
gil_scoped_release
Definition: gil.h:226
PYBIND11_TLS_REPLACE_VALUE
#define PYBIND11_TLS_REPLACE_VALUE(key, value)
Definition: internals.h:115
conf.release
release
Definition: gtsam/3rdparty/GeographicLib/python/doc/conf.py:69
gil_scoped_release::operator=
gil_scoped_release & operator=(const gil_scoped_release &)=delete
gil_scoped_acquire::gil_scoped_acquire
gil_scoped_acquire()
Definition: gil.h:217
PYBIND11_NAMESPACE
Definition: test_custom_type_casters.cpp:24
common.h
gil_scoped_acquire
Definition: gil.h:215
pybind11_fail
PyExc_RuntimeError PYBIND11_NOINLINE void pybind11_fail(const char *reason)
Used internally.
Definition: wrap/pybind11/include/pybind11/detail/common.h:1013
key
const gtsam::Symbol key('X', 0)
gil_scoped_release::gil_scoped_release
gil_scoped_release()
Definition: gil.h:228
PYBIND11_TLS_DELETE_VALUE
#define PYBIND11_TLS_DELETE_VALUE(key)
Definition: internals.h:114
gil_scoped_acquire::disarm
void disarm()
Definition: gil.h:223
make_changelog.state
state
Definition: make_changelog.py:28


gtsam
Author(s):
autogenerated on Thu Jun 13 2024 03:02:28