Panda3D
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 
20 TypeHandle PythonThread::_type_handle;
21 
22 /**
23  *
24  */
25 PythonThread::
26 PythonThread(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 #ifndef SIMPLE_THREADS
42  // Ensure that the Python threading system is initialized and ready to go.
43 #ifdef WITH_THREAD // This symbol defined within Python.h
44  PyEval_InitThreads();
45 #endif
46 #endif
47 }
48 
49 /**
50  *
51  */
52 PythonThread::
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  */
77 PyObject *PythonThread::
78 join() {
79  Thread::join();
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  */
94 PyObject *PythonThread::
95 get_args() const {
96  return _args;
97 }
98 
99 /**
100  *
101  */
102 void PythonThread::
103 set_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  */
127 PyObject *PythonThread::
128 call_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  PyErr_Restore(exc, val, tb);
262  } else {
263  // No exception. Restore the thread state normally.
264  PyGILState_Release(gstate);
265  }
266 
267 
268 #endif // SIMPLE_THREADS
269 #endif // HAVE_THREADS
270  }
271 
272  return result;
273 }
274 #endif // HAVE_PYTHON
275 
276 /**
277  *
278  */
279 void PythonThread::
280 thread_main() {
281  _result = call_python_func(_function, _args);
282 }
283 
284 #endif // HAVE_PYTHON
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_main_thread
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process.
Definition: thread.h:107
void join()
Blocks the calling process until the thread terminates.
Definition: thread.I:239
A thread; that is, a lightweight process.
Definition: thread.h:46
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81