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 #include <cassert>
15 
16 #if !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT)
17 # include "detail/internals.h"
18 #endif
19 
21 
23 
24 // forward declarations
25 PyThreadState *get_thread_state_unchecked();
26 
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 
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;
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 
94  --tstate->gilstate_counter;
95 # if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
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 
139 public:
140  // PRECONDITION: The GIL must be held when this constructor is called.
141  explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) {
142  assert(PyGILState_Check());
143  // `get_internals()` must be called here unconditionally in order to initialize
144  // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
145  // initialization race could occur as multiple threads try `gil_scoped_acquire`.
147  // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
148  tstate = PyEval_SaveThread();
149  if (disassoc) {
150  // Python >= 3.7 can remove this, it's an int before 3.7
151  // NOLINTNEXTLINE(readability-qualified-auto)
152  auto key = internals.tstate;
154  }
155  }
156 
157  gil_scoped_release(const gil_scoped_release &) = delete;
159 
165  PYBIND11_NOINLINE void disarm() { active = false; }
166 
168  if (!tstate) {
169  return;
170  }
171  // `PyEval_RestoreThread()` should not be called if runtime is finalizing
172  if (active) {
173  PyEval_RestoreThread(tstate);
174  }
175  if (disassoc) {
176  // Python >= 3.7 can remove this, it's an int before 3.7
177  // NOLINTNEXTLINE(readability-qualified-auto)
178  auto key = detail::get_internals().tstate;
180  }
181  }
182 
183 private:
184  PyThreadState *tstate;
185  bool disassoc;
186  bool active = true;
187 };
188 
189 #else // PYBIND11_SIMPLE_GIL_MANAGEMENT
190 
191 class gil_scoped_acquire {
192  PyGILState_STATE state;
193 
194 public:
195  gil_scoped_acquire() : state{PyGILState_Ensure()} {}
196  gil_scoped_acquire(const gil_scoped_acquire &) = delete;
198  ~gil_scoped_acquire() { PyGILState_Release(state); }
199  void disarm() {}
200 };
201 
202 class gil_scoped_release {
203  PyThreadState *state;
204 
205 public:
206  // PRECONDITION: The GIL must be held when this constructor is called.
208  assert(PyGILState_Check());
209  state = PyEval_SaveThread();
210  }
211  gil_scoped_release(const gil_scoped_release &) = delete;
213  ~gil_scoped_release() { PyEval_RestoreThread(state); }
214  void disarm() {}
215 };
216 
217 #endif // PYBIND11_SIMPLE_GIL_MANAGEMENT
218 
get_thread_state_unchecked
PyThreadState * get_thread_state_unchecked()
Definition: type_caster_base.h:517
gil_scoped_release::active
bool active
Definition: gil.h:186
internals
Definition: internals.h:162
gil_scoped_acquire::tstate
PyThreadState * tstate
Definition: gil.h:133
gil_scoped_acquire::dec_ref
PYBIND11_NOINLINE void dec_ref()
Definition: gil.h:93
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:194
gil_scoped_acquire::active
bool active
Definition: gil.h:135
detail
Definition: testSerializationNonlinear.cpp:70
PYBIND11_TLS_GET_VALUE
#define PYBIND11_TLS_GET_VALUE(key)
Definition: internals.h:98
get_internals
PYBIND11_NOINLINE internals & get_internals()
Return a reference to the current internals data.
Definition: internals.h:483
PYBIND11_NAMESPACE_BEGIN
#define PYBIND11_NAMESPACE_BEGIN(name)
Definition: wrap/pybind11/include/pybind11/detail/common.h:76
gil_scoped_acquire::~gil_scoped_acquire
PYBIND11_NOINLINE ~gil_scoped_acquire()
Definition: gil.h:125
gil_scoped_release
Definition: gil.h:138
PYBIND11_TLS_REPLACE_VALUE
#define PYBIND11_TLS_REPLACE_VALUE(key, value)
Definition: internals.h:99
gil_scoped_release::operator=
gil_scoped_release & operator=(const gil_scoped_release &)=delete
gil_scoped_release::~gil_scoped_release
~gil_scoped_release()
Definition: gil.h:167
PYBIND11_NAMESPACE
Definition: test_custom_type_casters.cpp:24
common.h
gil_scoped_acquire
Definition: gil.h:53
pybind11_fail
PyExc_RuntimeError PYBIND11_NOINLINE void pybind11_fail(const char *reason)
Used internally.
Definition: wrap/pybind11/include/pybind11/detail/common.h:1026
internals::istate
PyInterpreterState * istate
Definition: internals.h:197
gil_scoped_release::tstate
PyThreadState * tstate
Definition: gil.h:184
gil_scoped_release::gil_scoped_release
gil_scoped_release(bool disassoc=false)
Definition: gil.h:141
key
const gtsam::Symbol key('X', 0)
gil_scoped_acquire::gil_scoped_acquire
PYBIND11_NOINLINE gil_scoped_acquire()
Definition: gil.h:55
gil_scoped_acquire::release
bool release
Definition: gil.h:134
gil_scoped_acquire::inc_ref
void inc_ref()
Definition: gil.h:91
gil_scoped_release::disassoc
bool disassoc
Definition: gil.h:185
PYBIND11_TLS_DELETE_VALUE
#define PYBIND11_TLS_DELETE_VALUE(key)
Definition: internals.h:100
gil_scoped_release::disarm
PYBIND11_NOINLINE void disarm()
Definition: gil.h:165
gil_scoped_acquire::disarm
PYBIND11_NOINLINE void disarm()
Definition: gil.h:123
make_changelog.state
state
Definition: make_changelog.py:29


gtsam
Author(s):
autogenerated on Sun Dec 22 2024 04:11:37