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
20 PyThreadState *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 
48 class gil_scoped_acquire {
49 public:
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(PYBIND11_DETAILED_ERROR_MESSAGES)
66  if (!tstate) {
67  pybind11_fail("scoped_acquire: could not create thread state!");
68  }
69 # endif
70  tstate->gilstate_counter = 0;
71  PYBIND11_TLS_REPLACE_VALUE(internals.tstate, tstate);
72  } else {
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(PYBIND11_DETAILED_ERROR_MESSAGES)
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(PYBIND11_DETAILED_ERROR_MESSAGES)
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  }
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 
124 private:
125  PyThreadState *tstate = nullptr;
126  bool release = true;
127  bool active = true;
128 };
129 
130 class gil_scoped_release {
131 public:
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`.
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 
154  ~gil_scoped_release() {
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;
167  }
168  }
169 
170 private:
171  PyThreadState *tstate;
172  bool disassoc;
173  bool active = true;
174 };
175 #elif defined(PYPY_VERSION)
176 class gil_scoped_acquire {
177  PyGILState_STATE state;
178 
179 public:
180  gil_scoped_acquire() { state = PyGILState_Ensure(); }
181  ~gil_scoped_acquire() { PyGILState_Release(state); }
182  void disarm() {}
183 };
184 
185 class gil_scoped_release {
186  PyThreadState *state;
187 
188 public:
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 
const gtsam::Symbol key('X', 0)
PYBIND11_NOINLINE internals & get_internals()
Return a reference to the current internals data.
Definition: internals.h:405
PyExc_RuntimeError [[noreturn]] PYBIND11_NOINLINE void pybind11_fail(const char *reason)
Used internally.
void disarm()
Definition: gil.h:198
#define PYBIND11_TLS_DELETE_VALUE(key)
Definition: internals.h:97
PyThreadState * get_thread_state_unchecked()
#define PYBIND11_TLS_REPLACE_VALUE(key, value)
Definition: internals.h:98
void disarm()
Definition: gil.h:195
#define PYBIND11_TLS_GET_VALUE(key)
Definition: internals.h:84
#define PYBIND11_NAMESPACE_END(name)
#define PYBIND11_NAMESPACE_BEGIN(name)


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:34:18