Panda3D
|
00001 // Filename: pythonTask.cxx 00002 // Created by: drose (16Sep08) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "pythonTask.h" 00016 #include "pnotify.h" 00017 00018 #ifdef HAVE_PYTHON 00019 #include "py_panda.h" 00020 00021 TypeHandle PythonTask::_type_handle; 00022 00023 #ifndef CPPPARSER 00024 IMPORT_THIS struct Dtool_PyTypedObject Dtool_TypedReferenceCount; 00025 #endif 00026 00027 //////////////////////////////////////////////////////////////////// 00028 // Function: PythonTask::Constructor 00029 // Access: Published 00030 // Description: 00031 //////////////////////////////////////////////////////////////////// 00032 PythonTask:: 00033 PythonTask(PyObject *function, const string &name) : 00034 AsyncTask(name) 00035 { 00036 _function = NULL; 00037 _args = NULL; 00038 _upon_death = NULL; 00039 _owner = NULL; 00040 _registered_to_owner = false; 00041 _generator = NULL; 00042 00043 set_function(function); 00044 set_args(Py_None, true); 00045 set_upon_death(Py_None); 00046 set_owner(Py_None); 00047 00048 _dict = PyDict_New(); 00049 00050 #ifndef SIMPLE_THREADS 00051 // Ensure that the Python threading system is initialized and ready 00052 // to go. 00053 #ifdef WITH_THREAD // This symbol defined within Python.h 00054 PyEval_InitThreads(); 00055 #endif 00056 #endif 00057 } 00058 00059 //////////////////////////////////////////////////////////////////// 00060 // Function: PythonTask::Destructor 00061 // Access: Published, Virtual 00062 // Description: 00063 //////////////////////////////////////////////////////////////////// 00064 PythonTask:: 00065 ~PythonTask() { 00066 Py_DECREF(_function); 00067 Py_DECREF(_args); 00068 Py_DECREF(_dict); 00069 Py_XDECREF(_generator); 00070 Py_XDECREF(_owner); 00071 Py_XDECREF(_upon_death); 00072 } 00073 00074 //////////////////////////////////////////////////////////////////// 00075 // Function: PythonTask::set_function 00076 // Access: Published 00077 // Description: Replaces the function that is called when the task 00078 // runs. The parameter should be a Python callable 00079 // object. 00080 //////////////////////////////////////////////////////////////////// 00081 void PythonTask:: 00082 set_function(PyObject *function) { 00083 Py_XDECREF(_function); 00084 00085 _function = function; 00086 Py_INCREF(_function); 00087 if (_function != Py_None && !PyCallable_Check(_function)) { 00088 nassert_raise("Invalid function passed to PythonTask"); 00089 } 00090 } 00091 00092 //////////////////////////////////////////////////////////////////// 00093 // Function: PythonTask::get_function 00094 // Access: Published 00095 // Description: Returns the function that is called when the task 00096 // runs. 00097 //////////////////////////////////////////////////////////////////// 00098 PyObject *PythonTask:: 00099 get_function() { 00100 Py_INCREF(_function); 00101 return _function; 00102 } 00103 00104 //////////////////////////////////////////////////////////////////// 00105 // Function: PythonTask::set_args 00106 // Access: Published 00107 // Description: Replaces the argument list that is passed to the task 00108 // function. The parameter should be a tuple or list of 00109 // arguments, or None to indicate the empty list. 00110 //////////////////////////////////////////////////////////////////// 00111 void PythonTask:: 00112 set_args(PyObject *args, bool append_task) { 00113 Py_XDECREF(_args); 00114 _args = NULL; 00115 00116 if (args == Py_None) { 00117 // None means no arguments; create an empty tuple. 00118 _args = PyTuple_New(0); 00119 } else { 00120 if (PySequence_Check(args)) { 00121 _args = PySequence_Tuple(args); 00122 } 00123 } 00124 00125 if (_args == NULL) { 00126 nassert_raise("Invalid args passed to PythonTask"); 00127 _args = PyTuple_New(0); 00128 } 00129 00130 _append_task = append_task; 00131 } 00132 00133 //////////////////////////////////////////////////////////////////// 00134 // Function: PythonTask::get_args 00135 // Access: Published 00136 // Description: Returns the argument list that is passed to the task 00137 // function. 00138 //////////////////////////////////////////////////////////////////// 00139 PyObject *PythonTask:: 00140 get_args() { 00141 if (_append_task) { 00142 // If we want to append the task, we have to create a new tuple 00143 // with space for one more at the end. We have to do this 00144 // dynamically each time, to avoid storing the task itself in its 00145 // own arguments list, and thereby creating a cyclical reference. 00146 00147 int num_args = PyTuple_GET_SIZE(_args); 00148 PyObject *with_task = PyTuple_New(num_args + 1); 00149 for (int i = 0; i < num_args; ++i) { 00150 PyObject *item = PyTuple_GET_ITEM(_args, i); 00151 Py_INCREF(item); 00152 PyTuple_SET_ITEM(with_task, i, item); 00153 } 00154 00155 this->ref(); 00156 PyObject *self = 00157 DTool_CreatePyInstanceTyped(this, Dtool_TypedReferenceCount, 00158 true, false, get_type_index()); 00159 PyTuple_SET_ITEM(with_task, num_args, self); 00160 return with_task; 00161 00162 } else { 00163 Py_INCREF(_args); 00164 return _args; 00165 } 00166 } 00167 00168 //////////////////////////////////////////////////////////////////// 00169 // Function: PythonTask::set_upon_death 00170 // Access: Published 00171 // Description: Replaces the function that is called when the task 00172 // finishes. The parameter should be a Python callable 00173 // object. 00174 //////////////////////////////////////////////////////////////////// 00175 void PythonTask:: 00176 set_upon_death(PyObject *upon_death) { 00177 Py_XDECREF(_upon_death); 00178 00179 _upon_death = upon_death; 00180 Py_INCREF(_upon_death); 00181 if (_upon_death != Py_None && !PyCallable_Check(_upon_death)) { 00182 nassert_raise("Invalid upon_death function passed to PythonTask"); 00183 } 00184 } 00185 00186 //////////////////////////////////////////////////////////////////// 00187 // Function: PythonTask::get_upon_death 00188 // Access: Published 00189 // Description: Returns the function that is called when the task 00190 // finishes. 00191 //////////////////////////////////////////////////////////////////// 00192 PyObject *PythonTask:: 00193 get_upon_death() { 00194 Py_INCREF(_upon_death); 00195 return _upon_death; 00196 } 00197 00198 //////////////////////////////////////////////////////////////////// 00199 // Function: PythonTask::set_owner 00200 // Access: Published 00201 // Description: Specifies a Python object that serves as the "owner" 00202 // for the task. This owner object must have two 00203 // methods: _addTask() and _clearTask(), which will be 00204 // called with one parameter, the task object. 00205 // 00206 // owner._addTask() is called when the task is added 00207 // into the active task list, and owner._clearTask() is 00208 // called when it is removed. 00209 //////////////////////////////////////////////////////////////////// 00210 void PythonTask:: 00211 set_owner(PyObject *owner) { 00212 if (_owner != NULL && _owner != Py_None && _state != S_inactive) { 00213 unregister_from_owner(); 00214 } 00215 00216 Py_XDECREF(_owner); 00217 _owner = owner; 00218 Py_INCREF(_owner); 00219 00220 if (_owner != Py_None && _state != S_inactive) { 00221 register_to_owner(); 00222 } 00223 } 00224 00225 //////////////////////////////////////////////////////////////////// 00226 // Function: PythonTask::get_owner 00227 // Access: Published 00228 // Description: Returns the "owner" object. See set_owner(). 00229 //////////////////////////////////////////////////////////////////// 00230 PyObject *PythonTask:: 00231 get_owner() { 00232 Py_INCREF(_owner); 00233 return _owner; 00234 } 00235 00236 //////////////////////////////////////////////////////////////////// 00237 // Function: PythonTask::__setattr__ 00238 // Access: Published 00239 // Description: Maps from an expression like "task.attr_name = v". 00240 // This is customized here so we can support some 00241 // traditional task interfaces that supported directly 00242 // assigning certain values. We also support adding 00243 // arbitrary data to the Task object. 00244 //////////////////////////////////////////////////////////////////// 00245 int PythonTask:: 00246 __setattr__(const string &attr_name, PyObject *v) { 00247 if (task_cat.is_debug()) { 00248 PyObject *str = PyObject_Repr(v); 00249 task_cat.debug() 00250 << *this << ": task." << attr_name << " = " 00251 << PyString_AsString(str) << "\n"; 00252 Py_DECREF(str); 00253 } 00254 00255 if (attr_name == "delayTime") { 00256 if (v == Py_None) { 00257 clear_delay(); 00258 } else { 00259 double delay = PyFloat_AsDouble(v); 00260 if (!PyErr_Occurred()) { 00261 set_delay(delay); 00262 } 00263 } 00264 00265 } else if (attr_name == "name") { 00266 char *name = PyString_AsString(v); 00267 if (name != (char *)NULL) { 00268 set_name(name); 00269 } 00270 00271 } else if (attr_name == "id" || attr_name == "time" || 00272 attr_name == "frame" || attr_name == "wakeTime") { 00273 nassert_raise("Cannot set constant value"); 00274 return true; 00275 00276 } else { 00277 return PyDict_SetItemString(_dict, attr_name.c_str(), v); 00278 } 00279 00280 return 0; 00281 } 00282 00283 //////////////////////////////////////////////////////////////////// 00284 // Function: PythonTask::__setattr__ 00285 // Access: Published 00286 // Description: Maps from an expression like "del task.attr_name". 00287 // This is customized here so we can support some 00288 // traditional task interfaces that supported directly 00289 // assigning certain values. We also support adding 00290 // arbitrary data to the Task object. 00291 //////////////////////////////////////////////////////////////////// 00292 int PythonTask:: 00293 __setattr__(const string &attr_name) { 00294 return PyDict_DelItemString(_dict, attr_name.c_str()); 00295 } 00296 00297 //////////////////////////////////////////////////////////////////// 00298 // Function: PythonTask::__getattr__ 00299 // Access: Published 00300 // Description: Maps from an expression like "task.attr_name". 00301 // This is customized here so we can support some 00302 // traditional task interfaces that supported directly 00303 // querying certain values. We also support adding 00304 // arbitrary data to the Task object. 00305 //////////////////////////////////////////////////////////////////// 00306 PyObject *PythonTask:: 00307 __getattr__(const string &attr_name) const { 00308 if (attr_name == "time") { 00309 return PyFloat_FromDouble(get_elapsed_time()); 00310 } else if (attr_name == "name") { 00311 return PyString_FromString(get_name().c_str()); 00312 } else if (attr_name == "wakeTime") { 00313 return PyFloat_FromDouble(get_wake_time()); 00314 } else if (attr_name == "delayTime") { 00315 if (!has_delay()) { 00316 Py_RETURN_NONE; 00317 } 00318 return PyFloat_FromDouble(get_delay()); 00319 } else if (attr_name == "frame") { 00320 return PyInt_FromLong(get_elapsed_frames()); 00321 } else if (attr_name == "id") { 00322 return PyInt_FromLong(_task_id); 00323 } else { 00324 return PyMapping_GetItemString(_dict, (char *)attr_name.c_str()); 00325 } 00326 } 00327 00328 //////////////////////////////////////////////////////////////////// 00329 // Function: PythonTask::is_runnable 00330 // Access: Protected, Virtual 00331 // Description: Override this function to return true if the task can 00332 // be successfully executed, false if it cannot. Mainly 00333 // intended as a sanity check when attempting to add the 00334 // task to a task manager. 00335 // 00336 // This function is called with the lock held. 00337 //////////////////////////////////////////////////////////////////// 00338 bool PythonTask:: 00339 is_runnable() { 00340 return _function != Py_None; 00341 } 00342 00343 //////////////////////////////////////////////////////////////////// 00344 // Function: PythonTask::do_task 00345 // Access: Protected, Virtual 00346 // Description: Override this function to do something useful for the 00347 // task. 00348 // 00349 // This function is called with the lock *not* held. 00350 //////////////////////////////////////////////////////////////////// 00351 AsyncTask::DoneStatus PythonTask:: 00352 do_task() { 00353 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 00354 // Use PyGILState to protect this asynchronous call. 00355 PyGILState_STATE gstate; 00356 gstate = PyGILState_Ensure(); 00357 #endif 00358 00359 DoneStatus result = do_python_task(); 00360 00361 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 00362 PyGILState_Release(gstate); 00363 #endif 00364 00365 return result; 00366 } 00367 00368 //////////////////////////////////////////////////////////////////// 00369 // Function: PythonTask::do_python_task 00370 // Access: Protected 00371 // Description: The Python calls that implement do_task(). This 00372 // function is separate so we can acquire the Python 00373 // interpretor lock while it runs. 00374 //////////////////////////////////////////////////////////////////// 00375 AsyncTask::DoneStatus PythonTask:: 00376 do_python_task() { 00377 PyObject *result = NULL; 00378 00379 if (_generator == (PyObject *)NULL) { 00380 // We are calling the function directly. 00381 PyObject *args = get_args(); 00382 result = 00383 Thread::get_current_thread()->call_python_func(_function, args); 00384 Py_DECREF(args); 00385 00386 #ifdef PyGen_Check 00387 if (result != (PyObject *)NULL && PyGen_Check(result)) { 00388 // The function has yielded a generator. We will call into that 00389 // henceforth, instead of calling the function from the top 00390 // again. 00391 if (task_cat.is_debug()) { 00392 PyObject *str = PyObject_Repr(_function); 00393 task_cat.debug() 00394 << PyString_AsString(str) << " in " << *this 00395 << " yielded a generator.\n"; 00396 Py_DECREF(str); 00397 } 00398 _generator = result; 00399 result = NULL; 00400 } 00401 #endif 00402 } 00403 00404 if (_generator != (PyObject *)NULL) { 00405 // We are calling a generator. 00406 PyObject *func = PyObject_GetAttrString(_generator, "next"); 00407 nassertr(func != (PyObject *)NULL, DS_interrupt); 00408 00409 result = PyObject_CallObject(func, NULL); 00410 Py_DECREF(func); 00411 00412 if (result == (PyObject *)NULL && PyErr_Occurred() && 00413 PyErr_ExceptionMatches(PyExc_StopIteration)) { 00414 // "Catch" StopIteration and treat it like DS_done. 00415 PyErr_Clear(); 00416 Py_DECREF(_generator); 00417 _generator = NULL; 00418 return DS_done; 00419 } 00420 } 00421 00422 if (result == (PyObject *)NULL) { 00423 task_cat.error() 00424 << "Exception occurred in " << *this << "\n"; 00425 return DS_interrupt; 00426 } 00427 00428 if (result == Py_None) { 00429 Py_DECREF(result); 00430 return DS_done; 00431 } 00432 00433 if (PyInt_Check(result)) { 00434 int retval = PyInt_AS_LONG(result); 00435 switch (retval) { 00436 case DS_again: 00437 Py_XDECREF(_generator); 00438 _generator = NULL; 00439 // Fall through. 00440 00441 case DS_done: 00442 case DS_cont: 00443 case DS_pickup: 00444 case DS_exit: 00445 case DS_pause: 00446 // Legitimate value. 00447 Py_DECREF(result); 00448 return (DoneStatus)retval; 00449 00450 case -1: 00451 // Legacy value. 00452 Py_DECREF(result); 00453 return DS_done; 00454 00455 default: 00456 // Unexpected value. 00457 break; 00458 } 00459 } 00460 00461 PyObject *str = PyObject_Repr(result); 00462 ostringstream strm; 00463 strm 00464 << *this << " returned " << PyString_AsString(str); 00465 Py_DECREF(str); 00466 Py_DECREF(result); 00467 string message = strm.str(); 00468 nassert_raise(message); 00469 00470 return DS_interrupt; 00471 } 00472 00473 //////////////////////////////////////////////////////////////////// 00474 // Function: PythonTask::upon_birth 00475 // Access: Protected, Virtual 00476 // Description: Override this function to do something useful when the 00477 // task has been added to the active queue. 00478 // 00479 // This function is called with the lock *not* held. 00480 //////////////////////////////////////////////////////////////////// 00481 void PythonTask:: 00482 upon_birth(AsyncTaskManager *manager) { 00483 AsyncTask::upon_birth(manager); 00484 register_to_owner(); 00485 } 00486 00487 //////////////////////////////////////////////////////////////////// 00488 // Function: PythonTask::upon_death 00489 // Access: Protected, Virtual 00490 // Description: Override this function to do something useful when the 00491 // task has been removed from the active queue. The 00492 // parameter clean_exit is true if the task has been 00493 // removed because it exited normally (returning 00494 // DS_done), or false if it was removed for some other 00495 // reason (e.g. AsyncTaskManager::remove()). By the 00496 // time this method is called, _manager has been 00497 // cleared, so the parameter manager indicates the 00498 // original AsyncTaskManager that owned this task. 00499 // 00500 // The normal behavior is to throw the done_event only 00501 // if clean_exit is true. 00502 // 00503 // This function is called with the lock *not* held. 00504 //////////////////////////////////////////////////////////////////// 00505 void PythonTask:: 00506 upon_death(AsyncTaskManager *manager, bool clean_exit) { 00507 AsyncTask::upon_death(manager, clean_exit); 00508 00509 if (_upon_death != Py_None) { 00510 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 00511 // Use PyGILState to protect this asynchronous call. 00512 PyGILState_STATE gstate; 00513 gstate = PyGILState_Ensure(); 00514 #endif 00515 00516 call_function(_upon_death); 00517 00518 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 00519 PyGILState_Release(gstate); 00520 #endif 00521 } 00522 unregister_from_owner(); 00523 } 00524 00525 //////////////////////////////////////////////////////////////////// 00526 // Function: PythonTask::register_to_owner 00527 // Access: Private 00528 // Description: Tells the owner we are now his task. 00529 //////////////////////////////////////////////////////////////////// 00530 void PythonTask:: 00531 register_to_owner() { 00532 if (_owner != Py_None && !_registered_to_owner) { 00533 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 00534 // Use PyGILState to protect this asynchronous call. 00535 PyGILState_STATE gstate; 00536 gstate = PyGILState_Ensure(); 00537 #endif 00538 00539 _registered_to_owner = true; 00540 call_owner_method("_addTask"); 00541 00542 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 00543 PyGILState_Release(gstate); 00544 #endif 00545 } 00546 } 00547 00548 //////////////////////////////////////////////////////////////////// 00549 // Function: PythonTask::unregister_from_owner 00550 // Access: Private 00551 // Description: Tells the owner we are no longer his task. 00552 //////////////////////////////////////////////////////////////////// 00553 void PythonTask:: 00554 unregister_from_owner() { 00555 // make sure every call to _clearTask corresponds to a call to _addTask 00556 if (_owner != Py_None && _registered_to_owner) { 00557 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 00558 // Use PyGILState to protect this asynchronous call. 00559 PyGILState_STATE gstate; 00560 gstate = PyGILState_Ensure(); 00561 #endif 00562 00563 _registered_to_owner = false; 00564 call_owner_method("_clearTask"); 00565 00566 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 00567 PyGILState_Release(gstate); 00568 #endif 00569 } 00570 } 00571 00572 //////////////////////////////////////////////////////////////////// 00573 // Function: PythonTask::call_owner_method 00574 // Access: Private 00575 // Description: Calls the indicated method name on the given object, 00576 // if defined, passing in the task object as the only 00577 // parameter. 00578 //////////////////////////////////////////////////////////////////// 00579 void PythonTask:: 00580 call_owner_method(const char *method_name) { 00581 if (_owner != Py_None) { 00582 PyObject *func = PyObject_GetAttrString(_owner, (char *)method_name); 00583 if (func == (PyObject *)NULL) { 00584 PyObject *str = PyObject_Repr(_owner); 00585 task_cat.error() 00586 << "Owner object " << PyString_AsString(str) << " added to " 00587 << *this << " has no method " << method_name << "().\n"; 00588 Py_DECREF(str); 00589 00590 } else { 00591 call_function(func); 00592 Py_DECREF(func); 00593 } 00594 } 00595 } 00596 00597 //////////////////////////////////////////////////////////////////// 00598 // Function: PythonTask::call_function 00599 // Access: Private 00600 // Description: Calls the indicated Python function, passing in the 00601 // task object as the only parameter. 00602 //////////////////////////////////////////////////////////////////// 00603 void PythonTask:: 00604 call_function(PyObject *function) { 00605 if (function != Py_None) { 00606 this->ref(); 00607 PyObject *self = 00608 DTool_CreatePyInstanceTyped(this, Dtool_TypedReferenceCount, 00609 true, false, get_type_index()); 00610 PyObject *args = Py_BuildValue("(O)", self); 00611 Py_DECREF(self); 00612 00613 PyObject *result = PyObject_CallObject(function, args); 00614 Py_XDECREF(result); 00615 Py_DECREF(args); 00616 } 00617 } 00618 00619 #endif // HAVE_PYTHON