Panda3D
Loading...
Searching...
No Matches
pythonThread.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file pythonThread.cxx
10 * @author drose
11 * @date 2007-04-13
12 */
13
14#include "pythonThread.h"
15#include "pnotify.h"
16
17#ifdef HAVE_PYTHON
18#include "py_panda.h"
19
20TypeHandle PythonThread::_type_handle;
21
22/**
23 *
24 */
25PythonThread::
26PythonThread(PyObject *function, PyObject *args,
27 const std::string &name, const std::string &sync_name) :
28 Thread(name, sync_name)
29{
30 _function = function;
31 Py_INCREF(_function);
32 _args = nullptr;
33 _result = nullptr;
34
35 if (!PyCallable_Check(_function)) {
36 nassert_raise("Invalid function passed to PythonThread constructor");
37 }
38
39 set_args(args);
40
41#if !defined(SIMPLE_THREADS) && defined(WITH_THREAD) && PY_VERSION_HEX < 0x03090000
42 // Ensure that the Python threading system is initialized and ready to go.
43 // WITH_THREAD symbol defined within Python.h
44 // PyEval_InitThreads is now a deprecated no-op in Python 3.9+
45 PyEval_InitThreads();
46#endif
47}
48
49/**
50 *
51 */
52PythonThread::
53~PythonThread() {
54 // Unfortunately, we need to grab the GIL to release these things,
55 // since the destructor could be called from any thread.
56#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
57 PyGILState_STATE gstate;
58 gstate = PyGILState_Ensure();
59#endif
60
61 Py_DECREF(_function);
62 Py_XDECREF(_args);
63 Py_XDECREF(_result);
64
65#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
66 PyGILState_Release(gstate);
67#endif
68}
69
70/**
71 * Blocks the calling process until the thread terminates. If the thread has
72 * already terminated, this returns immediately.
73 *
74 * The PythonThread flavor of this function returns the same value returned by
75 * the thread function.
76 */
77PyObject *PythonThread::
78join() {
80
81 if (_result == nullptr) {
82 // No result; return None.
83 Py_INCREF(Py_None);
84 return Py_None;
85 }
86
87 Py_INCREF(_result);
88 return _result;
89}
90
91/**
92 *
93 */
94PyObject *PythonThread::
95get_args() const {
96 return _args;
97}
98
99/**
100 *
101 */
102void PythonThread::
103set_args(PyObject *args) {
104 Py_XDECREF(_args);
105
106 if (args == Py_None) {
107 // None means no arguments; create an empty tuple.
108 _args = PyTuple_New(0);
109 } else {
110 _args = nullptr;
111 if (PySequence_Check(args)) {
112 _args = PySequence_Tuple(args);
113 }
114 if (_args == nullptr) {
115 Dtool_Raise_TypeError("PythonThread args must be a tuple");
116 }
117 }
118}
119
120#ifdef HAVE_PYTHON
121/**
122 * Internal function to safely call a Python function within a sub-thread,
123 * that might execute in parallel with existing Python code. The return value
124 * is the return value of the Python function, or NULL if there was an
125 * exception.
126 */
127PyObject *PythonThread::
128call_python_func(PyObject *function, PyObject *args) {
129 Thread *current_thread = get_current_thread();
130
131 // Create a new Python thread state data structure, so Python can properly
132 // lock itself.
133 PyObject *result = nullptr;
134
135 if (current_thread == get_main_thread()) {
136 // In the main thread, just call the function.
137 result = PyObject_Call(function, args, nullptr);
138
139 if (result == nullptr) {
140 if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
141 // If we caught SystemExit, let it pass by without bothering to print
142 // a callback.
143
144 } else {
145 // Temporarily save and restore the exception state so we can print a
146 // callback on-the-spot.
147 PyObject *exc, *val, *tb;
148 PyErr_Fetch(&exc, &val, &tb);
149
150 Py_XINCREF(exc);
151 Py_XINCREF(val);
152 Py_XINCREF(tb);
153 PyErr_Restore(exc, val, tb);
154 PyErr_Print();
155
156 PyErr_Restore(exc, val, tb);
157 }
158 }
159
160 } else {
161#ifndef HAVE_THREADS
162 // Shouldn't be possible to come here without having some kind of
163 // threading support enabled.
164 nassert_raise("threading support disabled");
165 return nullptr;
166#else
167
168#ifdef SIMPLE_THREADS
169 // We can't use the PyGILState interface, which assumes we are using true
170 // OS-level threading. PyGILState enforces policies like only one thread
171 // state per OS-level thread, which is not true in the case of
172 // SIMPLE_THREADS.
173
174 // For some reason I don't fully understand, I'm getting a crash when I
175 // clean up old PyThreadState objects with PyThreadState_Delete(). It
176 // appears that the thread state is still referenced somewhere at the time
177 // I call delete, and the crash occurs because I've deleted an active
178 // pointer.
179
180 // Storing these pointers in a vector for permanent recycling seems to
181 // avoid this problem. I wish I understood better what's going wrong, but
182 // I guess this workaround will do.
183 static pvector<PyThreadState *> thread_states;
184
185 PyThreadState *orig_thread_state = PyThreadState_Get();
186 PyInterpreterState *istate = orig_thread_state->interp;
187 PyThreadState *new_thread_state;
188 if (thread_states.empty()) {
189 new_thread_state = PyThreadState_New(istate);
190 } else {
191 new_thread_state = thread_states.back();
192 thread_states.pop_back();
193 }
194 PyThreadState_Swap(new_thread_state);
195
196 // Call the user's function.
197 result = PyObject_Call(function, args, nullptr);
198 if (result == nullptr && PyErr_Occurred()) {
199 // We got an exception. Move the exception from the current thread into
200 // the main thread, so it can be handled there.
201 PyObject *exc, *val, *tb;
202 PyErr_Fetch(&exc, &val, &tb);
203
204 thread_cat.error()
205 << "Exception occurred within " << *current_thread << "\n";
206
207 // Temporarily restore the exception state so we can print a callback
208 // on-the-spot.
209 Py_XINCREF(exc);
210 Py_XINCREF(val);
211 Py_XINCREF(tb);
212 PyErr_Restore(exc, val, tb);
213 PyErr_Print();
214
215 PyThreadState_Swap(orig_thread_state);
216 thread_states.push_back(new_thread_state);
217 // PyThreadState_Clear(new_thread_state);
218 // PyThreadState_Delete(new_thread_state);
219
220 PyErr_Restore(exc, val, tb);
221
222 // Now attempt to force the main thread to the head of the ready queue,
223 // so it can respond to the exception immediately. This only works if
224 // the main thread is not blocked, of course.
225 Thread::get_main_thread()->preempt();
226
227 } else {
228 // No exception. Restore the thread state normally.
229 PyThreadState_Swap(orig_thread_state);
230 thread_states.push_back(new_thread_state);
231 // PyThreadState_Clear(new_thread_state);
232 // PyThreadState_Delete(new_thread_state);
233 }
234
235#else // SIMPLE_THREADS
236 // With true threading enabled, we're better off using PyGILState.
237 PyGILState_STATE gstate;
238 gstate = PyGILState_Ensure();
239
240 // Call the user's function.
241 result = PyObject_Call(function, args, nullptr);
242 if (result == nullptr && PyErr_Occurred()) {
243 // We got an exception. Move the exception from the current thread into
244 // the main thread, so it can be handled there.
245 PyObject *exc, *val, *tb;
246 PyErr_Fetch(&exc, &val, &tb);
247
248 thread_cat.error()
249 << "Exception occurred within " << *current_thread << "\n";
250
251 // Temporarily restore the exception state so we can print a callback
252 // on-the-spot.
253 Py_XINCREF(exc);
254 Py_XINCREF(val);
255 Py_XINCREF(tb);
256 PyErr_Restore(exc, val, tb);
257 PyErr_Print();
258
259 PyGILState_Release(gstate);
260
261 if (PyGILState_Check()) {
262 PyErr_Restore(exc, val, tb);
263 }
264 } else {
265 // No exception. Restore the thread state normally.
266 PyGILState_Release(gstate);
267 }
268
269
270#endif // SIMPLE_THREADS
271#endif // HAVE_THREADS
272 }
273
274 return result;
275}
276#endif // HAVE_PYTHON
277
278/**
279 *
280 */
281void PythonThread::
282thread_main() {
283 _result = call_python_func(_function, _args);
284}
285
286#endif // HAVE_PYTHON
A thread; that is, a lightweight process.
Definition thread.h:46
void join()
Blocks the calling process until the thread terminates.
Definition thread.I:239
get_main_thread
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process.
Definition thread.h:107
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.