diff --git a/contrib/src/ai/aiBehaviors.h b/contrib/src/ai/aiBehaviors.h index eb70906e4a..0d3f8f0a70 100644 --- a/contrib/src/ai/aiBehaviors.h +++ b/contrib/src/ai/aiBehaviors.h @@ -151,7 +151,7 @@ PUBLISHED: void obstacle_avoidance(float feeler_length = 1.0); - void path_follow(float follow_wt); + void path_follow(float follow_wt = 1.0f); void add_to_path(LVecBase3 pos); void start_follow(std::string type = "normal"); diff --git a/direct/src/motiontrail/cMotionTrail.cxx b/direct/src/motiontrail/cMotionTrail.cxx index e578ea2f63..61b6a08916 100644 --- a/direct/src/motiontrail/cMotionTrail.cxx +++ b/direct/src/motiontrail/cMotionTrail.cxx @@ -331,7 +331,7 @@ update_motion_trail (PN_stdfloat current_time, LMatrix4 *transform) { frame_iterator = _frame_list.begin ( ); motion_trail_frame = *frame_iterator; - if (*transform == motion_trail_frame._transform) { + if (motion_trail_frame._transform == UnalignedLMatrix4(*transform)) { // duplicate transform return; } diff --git a/dtool/src/dtoolbase/pallocator.T b/dtool/src/dtoolbase/pallocator.T index ed8349debe..0a92caee20 100644 --- a/dtool/src/dtoolbase/pallocator.T +++ b/dtool/src/dtoolbase/pallocator.T @@ -20,7 +20,7 @@ pallocator_single(TypeHandle type_handle) noexcept : template INLINE Type *pallocator_single:: -allocate(typename pallocator_single::size_type n, typename std::allocator::const_pointer) { +allocate(typename pallocator_single::size_type n, const void *) { TAU_PROFILE("pallocator_single:allocate()", " ", TAU_USER); // This doesn't support allocating arrays. assert(n == 1); @@ -30,7 +30,7 @@ allocate(typename pallocator_single::size_type n, typename std::allocator< template INLINE void pallocator_single:: -deallocate(typename pallocator_single::pointer p, typename pallocator_single::size_type) { +deallocate(Type *p, typename pallocator_single::size_type) { TAU_PROFILE("pallocator_single:deallocate()", " ", TAU_USER); StaticDeletedChain::deallocate(p, _type_handle); } @@ -44,13 +44,13 @@ pallocator_array(TypeHandle type_handle) noexcept : template INLINE Type *pallocator_array:: -allocate(typename pallocator_array::size_type n, typename std::allocator::const_pointer) { - return (typename pallocator_array::pointer) +allocate(typename pallocator_array::size_type n, const void *) { + return (Type *) ASSUME_ALIGNED(_type_handle.allocate_array(n * sizeof(Type)), MEMORY_HOOK_ALIGNMENT); } template INLINE void pallocator_array:: -deallocate(typename pallocator_array::pointer p, typename pallocator_array::size_type) { +deallocate(Type *p, typename pallocator_array::size_type) { _type_handle.deallocate_array((void *)p); } diff --git a/dtool/src/dtoolbase/pallocator.h b/dtool/src/dtoolbase/pallocator.h index d61ca64ceb..77fe34f50c 100644 --- a/dtool/src/dtoolbase/pallocator.h +++ b/dtool/src/dtoolbase/pallocator.h @@ -46,10 +46,10 @@ class pallocator_single : public std::allocator { public: // Nowadays we cannot implicitly inherit typedefs from base classes in a // template class; we must explicitly copy them here. - typedef typename std::allocator::pointer pointer; - typedef typename std::allocator::reference reference; - typedef typename std::allocator::const_pointer const_pointer; - typedef typename std::allocator::const_reference const_reference; + typedef Type *pointer; + typedef Type &reference; + typedef const Type *const_pointer; + typedef const Type &const_reference; typedef typename std::allocator::size_type size_type; INLINE pallocator_single(TypeHandle type_handle) noexcept; @@ -59,9 +59,9 @@ public: INLINE pallocator_single(const pallocator_single ©) noexcept : _type_handle(copy._type_handle) { } - INLINE Type *allocate(size_type n, std::allocator::const_pointer hint = 0) + INLINE Type *allocate(size_type n, const void *hint = 0) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); - INLINE void deallocate(pointer p, size_type n); + INLINE void deallocate(Type *p, size_type n); template struct rebind { typedef pallocator_single other; @@ -75,10 +75,10 @@ class pallocator_array : public std::allocator { public: // Nowadays we cannot implicitly inherit typedefs from base classes in a // template class; we must explicitly copy them here. - typedef typename std::allocator::pointer pointer; - typedef typename std::allocator::reference reference; - typedef typename std::allocator::const_pointer const_pointer; - typedef typename std::allocator::const_reference const_reference; + typedef Type *pointer; + typedef Type &reference; + typedef const Type *const_pointer; + typedef const Type &const_reference; typedef typename std::allocator::size_type size_type; INLINE pallocator_array(TypeHandle type_handle = TypeHandle::none()) noexcept; @@ -88,9 +88,9 @@ public: INLINE pallocator_array(const pallocator_array ©) noexcept : _type_handle(copy._type_handle) { } - INLINE Type *allocate(size_type n, std::allocator::const_pointer hint = 0) + INLINE Type *allocate(size_type n, const void *hint = 0) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); - INLINE void deallocate(pointer p, size_type n); + INLINE void deallocate(Type *p, size_type n); template struct rebind { typedef pallocator_array other; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index d8ac49d929..1c06c942c9 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -4306,18 +4306,19 @@ int get_type_sort(CPPType *type) { TypeManager::is_struct(type)) { answer = 20; int deepest = 0; - TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)), false); - InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); - const InterrogateType &itype = idb->get_type(type_index); - if (itype.is_class() || itype.is_struct()) { - int num_derivations = itype.number_of_derivations(); - for (int di = 0; di < num_derivations; di++) { - TypeIndex d_type_Index = itype.get_derivation(di); - const InterrogateType &d_itype = idb->get_type(d_type_Index); - int this_one = get_type_sort(d_itype._cpptype); - if (this_one > deepest) { - deepest = this_one; + // Sort such that more derived classes come first. + type = TypeManager::unwrap(TypeManager::resolve_type(type)); + if (type != nullptr) { + CPPStructType *struct_type = type->as_struct_type(); + if (struct_type != nullptr) { + for (const CPPStructType::Base &base : struct_type->_derivation) { + if (base._base != nullptr) { + int this_one = get_type_sort(base._base); + if (this_one > deepest) { + deepest = this_one; + } + } } } } diff --git a/dtool/src/interrogatedb/py_compat.h b/dtool/src/interrogatedb/py_compat.h index 2cfd913b7f..f294651acc 100644 --- a/dtool/src/interrogatedb/py_compat.h +++ b/dtool/src/interrogatedb/py_compat.h @@ -137,6 +137,11 @@ typedef long Py_hash_t; # endif #endif +/* Python 3.4 */ +#if PY_VERSION_HEX < 0x03040000 +#define PyGILState_Check() (PyGILState_GetThisThreadState() == _PyThreadState_Current) +#endif + /* Python 3.6 */ #if PY_VERSION_HEX < 0x03080000 && !defined(_PyObject_CallNoArg) diff --git a/panda/src/event/asyncFuture.h b/panda/src/event/asyncFuture.h index 27fa1db29c..102deff8ee 100644 --- a/panda/src/event/asyncFuture.h +++ b/panda/src/event/asyncFuture.h @@ -84,13 +84,14 @@ PUBLISHED: BLOCKING void wait(); BLOCKING void wait(double timeout); + EXTENSION(void set_result(PyObject *)); + +public: INLINE void set_result(std::nullptr_t); INLINE void set_result(TypedObject *result); INLINE void set_result(TypedReferenceCount *result); INLINE void set_result(TypedWritableReferenceCount *result); INLINE void set_result(const EventParameter &result); - -public: void set_result(TypedObject *ptr, ReferenceCount *ref_ptr); INLINE TypedObject *get_result() const; diff --git a/panda/src/event/asyncFuture_ext.cxx b/panda/src/event/asyncFuture_ext.cxx index 7f0ab9d658..63c67a5840 100644 --- a/panda/src/event/asyncFuture_ext.cxx +++ b/panda/src/event/asyncFuture_ext.cxx @@ -15,14 +15,20 @@ #include "asyncTaskSequence.h" #include "eventParameter.h" #include "paramValue.h" +#include "paramPyObject.h" #include "pythonTask.h" +#include "asyncTaskManager.h" +#include "config_event.h" #ifdef HAVE_PYTHON #ifndef CPPPARSER extern struct Dtool_PyTypedObject Dtool_AsyncFuture; +extern struct Dtool_PyTypedObject Dtool_EventParameter; extern struct Dtool_PyTypedObject Dtool_ParamValueBase; extern struct Dtool_PyTypedObject Dtool_TypedObject; +extern struct Dtool_PyTypedObject Dtool_TypedReferenceCount; +extern struct Dtool_PyTypedObject Dtool_TypedWritableReferenceCount; #endif /** @@ -90,20 +96,24 @@ static PyObject *get_done_result(const AsyncFuture *future) { // EventStoreInt and Double are not exposed to Python for some reason. if (type == EventStoreInt::get_class_type()) { return Dtool_WrapValue(((EventStoreInt *)ptr)->get_value()); - } else if (type == EventStoreDouble::get_class_type()) { + } + else if (type == EventStoreDouble::get_class_type()) { return Dtool_WrapValue(((EventStoreDouble *)ptr)->get_value()); } + else if (type == ParamPyObject::get_class_type()) { + return ((ParamPyObject *)ptr)->get_value(); + } ParamValueBase *value = (ParamValueBase *)ptr; PyObject *wrap = DTool_CreatePyInstanceTyped ((void *)value, Dtool_ParamValueBase, false, false, type.get_index()); if (wrap != nullptr) { PyObject *value = PyObject_GetAttrString(wrap, "value"); + Py_DECREF(wrap); if (value != nullptr) { return value; } PyErr_Clear(); - Py_DECREF(wrap); } } @@ -154,6 +164,58 @@ __await__(PyObject *self) { return Dtool_NewGenerator(self, &gen_next); } +/** + * Sets this future's result. Can only be called if done() returns false. + */ +void Extension:: +set_result(PyObject *result) { + if (result == Py_None) { + _this->set_result(nullptr); + return; + } + else if (DtoolInstance_Check(result)) { + void *ptr; + if ((ptr = DtoolInstance_UPCAST(result, Dtool_EventParameter))) { + _this->set_result(*(const EventParameter *)ptr); + return; + } + if ((ptr = DtoolInstance_UPCAST(result, Dtool_TypedWritableReferenceCount))) { + _this->set_result((TypedWritableReferenceCount *)ptr); + return; + } + if ((ptr = DtoolInstance_UPCAST(result, Dtool_TypedReferenceCount))) { + _this->set_result((TypedReferenceCount *)ptr); + return; + } + if ((ptr = DtoolInstance_UPCAST(result, Dtool_TypedObject))) { + _this->set_result((TypedObject *)ptr); + return; + } + } + else if (PyUnicode_Check(result)) { + Py_ssize_t result_len; + wchar_t *result_str = PyUnicode_AsWideCharString(result, &result_len); + _this->set_result(new EventStoreWstring(std::wstring(result_str, result_len))); + PyMem_Free(result_str); + return; + } + else if (PyLongOrInt_Check(result)) { + long result_val = PyLongOrInt_AS_LONG(result); + if (result_val >= INT_MIN && result_val <= INT_MAX) { + _this->set_result(new EventStoreInt((int)result_val)); + return; + } + } + else if (PyNumber_Check(result)) { + _this->set_result(new EventStoreDouble(PyFloat_AsDouble(result))); + return; + } + + // If we don't recognize the type, store it as a generic PyObject pointer. + ParamPyObject::init_type(); + _this->set_result(new ParamPyObject(result)); +} + /** * Returns the result of this future, unless it was cancelled, in which case * it returns CancelledError. @@ -285,8 +347,19 @@ gather(PyObject *args) { } } else if (PyCoro_CheckExact(item)) { // We allow passing in a coroutine instead of a future. This causes it - // to be scheduled as a task. - futures.push_back(new PythonTask(item)); + // to be scheduled as a task on the current task manager. + PT(AsyncTask) task = new PythonTask(item); + Thread *current_thread = Thread::get_current_thread(); + AsyncTask *current_task = (AsyncTask *)current_thread->get_current_task(); + if (current_task != nullptr) { + task->set_task_chain(current_task->get_task_chain()); + current_task->get_manager()->add(task); + } + else { + event_cat.warning() + << "gather() with coroutine not called from within a task; not scheduling with task manager.\n"; + } + futures.push_back(task); continue; } return Dtool_Raise_ArgTypeError(item, i, "gather", "coroutine, task or future"); diff --git a/panda/src/event/asyncFuture_ext.h b/panda/src/event/asyncFuture_ext.h index eb8c688710..fbf834331f 100644 --- a/panda/src/event/asyncFuture_ext.h +++ b/panda/src/event/asyncFuture_ext.h @@ -29,6 +29,7 @@ public: static PyObject *__await__(PyObject *self); static PyObject *__iter__(PyObject *self) { return __await__(self); } + void set_result(PyObject *result); PyObject *result(PyObject *timeout = Py_None) const; PyObject *add_done_callback(PyObject *self, PyObject *fn); diff --git a/panda/src/event/pythonTask.cxx b/panda/src/event/pythonTask.cxx index adebb3e2b7..2091fa9214 100644 --- a/panda/src/event/pythonTask.cxx +++ b/panda/src/event/pythonTask.cxx @@ -84,7 +84,6 @@ PythonTask(PyObject *func_or_coro, const std::string &name) : */ PythonTask:: ~PythonTask() { -#ifndef NDEBUG // If the coroutine threw an exception, and there was no opportunity to // handle it, let the user know. if (_exception != nullptr && !_retrieved_exception) { @@ -97,7 +96,6 @@ PythonTask:: _exc_value = nullptr; _exc_traceback = nullptr; } -#endif Py_XDECREF(_function); Py_DECREF(_args); diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index de7874ec55..33eb7256b2 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -3054,6 +3054,37 @@ glsl_report_program_errors(GLuint program, bool fatal) { if (strcmp(info_log, "Success.\n") != 0 && strcmp(info_log, "No errors.\n") != 0 && strcmp(info_log, "Validation successful.\n") != 0) { + +#ifdef __APPLE__ + // Filter out these unhelpful warnings that Apple always generates. + while (true) { + if (info_log[0] == '\n') { + ++info_log; + continue; + } + if (info_log[0] == '\0') { + // We reached the end without finding anything interesting. + return; + } + int linelen = 0; + if ((sscanf(info_log, "WARNING: Could not find vertex shader attribute %*s to match BindAttributeLocation request.%*[\n]%n", &linelen) == 0 && linelen > 0) || + (sscanf(info_log, "WARNING: Could not find fragment shader output %*s to match FragDataBinding request.%*[\n]%n", &linelen) == 0 && linelen > 0)) { + info_log += linelen; + continue; + } + else { + break; + } + + info_log = strchr(info_log, '\n'); + if (info_log == nullptr) { + // We reached the end without finding anything interesting. + return; + } + ++info_log; + } +#endif + if (!fatal) { GLCAT.warning() << "Shader " << _shader->get_filename() << " produced the " diff --git a/panda/src/pipeline/pythonThread.cxx b/panda/src/pipeline/pythonThread.cxx index 066553eaea..506f949289 100644 --- a/panda/src/pipeline/pythonThread.cxx +++ b/panda/src/pipeline/pythonThread.cxx @@ -258,7 +258,9 @@ call_python_func(PyObject *function, PyObject *args) { PyGILState_Release(gstate); - PyErr_Restore(exc, val, tb); + if (PyGILState_Check()) { + PyErr_Restore(exc, val, tb); + } } else { // No exception. Restore the thread state normally. PyGILState_Release(gstate); diff --git a/panda/src/putil/p3putil_ext_composite.cxx b/panda/src/putil/p3putil_ext_composite.cxx index 4dde9e5c98..6306661a7f 100644 --- a/panda/src/putil/p3putil_ext_composite.cxx +++ b/panda/src/putil/p3putil_ext_composite.cxx @@ -1,5 +1,6 @@ #include "bamReader_ext.cxx" #include "bitArray_ext.cxx" +#include "paramPyObject.cxx" #include "pythonCallbackObject.cxx" #include "sparseArray_ext.cxx" #include "typedWritable_ext.cxx" diff --git a/panda/src/putil/paramPyObject.I b/panda/src/putil/paramPyObject.I new file mode 100644 index 0000000000..4099812974 --- /dev/null +++ b/panda/src/putil/paramPyObject.I @@ -0,0 +1,31 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file paramPyObject.I + * @author rdb + * @date 2021-03-01 + */ + +#include "paramPyObject.h" + +/** + * Increments the reference count. Assumes the GIL is held. + */ +INLINE ParamPyObject:: +ParamPyObject(PyObject *value) : _value(value) { + Py_INCREF(value); +} + +/** + * Returns a new reference to the stored value. + */ +INLINE PyObject *ParamPyObject:: +get_value() const { + Py_INCREF(_value); + return _value; +} diff --git a/panda/src/putil/paramPyObject.cxx b/panda/src/putil/paramPyObject.cxx new file mode 100644 index 0000000000..ac2e54aff9 --- /dev/null +++ b/panda/src/putil/paramPyObject.cxx @@ -0,0 +1,42 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file paramPyObject.cxx + * @author rdb + * @date 2021-03-01 + */ + +#include "paramPyObject.h" + +TypeHandle ParamPyObject::_type_handle; + +/** + * Decrements the reference count. + */ +ParamPyObject:: +~ParamPyObject() { +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); +#endif + + Py_DECREF(_value); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_Release(gstate); +#endif +} + +/** + * + */ +void ParamPyObject:: +output(std::ostream &out) const { + out << "<" << Py_TYPE(_value)->tp_name + << " object at " << (void *)_value << ">"; +} diff --git a/panda/src/putil/paramPyObject.h b/panda/src/putil/paramPyObject.h new file mode 100644 index 0000000000..cd1305b231 --- /dev/null +++ b/panda/src/putil/paramPyObject.h @@ -0,0 +1,61 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file paramPyObject.h + * @author rdb + * @date 2021-03-01 + */ + +#ifndef PARAMPYOBJECT_H +#define PARAMPYOBJECT_H + +#include "pandabase.h" +#include "paramValue.h" + +#ifdef HAVE_PYTHON + +#include "py_panda.h" + +/** + * A class object for storing an arbitrary Python object. + */ +class ParamPyObject final : public ParamValueBase { +public: + INLINE ParamPyObject(PyObject *value); + virtual ~ParamPyObject(); + + INLINE PyObject *get_value() const; + + void output(std::ostream &out) const override; + +public: + PyObject *_value; + +public: + virtual TypeHandle get_type() const override { + return get_class_type(); + } + virtual TypeHandle force_init_type() override {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + ParamValueBase::init_type(); + register_type(_type_handle, "ParamPyObject", + ParamValueBase::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +#include "paramPyObject.I" + +#endif // HAVE_PYTHON + +#endif diff --git a/tests/event/test_futures.py b/tests/event/test_futures.py index dcde328029..9e390ba02e 100644 --- a/tests/event/test_futures.py +++ b/tests/event/test_futures.py @@ -163,6 +163,71 @@ def test_coro_exception(): task.result() +def test_future_result(): + # Cancelled + fut = core.AsyncFuture() + assert not fut.done() + fut.cancel() + with pytest.raises(CancelledError): + fut.result() + + # None + fut = core.AsyncFuture() + fut.set_result(None) + assert fut.done() + assert fut.result() is None + + # Store int + fut = core.AsyncFuture() + fut.set_result(123) + assert fut.result() == 123 + + # Store string + fut = core.AsyncFuture() + fut.set_result("test\000\u1234") + assert fut.result() == "test\000\u1234" + + # Store TypedWritableReferenceCount + tex = core.Texture() + rc = tex.get_ref_count() + fut = core.AsyncFuture() + fut.set_result(tex) + assert tex.get_ref_count() == rc + 1 + assert fut.result() == tex + assert tex.get_ref_count() == rc + 1 + assert fut.result() == tex + assert tex.get_ref_count() == rc + 1 + fut = None + assert tex.get_ref_count() == rc + + # Store EventParameter (gets unwrapped) + ep = core.EventParameter(0.5) + fut = core.AsyncFuture() + fut.set_result(ep) + assert fut.result() == 0.5 + assert fut.result() == 0.5 + + # Store TypedObject + dg = core.Datagram(b"test") + fut = core.AsyncFuture() + fut.set_result(dg) + assert fut.result() == dg + assert fut.result() == dg + + # Store arbitrary Python object + obj = object() + rc = sys.getrefcount(obj) + fut = core.AsyncFuture() + fut.set_result(obj) + assert sys.getrefcount(obj) == rc + 1 + assert fut.result() is obj + assert sys.getrefcount(obj) == rc + 1 + assert fut.result() is obj + assert sys.getrefcount(obj) == rc + 1 + fut = None + assert sys.getrefcount(obj) == rc + + def test_future_gather(): fut1 = core.AsyncFuture() fut2 = core.AsyncFuture()