diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 30de77e03a..6abd56531a 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -39,6 +39,7 @@ #include "thread.h" #include "pipeline.h" #include "throw_event.h" +#include "objectDeletor.h" #if defined(WIN32) #define WINDOWS_LEAN_AND_MEAN @@ -68,6 +69,7 @@ PStatCollector GraphicsEngine::_render_states_pcollector("RenderStates"); PStatCollector GraphicsEngine::_render_states_unused_pcollector("RenderStates:Unused"); PStatCollector GraphicsEngine::_cyclers_pcollector("PipelineCyclers"); PStatCollector GraphicsEngine::_dirty_cyclers_pcollector("Dirty PipelineCyclers"); +PStatCollector GraphicsEngine::_delete_pcollector("App:Delete"); // These are counted independently by the collision system; we // redefine them here so we can reset them at each frame. @@ -631,6 +633,14 @@ render_frame() { #endif // THREADED_PIPELINE && DO_PSTATS + // If there is an object deletor, tell it to flush now, while we're + // between frames. + ObjectDeletor *deletor = ObjectDeletor::get_global_ptr(); + if (deletor != (ObjectDeletor *)NULL) { + PStatTimer timer(_delete_pcollector); + deletor->flush(); + } + GeomCacheManager::flush_level(); CullTraverser::flush_level(); RenderState::flush_level(); diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 7d0547a0cf..7b1b0231dc 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -359,6 +359,7 @@ private: static PStatCollector _render_states_unused_pcollector; static PStatCollector _cyclers_pcollector; static PStatCollector _dirty_cyclers_pcollector; + static PStatCollector _delete_pcollector; static PStatCollector _cnode_volume_pcollector; static PStatCollector _gnode_volume_pcollector; diff --git a/panda/src/express/Sources.pp b/panda/src/express/Sources.pp index bfff880532..dc49b1ddf5 100644 --- a/panda/src/express/Sources.pp +++ b/panda/src/express/Sources.pp @@ -30,6 +30,7 @@ multifile.I multifile.h \ namable.I \ namable.h nativeNumericData.I nativeNumericData.h \ + objectDeletor.h objectDeletor.I \ ordered_vector.h ordered_vector.I ordered_vector.T \ password_hash.h \ patchfile.I patchfile.h \ @@ -77,6 +78,7 @@ memoryUsagePointers.cxx multifile.cxx \ namable.cxx \ nativeNumericData.cxx \ + objectDeletor.cxx \ ordered_vector.cxx \ password_hash.cxx \ patchfile.cxx \ @@ -130,6 +132,7 @@ multifile.I multifile.h \ namable.I \ namable.h nativeNumericData.I nativeNumericData.h \ + objectDeletor.h objectDeletor.I \ ordered_vector.h ordered_vector.I ordered_vector.T \ password_hash.h \ patchfile.I patchfile.h \ diff --git a/panda/src/express/config_express.cxx b/panda/src/express/config_express.cxx index bf018af131..a46e0001b2 100644 --- a/panda/src/express/config_express.cxx +++ b/panda/src/express/config_express.cxx @@ -168,45 +168,6 @@ init_libexpress() { #endif } - -bool -get_leak_memory() { - static ConfigVariableBool *leak_memory = NULL; - - if (leak_memory == (ConfigVariableBool *)NULL) { - leak_memory = new ConfigVariableBool - ("leak-memory", false, - PRC_DESC("Set leak-memory true to disable the actual deletion of " - "ReferenceCount-derived objects. This is sometimes useful to track " - "a reference counting bug, since the formerly deleted objects will " - "still remain (with a reference count of -100) without being " - "overwritten with a newly-allocated object, and the assertion tests " - "in ReferenceCount may more accurately detect the first instance of " - "an error.")); - } - - return *leak_memory; -} - -bool -get_never_destruct() { - static ConfigVariableBool *never_destruct = NULL; - - if (never_destruct == (ConfigVariableBool *)NULL) { - never_destruct = new ConfigVariableBool - ("never-destruct", false, - PRC_DESC("never-destruct is similar to leak-memory, except that not " - "only will memory not be freed, but the destructor will not even be " - "called (on ReferenceCount objects, at least). This will leak gobs " - "of memory, but ensures that every pointer to a ReferenceCount " - "object will always be valid, and may be useful for tracking down " - "certain kinds of errors. " - "never-destruct is only respected if leak-memory is true.")); - } - - return *never_destruct; -} - bool get_use_high_res_clock() { static ConfigVariableBool *use_high_res_clock = NULL; diff --git a/panda/src/express/config_express.h b/panda/src/express/config_express.h index 832e6bd980..171c753e3f 100644 --- a/panda/src/express/config_express.h +++ b/panda/src/express/config_express.h @@ -51,8 +51,6 @@ NotifyCategoryDecl(express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS); //extern EXPCL_PANDAEXPRESS const bool track_memory_usage; -EXPCL_PANDAEXPRESS bool get_leak_memory(); -EXPCL_PANDAEXPRESS bool get_never_destruct(); EXPCL_PANDAEXPRESS bool get_use_high_res_clock(); EXPCL_PANDAEXPRESS bool get_paranoid_clock(); EXPCL_PANDAEXPRESS bool get_paranoid_inheritance(); diff --git a/panda/src/express/express_composite1.cxx b/panda/src/express/express_composite1.cxx index 300050f3a4..72f7181cde 100644 --- a/panda/src/express/express_composite1.cxx +++ b/panda/src/express/express_composite1.cxx @@ -18,6 +18,7 @@ #include "multifile.cxx" #include "namable.cxx" #include "nativeNumericData.cxx" +#include "objectDeletor.cxx" #include "ordered_vector.cxx" #include "patchfile.cxx" #include "password_hash.cxx" diff --git a/panda/src/express/objectDeletor.I b/panda/src/express/objectDeletor.I new file mode 100644 index 0000000000..eb4da91de5 --- /dev/null +++ b/panda/src/express/objectDeletor.I @@ -0,0 +1,62 @@ +// Filename: objectDeletor.I +// Created by: drose (10Apr06) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: ObjectDeletor::get_global_ptr +// Access: Public, Static +// Description: Returns the global ObjectDeletor object. This may +// return NULL if there is no such object. +//////////////////////////////////////////////////////////////////// +INLINE ObjectDeletor *ObjectDeletor:: +get_global_ptr() { + return (ObjectDeletor *)AtomicAdjust::get_ptr(_global_ptr); +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjectDeletor::set_global_ptr +// Access: Public, Static +// Description: Assigns the global ObjectDeletor object. Returns +// the pointer to the previous object, if any. +//////////////////////////////////////////////////////////////////// +INLINE ObjectDeletor *ObjectDeletor:: +set_global_ptr(ObjectDeletor *ptr) { + return (ObjectDeletor *)AtomicAdjust::set_ptr(_global_ptr, ptr); +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjectDeletor::DeleteToken::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ObjectDeletor::DeleteToken:: +DeleteToken(DeleteFunc *func, void *ptr) : + _func(func), + _ptr(ptr) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjectDeletor::DeleteToken::do_delete +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void ObjectDeletor::DeleteToken:: +do_delete() { + (*_func)(_ptr); +} diff --git a/panda/src/express/objectDeletor.cxx b/panda/src/express/objectDeletor.cxx new file mode 100644 index 0000000000..78f50e2441 --- /dev/null +++ b/panda/src/express/objectDeletor.cxx @@ -0,0 +1,84 @@ +// Filename: objectDeletor.h +// Created by: drose (10Apr06) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "objectDeletor.h" +#include "configVariableString.h" + +void *ObjectDeletor::_global_ptr = NULL; + +//////////////////////////////////////////////////////////////////// +// Function: ObjectDeletor::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +ObjectDeletor:: +~ObjectDeletor() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjectDeletor::delete_object +// Access: Public, Virtual +// Description: Adds the pointer to the object to be deleted, along +// with a pointer to a function that can delete it. +//////////////////////////////////////////////////////////////////// +void ObjectDeletor:: +delete_object(DeleteFunc *func, void *ptr) { + // The base class functionality simply deletes the pointer + // immediately. + (*func)(ptr); +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjectDeletor::flush +// Access: Public, Virtual +// Description: Ensures that any objects queued up for deletion have +// been fully deleted by the time flush() returns. +//////////////////////////////////////////////////////////////////// +void ObjectDeletor:: +flush() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ObjectDeletor::register_subclass +// Access: Public, Static +// Description: Called at static init time as each subclass is +// defined. The purpose here is to consult the config +// variable object-deletor and install the requested +// deletor. If the indicated deletor is the one that is +// named by the config variable, it is installed. +//////////////////////////////////////////////////////////////////// +void ObjectDeletor:: +register_subclass(ObjectDeletor *deletor, const string &name) { + ConfigVariableString object_deletor + ("object-deletor", "", + PRC_DESC("Specify the type of ObjectDeletor to install. This string " + "must match one of the existing ObjectDeletor types, and if " + "it does not match, no error message is generated. To " + "determine if the ObjectDeletor was properly installed, " + "set notify-level-express debug.")); + + if (object_deletor == name) { + if (express_cat.is_debug()) { + express_cat.debug() + << "Installing ObjectDeletor type " << name << "\n"; + } + set_global_ptr(deletor); + } else { + delete deletor; + } +} diff --git a/panda/src/express/objectDeletor.h b/panda/src/express/objectDeletor.h new file mode 100644 index 0000000000..262a799600 --- /dev/null +++ b/panda/src/express/objectDeletor.h @@ -0,0 +1,72 @@ +// Filename: objectDeletor.h +// Created by: drose (10Apr06) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef OBJECTDELETOR_H +#define OBJECTDELETOR_H + +#include "pandabase.h" +#include "atomicAdjust.h" + +//////////////////////////////////////////////////////////////////// +// Class : ObjectDeletor +// Description : This class is used to collect together pointers to +// objects that are ready to be destructed and freed. +// The actual destruction may be performed immediately, +// or it can be performed at some later time, when it is +// convenient for the application. +// +// This is particularly useful for a multithreaded +// application; the destruction may be performed in a +// sub-thread with a lower priority. +// +// This class body is just an interface; the +// ObjectDeletor class simply deletes its pointers +// immediately. More sophisticated deletors are +// implemented elsewhere. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS ObjectDeletor { +public: + virtual ~ObjectDeletor(); + + typedef void DeleteFunc(void *ptr); + + virtual void delete_object(DeleteFunc *func, void *ptr); + virtual void flush(); + + INLINE static ObjectDeletor *get_global_ptr(); + INLINE static ObjectDeletor *set_global_ptr(ObjectDeletor *ptr); + + static void register_subclass(ObjectDeletor *deletor, const string &name); + +protected: + class DeleteToken { + public: + INLINE DeleteToken(DeleteFunc *func, void *ptr); + INLINE void do_delete(); + + DeleteFunc *_func; + void *_ptr; + }; + +private: + static void *_global_ptr; +}; + +#include "objectDeletor.I" + +#endif diff --git a/panda/src/express/referenceCount.I b/panda/src/express/referenceCount.I index 3af97f7e51..c8d901bb0b 100644 --- a/panda/src/express/referenceCount.I +++ b/panda/src/express/referenceCount.I @@ -332,6 +332,41 @@ weak_unref(WeakPointerToVoid *ptv) { _weak_list->clear_reference(ptv); } +//////////////////////////////////////////////////////////////////// +// Function: DeleteWrapper::do_delete +// Access: Public, Static +// Description: This helper function encapsulates calling the +// destructor and delete operator for the given pointer. +// A pointer to this function is passed to the global +// ObjectDeletor, when there is one. +//////////////////////////////////////////////////////////////////// +template +void DeleteWrapper:: +do_delete(void *ptr) { + TAU_PROFILE("void DeleteWrapper::do_delete(void *)", " ", TAU_USER); + delete ((ObjectType *)ptr); +} + +//////////////////////////////////////////////////////////////////// +// Function: RefCountDeleteWrapper::do_delete +// Access: Public, Static +// Description: This is similar to DeleteWrapper::do_delete(), above, +// except it is specialized for an object that inherits +// from ReferenceCount, and it will check that some +// other pointer hasn't incremented the reference count +// in the interim before the pointer is actually +// deleted. +//////////////////////////////////////////////////////////////////// +template +void RefCountDeleteWrapper:: +do_delete(void *ptr) { + TAU_PROFILE("void RefCountDeleteWrapper::do_delete(void *)", " ", TAU_USER); + if (!((RefCountType *)ptr)->unref()) { + // The reference count has reached 0; time to delete the pointer. + delete ((RefCountType *)ptr); + } +} + //////////////////////////////////////////////////////////////////// // Function: unref_delete // Description: This global helper function will unref the given @@ -345,32 +380,66 @@ weak_unref(WeakPointerToVoid *ptv) { template INLINE void unref_delete(RefCountType *ptr) { - TAU_PROFILE("unref_delete()", " ", TAU_USER); + TAU_PROFILE("void unref_delete(RefCountType *)", " ", TAU_USER); // Although it may be tempting to try to upcast ptr to a // ReferenceCount object (particularly to get around inheritance // issues), resist that temptation, since some classes (in // particular, TransformState and RenderState) rely on a non-virtual // overloading of the unref() method. - if (!ptr->unref()) { -#ifndef NDEBUG - if (get_leak_memory()) { - // In leak-memory mode, we don't actually delete the pointer, - // although we do call the destructor explicitly. This has - // exactly the same effect as deleting it, without actually - // freeing up the memory it uses. - // Furthermore, if we have never-destruct set, we don't even - // call the destructor. - if (!get_never_destruct()) { - ptr->~RefCountType(); - } + if (!ptr->unref()) { + // The reference count has reached 0; time to delete the pointer. + + // Is there a deletor in effect? If so, pass it to that object. + ObjectDeletor *deletor = ObjectDeletor::get_global_ptr(); + if (deletor != (ObjectDeletor *)NULL) { + // Re-ref the pointer before adding it to the deletor. The + // deletor will de-ref it again before deleting it. + ptr->ref(); + deletor->delete_object(RefCountDeleteWrapper::do_delete, (void *)ptr); return; } -#endif + + // If there's no deletor, just delete the pointer now. delete ptr; } } +//////////////////////////////////////////////////////////////////// +// Function: defer_delete +// Description: This global helper function is designed to be called +// in lieu of the delete operator on any type of object +// (except for an object that inherits from +// ReferenceCount, of course, since you should never +// call delete explicitly on a reference-counted +// object). +// +// This function will ultimately have the same effect as +// calling delete on the object. If a ObjectDeletor +// is currently in effect, the delete will happen at +// some later time; but if a ObjectDeletor is not in +// effect, the delete will happen immediately. +// +// You should not attempt to replace an array-delete +// call, e.g. delete[] array, with a call to +// defer_delete(). This only replaces single-object +// deletes, e.g. delete object. +//////////////////////////////////////////////////////////////////// +template +INLINE void +defer_delete(ObjectType *ptr) { + TAU_PROFILE("void defer_delete(ObjectType *)", " ", TAU_USER); + + ObjectDeletor *deletor = ObjectDeletor::get_global_ptr(); + if (deletor != (ObjectDeletor *)NULL) { + deletor->delete_object(DeleteWrapper::do_delete, (void *)ptr); + return; + } + + // If there's no deletor, just delete the pointer now. + delete ptr; +} + //////////////////////////////////////////////////////////////////// // Function: RefCountProxy::Constructor diff --git a/panda/src/express/referenceCount.h b/panda/src/express/referenceCount.h index a43f8f9bff..3136a1f706 100644 --- a/panda/src/express/referenceCount.h +++ b/panda/src/express/referenceCount.h @@ -27,6 +27,7 @@ #include "atomicAdjust.h" #include "numeric_types.h" #include "deletedChain.h" +#include "objectDeletor.h" #include @@ -101,9 +102,24 @@ private: static TypeHandle _type_handle; }; +template +class DeleteWrapper { +public: + static void do_delete(void *ptr); +}; + +template +class RefCountDeleteWrapper { +public: + static void do_delete(void *ptr); +}; + template INLINE void unref_delete(RefCountType *ptr); +template +INLINE void defer_delete(ObjectType *ptr); + //////////////////////////////////////////////////////////////////// // Class : RefCountProxy // Description : A "proxy" to use to make a reference-countable object diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index 26181a5c8e..38b1cde935 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -606,26 +606,41 @@ get_override(TypeHandle type) const { //////////////////////////////////////////////////////////////////// bool RenderState:: unref() const { - ReMutexHolder holder(*_states_lock); + // Most of the time, we won't need to grab the lock. We first check + // whether we think we will need to grab it. Then, after we have + // successfully acquired the lock, we check that the condition is + // still valid. + // It is possible that, due to some race condition, this condition + // is never seen as true on any one thread. In that case, the cycle + // will not automatically be detected and broken. But since (a) + // that will be a relatively rare situation, (b) it will be + // expensive to protect against it, and (c) the damage is minimal, + // the race condition is allowed to remain. if (get_cache_ref_count() > 0 && get_ref_count() == get_cache_ref_count() + 1) { - // If we are about to remove the one reference that is not in the - // cache, leaving only references in the cache, then we need to - // check for a cycle involving this RenderState and break it if - // it exists. - if (auto_break_cycles) { - ++_last_cycle_detect; - if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) { - // Ok, we have a cycle. This will be a leak unless we break the - // cycle by freeing the cache on this object. - if (pgraph_cat.is_debug()) { - pgraph_cat.debug() - << "Breaking cycle involving " << (*this) << "\n"; + ReMutexHolder holder(*_states_lock); + + if (get_cache_ref_count() > 0 && + get_ref_count() == get_cache_ref_count() + 1) { + // If we are about to remove the one reference that is not in the + // cache, leaving only references in the cache, then we need to + // check for a cycle involving this RenderState and break it if + // it exists. + + if (auto_break_cycles) { + ++_last_cycle_detect; + if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) { + // Ok, we have a cycle. This will be a leak unless we break the + // cycle by freeing the cache on this object. + if (pgraph_cat.is_debug()) { + pgraph_cat.debug() + << "Breaking cycle involving " << (*this) << "\n"; + } + + ((RenderState *)this)->remove_cache_pointers(); } - - ((RenderState *)this)->remove_cache_pointers(); } } } diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx index 95edb3b8e7..d7c18cf30e 100644 --- a/panda/src/pgraph/transformState.cxx +++ b/panda/src/pgraph/transformState.cxx @@ -805,26 +805,41 @@ invert_compose(const TransformState *other) const { //////////////////////////////////////////////////////////////////// bool TransformState:: unref() const { - ReMutexHolder holder(*_states_lock); + // Most of the time, we won't need to grab the lock. We first check + // whether we think we will need to grab it. Then, after we have + // successfully acquired the lock, we check that the condition is + // still valid. + // It is possible that, due to some race condition, this condition + // is never seen as true on any one thread. In that case, the cycle + // will not automatically be detected and broken. But since (a) + // that will be a relatively rare situation, (b) it will be + // expensive to protect against it, and (c) the damage is minimal, + // the race condition is allowed to remain. if (get_cache_ref_count() > 0 && get_ref_count() == get_cache_ref_count() + 1) { - // If we are about to remove the one reference that is not in the - // cache, leaving only references in the cache, then we need to - // check for a cycle involving this TransformState and break it if - // it exists. - if (auto_break_cycles) { - ++_last_cycle_detect; - if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) { - // Ok, we have a cycle. This will be a leak unless we break the - // cycle by freeing the cache on this object. - if (pgraph_cat.is_debug()) { - pgraph_cat.debug() - << "Breaking cycle involving " << (*this) << "\n"; + ReMutexHolder holder(*_states_lock); + + if (get_cache_ref_count() > 0 && + get_ref_count() == get_cache_ref_count() + 1) { + // If we are about to remove the one reference that is not in the + // cache, leaving only references in the cache, then we need to + // check for a cycle involving this TransformState and break it if + // it exists. + + if (auto_break_cycles) { + ++_last_cycle_detect; + if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) { + // Ok, we have a cycle. This will be a leak unless we break the + // cycle by freeing the cache on this object. + if (pgraph_cat.is_debug()) { + pgraph_cat.debug() + << "Breaking cycle involving " << (*this) << "\n"; + } + + ((TransformState *)this)->remove_cache_pointers(); } - - ((TransformState *)this)->remove_cache_pointers(); } } } diff --git a/panda/src/putil/Sources.pp b/panda/src/putil/Sources.pp index cc0b77b9fb..5cfd5b29a6 100644 --- a/panda/src/putil/Sources.pp +++ b/panda/src/putil/Sources.pp @@ -27,6 +27,7 @@ config_util.N config_util.h configurable.h \ datagramInputFile.I datagramInputFile.h \ datagramOutputFile.I datagramOutputFile.h \ + deferredDeletor.h \ drawMask.h \ factoryBase.I factoryBase.h \ factoryParam.I factoryParam.h factoryParams.I \ @@ -47,6 +48,7 @@ nodeCachedReferenceCount.h nodeCachedReferenceCount.I \ nodePointerToBase.h nodePointerToBase.I \ nodePointerTo.h nodePointerTo.I \ + nonDeletor.h \ pta_double.h \ pta_float.h pta_int.h \ string_utils.I string_utils.N string_utils.h \ @@ -72,6 +74,7 @@ clockObject.cxx \ config_util.cxx configurable.cxx \ datagramInputFile.cxx datagramOutputFile.cxx \ + deferredDeletor.cxx \ factoryBase.cxx \ factoryParam.cxx factoryParams.cxx \ globalPointerRegistry.cxx \ @@ -85,6 +88,7 @@ nodeCachedReferenceCount.cxx \ nodePointerToBase.cxx \ nodePointerTo.cxx \ + nonDeletor.cxx \ pta_double.cxx pta_float.cxx \ pta_int.cxx pta_ushort.cxx \ string_utils.cxx timedCycle.cxx typedWritable.cxx \ @@ -112,6 +116,7 @@ config_util.h configurable.h factory.I factory.h \ datagramInputFile.I datagramInputFile.h \ datagramOutputFile.I datagramOutputFile.h \ + deferredDeletor.h \ drawMask.h \ factoryBase.I factoryBase.h factoryParam.I factoryParam.h \ factoryParams.I factoryParams.h \ @@ -132,6 +137,7 @@ nodeCachedReferenceCount.h nodeCachedReferenceCount.I \ nodePointerToBase.h nodePointerToBase.I \ nodePointerTo.h nodePointerTo.I \ + nonDeletor.h \ pta_double.h \ pta_float.h pta_int.h pta_ushort.h \ string_utils.I \ diff --git a/panda/src/putil/cachedTypedWritableReferenceCount.I b/panda/src/putil/cachedTypedWritableReferenceCount.I index 1c23b8d483..274115aa01 100644 --- a/panda/src/putil/cachedTypedWritableReferenceCount.I +++ b/panda/src/putil/cachedTypedWritableReferenceCount.I @@ -234,21 +234,12 @@ INLINE void cache_unref_delete(RefCountType *ptr) { ptr->cache_unref(); if (ptr->get_ref_count() == 0) { -#ifndef NDEBUG - if (get_leak_memory()) { - // In leak-memory mode, we don't actually delete the pointer, - // although we do call the destructor explicitly. This has - // exactly the same effect as deleting it, without actually - // freeing up the memory it uses. - - // Furthermore, if we have never-destruct set, we don't even - // call the destructor. - if (!get_never_destruct()) { - ptr->~RefCountType(); - } + ObjectDeletor *deletor = ObjectDeletor::get_global_ptr(); + if (deletor != (ObjectDeletor *)NULL) { + ptr->ref(); + deletor->delete_object(RefCountDeleteWrapper::do_delete, (void *)ptr); return; } -#endif delete ptr; } } diff --git a/panda/src/putil/cachedTypedWritableReferenceCount.h b/panda/src/putil/cachedTypedWritableReferenceCount.h index fa59f5f59d..2d561fd2d5 100644 --- a/panda/src/putil/cachedTypedWritableReferenceCount.h +++ b/panda/src/putil/cachedTypedWritableReferenceCount.h @@ -22,6 +22,7 @@ #include "pandabase.h" #include "typedWritableReferenceCount.h" +#include "objectDeletor.h" //////////////////////////////////////////////////////////////////// // Class : CachedTypedWritableReferenceCount diff --git a/panda/src/putil/config_util.cxx b/panda/src/putil/config_util.cxx index 70a7962847..fb4239b622 100644 --- a/panda/src/putil/config_util.cxx +++ b/panda/src/putil/config_util.cxx @@ -38,6 +38,8 @@ #include "writableParam.h" #include "keyboardButton.h" #include "mouseButton.h" +#include "deferredDeletor.h" +#include "nonDeletor.h" #include "dconfig.h" @@ -98,6 +100,9 @@ ConfigureFn(config_util) { KeyboardButton::init_keyboard_buttons(); MouseButton::init_mouse_buttons(); + DeferredDeletor::register_deletor(); + NonDeletor::register_deletor(); + register_type(BamReader::_remove_flag, "remove"); } diff --git a/panda/src/putil/deferredDeletor.cxx b/panda/src/putil/deferredDeletor.cxx new file mode 100644 index 0000000000..50ba2b6d35 --- /dev/null +++ b/panda/src/putil/deferredDeletor.cxx @@ -0,0 +1,68 @@ +// Filename: deferredDeletor.cxx +// Created by: drose (10Apr06) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "deferredDeletor.h" +#include "config_util.h" +#include "mutexHolder.h" + +//////////////////////////////////////////////////////////////////// +// Function: DeferredDeletor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DeferredDeletor:: +DeferredDeletor() : _lock("DeferredDeletor") { +} + +//////////////////////////////////////////////////////////////////// +// Function: DeferredDeletor::delete_object +// Access: Public, Virtual +// Description: Adds the pointer to the object to be deleted, along +// with a pointer to a function that can delete it. +//////////////////////////////////////////////////////////////////// +void DeferredDeletor:: +delete_object(DeleteFunc *func, void *ptr) { + MutexHolder holder(_lock); + _tokens.push_back(DeleteToken(func, ptr)); +} + +//////////////////////////////////////////////////////////////////// +// Function: DeferredDeletor::flush +// Access: Public, Virtual +// Description: Ensures that any objects queued up for deletion have +// been fully deleted by the time flush() returns. +//////////////////////////////////////////////////////////////////// +void DeferredDeletor:: +flush() { + MutexHolder holder(_lock); + Tokens::iterator ti; + for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) { + (*ti).do_delete(); + } + _tokens.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DeferredDeletor::register_deletor +// Access: Public, Static +// Description: Registers this deletor with the global pool. +//////////////////////////////////////////////////////////////////// +void DeferredDeletor:: +register_deletor() { + register_subclass(new DeferredDeletor, "deferred"); +} diff --git a/panda/src/putil/deferredDeletor.h b/panda/src/putil/deferredDeletor.h new file mode 100644 index 0000000000..1233f67f77 --- /dev/null +++ b/panda/src/putil/deferredDeletor.h @@ -0,0 +1,48 @@ +// Filename: deferredDeletor.h +// Created by: drose (10Apr06) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef DEFERREDDELETOR_H +#define DEFERREDDELETOR_H + +#include "pandabase.h" +#include "objectDeletor.h" +#include "pvector.h" +#include "pmutex.h" + +//////////////////////////////////////////////////////////////////// +// Class : DeferredDeletor +// Description : The DeferredDeletor does all its deleting between +// frames, where it can be observed by PStats and where +// it won't interfere with rendering. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS DeferredDeletor : public ObjectDeletor { +public: + DeferredDeletor(); + + virtual void delete_object(DeleteFunc *func, void *ptr); + virtual void flush(); + + static void register_deletor(); + +private: + typedef pvector Tokens; + Tokens _tokens; + Mutex _lock; +}; + +#endif diff --git a/panda/src/putil/nodeCachedReferenceCount.I b/panda/src/putil/nodeCachedReferenceCount.I index c28a6ccda8..e16c8f50a4 100644 --- a/panda/src/putil/nodeCachedReferenceCount.I +++ b/panda/src/putil/nodeCachedReferenceCount.I @@ -258,21 +258,13 @@ INLINE void node_unref_delete(RefCountType *ptr) { ptr->node_unref(); if (ptr->get_ref_count() == 0) { -#ifndef NDEBUG - if (get_leak_memory()) { - // In leak-memory mode, we don't actually delete the pointer, - // although we do call the destructor explicitly. This has - // exactly the same effect as deleting it, without actually - // freeing up the memory it uses. - - // Furthermore, if we have never-destruct set, we don't even - // call the destructor. - if (!get_never_destruct()) { - ptr->~RefCountType(); - } + ObjectDeletor *deletor = ObjectDeletor::get_global_ptr(); + if (deletor != (ObjectDeletor *)NULL) { + ptr->ref(); + deletor->delete_object(RefCountDeleteWrapper::do_delete, (void *)ptr); return; } -#endif + delete ptr; } } diff --git a/panda/src/putil/nodeCachedReferenceCount.h b/panda/src/putil/nodeCachedReferenceCount.h index 9d8f7d8901..e7177af6d4 100644 --- a/panda/src/putil/nodeCachedReferenceCount.h +++ b/panda/src/putil/nodeCachedReferenceCount.h @@ -22,6 +22,7 @@ #include "pandabase.h" #include "cachedTypedWritableReferenceCount.h" +#include "objectDeletor.h" //////////////////////////////////////////////////////////////////// // Class : NodeCachedReferenceCount diff --git a/panda/src/putil/nonDeletor.cxx b/panda/src/putil/nonDeletor.cxx new file mode 100644 index 0000000000..9e60c4d33e --- /dev/null +++ b/panda/src/putil/nonDeletor.cxx @@ -0,0 +1,44 @@ +// Filename: nonDeletor.cxx +// Created by: drose (10Apr06) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "nonDeletor.h" +#include "config_util.h" + +//////////////////////////////////////////////////////////////////// +// Function: NonDeletor::delete_object +// Access: Public, Virtual +// Description: Adds the pointer to the object to be deleted, along +// with a pointer to a function that can delete it. +//////////////////////////////////////////////////////////////////// +void NonDeletor:: +delete_object(DeleteFunc *, void *ptr) { + if (util_cat.is_spam()) { + util_cat.spam() + << "Not deleting pointer " << ptr << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NonDeletor::register_deletor +// Access: Public, Static +// Description: Registers this deletor with the global pool. +//////////////////////////////////////////////////////////////////// +void NonDeletor:: +register_deletor() { + register_subclass(new NonDeletor, "none"); +} diff --git a/panda/src/putil/nonDeletor.h b/panda/src/putil/nonDeletor.h new file mode 100644 index 0000000000..1f951b2892 --- /dev/null +++ b/panda/src/putil/nonDeletor.h @@ -0,0 +1,40 @@ +// Filename: nonDeletor.h +// Created by: drose (10Apr06) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef NONDELETOR_H +#define NONDELETOR_H + +#include "pandabase.h" +#include "objectDeletor.h" + +//////////////////////////////////////////////////////////////////// +// Class : NonDeletor +// Description : This specialization of ObjectDeletor serves a very +// specific function: it *doesn't* delete pointers it is +// given. This is useful mainly for testing, for +// instance to determine if there is a problem with a +// destructor somewhere. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDAEXPRESS NonDeletor : public ObjectDeletor { +public: + virtual void delete_object(DeleteFunc *func, void *ptr); + + static void register_deletor(); +}; + +#endif diff --git a/panda/src/putil/putil_composite1.cxx b/panda/src/putil/putil_composite1.cxx index bf11ad29b2..45f1f9438e 100644 --- a/panda/src/putil/putil_composite1.cxx +++ b/panda/src/putil/putil_composite1.cxx @@ -14,6 +14,7 @@ #include "configurable.cxx" #include "datagramInputFile.cxx" #include "datagramOutputFile.cxx" +#include "deferredDeletor.cxx" #include "factoryBase.cxx" #include "factoryParam.cxx" #include "factoryParams.cxx" diff --git a/panda/src/putil/putil_composite2.cxx b/panda/src/putil/putil_composite2.cxx index 89efd409c3..604e113687 100644 --- a/panda/src/putil/putil_composite2.cxx +++ b/panda/src/putil/putil_composite2.cxx @@ -5,6 +5,7 @@ #include "nodeCachedReferenceCount.cxx" #include "nodePointerToBase.cxx" #include "nodePointerTo.cxx" +#include "nonDeletor.cxx" #include "pta_double.cxx" #include "pta_float.cxx" #include "pta_int.cxx"