Browse Source

better thread protection in reference counts, esp. TransformState etc.

David Rose 18 years ago
parent
commit
68cd3322a9
41 changed files with 604 additions and 899 deletions
  1. 6 0
      dtool/src/dtoolbase/memoryHook.cxx
  2. 11 9
      panda/src/display/graphicsEngine.cxx
  3. 0 3
      panda/src/express/Sources.pp
  4. 0 1
      panda/src/express/express_composite1.cxx
  5. 29 47
      panda/src/express/nodeReferenceCount.I
  6. 2 0
      panda/src/express/nodeReferenceCount.h
  7. 0 62
      panda/src/express/objectDeletor.I
  8. 0 85
      panda/src/express/objectDeletor.cxx
  9. 0 72
      panda/src/express/objectDeletor.h
  10. 2 85
      panda/src/express/referenceCount.I
  11. 0 15
      panda/src/express/referenceCount.h
  12. 38 9
      panda/src/gobj/geomVertexArrayFormat.cxx
  13. 4 0
      panda/src/gobj/geomVertexArrayFormat.h
  14. 44 7
      panda/src/gobj/geomVertexFormat.cxx
  15. 6 1
      panda/src/gobj/geomVertexFormat.h
  16. 39 4
      panda/src/gobj/internalName.cxx
  17. 4 0
      panda/src/gobj/internalName.h
  18. 84 24
      panda/src/pgraph/renderAttrib.cxx
  19. 12 0
      panda/src/pgraph/renderAttrib.h
  20. 79 4
      panda/src/pgraph/renderEffects.cxx
  21. 5 0
      panda/src/pgraph/renderEffects.h
  22. 29 4
      panda/src/pgraph/renderState.I
  23. 56 41
      panda/src/pgraph/renderState.cxx
  24. 4 0
      panda/src/pgraph/renderState.h
  25. 29 4
      panda/src/pgraph/transformState.I
  26. 59 43
      panda/src/pgraph/transformState.cxx
  27. 4 0
      panda/src/pgraph/transformState.h
  28. 0 9
      panda/src/putil/Sources.pp
  29. 30 38
      panda/src/putil/cachedTypedWritableReferenceCount.I
  30. 1 0
      panda/src/putil/cachedTypedWritableReferenceCount.h
  31. 0 7
      panda/src/putil/config_util.cxx
  32. 0 76
      panda/src/putil/deferredDeletor.cxx
  33. 0 48
      panda/src/putil/deferredDeletor.h
  34. 26 30
      panda/src/putil/nodeCachedReferenceCount.I
  35. 1 0
      panda/src/putil/nodeCachedReferenceCount.h
  36. 0 44
      panda/src/putil/nonDeletor.cxx
  37. 0 40
      panda/src/putil/nonDeletor.h
  38. 0 1
      panda/src/putil/putil_composite1.cxx
  39. 0 2
      panda/src/putil/putil_composite2.cxx
  40. 0 45
      panda/src/putil/spamDeletor.cxx
  41. 0 39
      panda/src/putil/spamDeletor.h

+ 6 - 0
dtool/src/dtoolbase/memoryHook.cxx

@@ -51,6 +51,9 @@
 
 #define USE_DL_PREFIX 1
 #define NO_MALLINFO 1
+#ifdef _DEBUG
+  #define DEBUG
+#endif
 #include "dlmalloc.h"
 #include "dlmalloc_src.cxx"
 
@@ -77,6 +80,9 @@
 
 #define USE_DL_PREFIX 1
 #define NO_MALLINFO 1
+#ifdef _DEBUG
+  #define MALLOC_DEBUG 2
+#endif
 #include "ptmalloc2_smp_src.cxx"
 
 #define call_malloc dlmalloc

+ 11 - 9
panda/src/display/graphicsEngine.cxx

@@ -175,6 +175,15 @@ GraphicsEngine::
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 set_threading_model(const GraphicsThreadingModel &threading_model) {
+  if (!Thread::is_threading_supported()) {
+    if (!threading_model.is_single_threaded()) {
+      display_cat.warning()
+        << "Threading model " << threading_model
+        << " requested but threading is not available.  Ignoring.\n";
+      return;
+    }
+  }
+    
 #ifndef THREADED_PIPELINE
   if (!threading_model.is_single_threaded()) {
     display_cat.warning()
@@ -688,14 +697,6 @@ 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, current_thread);
-    deletor->flush();
-  }
-
   GeomCacheManager::flush_level();
   CullTraverser::flush_level();
   RenderState::flush_level();
@@ -1960,7 +1961,8 @@ get_window_renderer(const string &name, int pipeline_stage) {
   thread->set_min_pipeline_stage(pipeline_stage);
   _pipeline->set_min_stages(pipeline_stage + 1);
 
-  thread->start(TP_normal, true);
+  bool started = thread->start(TP_normal, true);
+  nassertr(started, thread.p());
   _threads[name] = thread;
 
   nassertr(thread->get_pipeline_stage() < _pipeline->get_num_stages(), thread.p());

+ 0 - 3
panda/src/express/Sources.pp

@@ -32,7 +32,6 @@
     nodePointerToBase.h nodePointerToBase.I \
     nodePointerTo.h nodePointerTo.I \
     nodeReferenceCount.h nodeReferenceCount.I \
-    objectDeletor.h objectDeletor.I \
     ordered_vector.h ordered_vector.I ordered_vector.T \
     password_hash.h \
     patchfile.I patchfile.h \
@@ -84,7 +83,6 @@
     nodePointerToBase.cxx \
     nodePointerTo.cxx \
     nodeReferenceCount.cxx \
-    objectDeletor.cxx \
     ordered_vector.cxx \
     password_hash.cxx \
     patchfile.cxx \
@@ -142,7 +140,6 @@
     nodePointerToBase.h nodePointerToBase.I \
     nodePointerTo.h nodePointerTo.I \
     nodeReferenceCount.h nodeReferenceCount.I \
-    objectDeletor.h objectDeletor.I \
     ordered_vector.h ordered_vector.I ordered_vector.T \
     password_hash.h \
     patchfile.I patchfile.h \

+ 0 - 1
panda/src/express/express_composite1.cxx

@@ -19,7 +19,6 @@
 #include "nodePointerToBase.cxx"
 #include "nodePointerTo.cxx"
 #include "nodeReferenceCount.cxx"
-#include "objectDeletor.cxx"
 #include "ordered_vector.cxx"
 #include "patchfile.cxx"
 #include "password_hash.cxx"

+ 29 - 47
panda/src/express/nodeReferenceCount.I

@@ -147,14 +147,8 @@ get_node_ref_count() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeReferenceCount::node_ref
 //       Access: Published
-//  Description: Explicitly increments the reference count.
-//
-//               This function is const, even though it changes the
-//               object, because generally fiddling with an object's
-//               reference count isn't considered part of fiddling
-//               with the object.  An object might be const in other
-//               ways, but we still need to accurately count the
-//               number of references to it.
+//  Description: Explicitly increments the node reference count and
+//               the normal reference count simultaneously.
 ////////////////////////////////////////////////////////////////////
 INLINE void NodeReferenceCount::
 node_ref() const {
@@ -169,42 +163,16 @@ node_ref() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeReferenceCount::node_unref
 //       Access: Published
-//  Description: Explicitly decrements the reference count.  Note that
-//               the object will not be implicitly deleted by unref()
-//               simply because the reference count drops to zero.
-//               (Having a member function delete itself is
-//               problematic; plus, we don't have a virtual destructor
-//               anyway.) However, see the helper function
-//               unref_delete().
-//
-//               User code should avoid using ref() and unref()
-//               directly, which can result in missed reference
-//               counts.  Instead, let a PointerTo object manage the
-//               reference counting automatically.
-//
-//               This function is const, even though it changes the
-//               object, because generally fiddling with an object's
-//               reference count isn't considered part of fiddling
-//               with the object.  An object might be const in other
-//               ways, but we still need to accurately count the
-//               number of references to it.
+//  Description: Explicitly decrements the node reference count and
+//               the normal reference count simultaneously.
 //
 //               The return value is true if the new reference count
 //               is nonzero, false if it is zero.
 ////////////////////////////////////////////////////////////////////
 INLINE bool NodeReferenceCount::
 node_unref() const {
-#ifdef _DEBUG
-  nassertr(test_ref_count_integrity(), 0);
-#endif
-
-  // If this assertion fails, you tried to unref an object with a
-  // zero reference count.  Are you using ref() and unref()
-  // directly?  Are you sure you can't use PointerTo's?
-  nassertr(_node_ref_count > 0, 0);
-
-  unref();
-  return AtomicAdjust::dec(((NodeReferenceCount *)this)->_node_ref_count);
+  node_unref_only();
+  return unref();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -222,6 +190,28 @@ test_ref_count_integrity() const {
 #endif
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodeReferenceCount::node_unref_only
+//       Access: Protected
+//  Description: Decrements the node reference count without affecting
+//               the normal reference count.  Intended to be called by
+//               derived classes only, presumably to reimplement
+//               node_unref().
+////////////////////////////////////////////////////////////////////
+INLINE void NodeReferenceCount::
+node_unref_only() const {
+#ifdef _DEBUG
+  nassertv(test_ref_count_integrity());
+#endif
+
+  // If this assertion fails, you tried to unref an object with a
+  // zero reference count.  Are you using ref() and unref()
+  // directly?  Are you sure you can't use PointerTo's?
+  nassertv(_node_ref_count > 0);
+
+  AtomicAdjust::dec(((NodeReferenceCount *)this)->_node_ref_count);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: node_unref_delete
 //  Description: This global helper function will unref the given
@@ -235,15 +225,7 @@ test_ref_count_integrity() const {
 template<class RefCountType>
 INLINE void
 node_unref_delete(RefCountType *ptr) {
-  ptr->node_unref();
-  if (ptr->get_ref_count() == 0) {
-    ObjectDeletor *deletor = ObjectDeletor::get_global_ptr();
-    if (deletor != (ObjectDeletor *)NULL) {
-      ptr->ref();
-      deletor->delete_object(RefCountDeleteWrapper<RefCountType>::do_delete, (void *)ptr);
-      return;
-    }
-
+  if (!ptr->node_unref()) {
     delete ptr;
   }
 }

+ 2 - 0
panda/src/express/nodeReferenceCount.h

@@ -57,6 +57,8 @@ PUBLISHED:
   INLINE bool test_ref_count_integrity() const;
 
 protected:
+  INLINE void node_unref_only() const;
+
   bool do_test_ref_count_integrity() const;
   
 private:

+ 0 - 62
panda/src/express/objectDeletor.I

@@ -1,62 +0,0 @@
-// 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
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-
-////////////////////////////////////////////////////////////////////
-//     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);
-}

+ 0 - 85
panda/src/express/objectDeletor.cxx

@@ -1,85 +0,0 @@
-// 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
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include "objectDeletor.h"
-#include "configVariableString.h"
-#include "config_express.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;
-  }
-}

+ 0 - 72
panda/src/express/objectDeletor.h

@@ -1,72 +0,0 @@
-// 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
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#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

+ 2 - 85
panda/src/express/referenceCount.I

@@ -348,41 +348,6 @@ 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<class ObjectType>
-void DeleteWrapper<ObjectType>::
-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<class RefCountType>
-void RefCountDeleteWrapper<RefCountType>::
-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
@@ -404,59 +369,11 @@ unref_delete(RefCountType *ptr) {
   // overloading of the unref() method.
 
   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<RefCountType>::do_delete, (void *)ptr);
-      return;
-    }
-
-    // If there's no deletor, just delete the pointer now.
+    // If the reference count has gone to zero, delete the object.
     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<class ObjectType>
-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<ObjectType>::do_delete, (void *)ptr);
-    return;
-  }
-
-  // If there's no deletor, just delete the pointer now.
-  delete ptr;
-}
-
-
 ////////////////////////////////////////////////////////////////////
 //     Function: RefCountProxy::Constructor
 //       Access: Public

+ 0 - 15
panda/src/express/referenceCount.h

@@ -105,24 +105,9 @@ private:
   static TypeHandle _type_handle;
 };
 
-template<class ObjectType>
-class DeleteWrapper {
-public:
-  static void do_delete(void *ptr);
-};
-
-template<class RefCountType>
-class RefCountDeleteWrapper {
-public:
-  static void do_delete(void *ptr);
-};
-
 template<class RefCountType>
 INLINE void unref_delete(RefCountType *ptr);
 
-template<class ObjectType>
-INLINE void defer_delete(ObjectType *ptr);
-
 ////////////////////////////////////////////////////////////////////
 //       Class : RefCountProxy
 // Description : A "proxy" to use to make a reference-countable object

+ 38 - 9
panda/src/gobj/geomVertexArrayFormat.cxx

@@ -24,6 +24,7 @@
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "indirectLess.h"
+#include "mutexHolder.h"
 
 GeomVertexArrayFormat::Registry *GeomVertexArrayFormat::_registry = NULL;
 TypeHandle GeomVertexArrayFormat::_type_handle;
@@ -186,15 +187,38 @@ operator = (const GeomVertexArrayFormat &copy) {
 ////////////////////////////////////////////////////////////////////
 GeomVertexArrayFormat::
 ~GeomVertexArrayFormat() {
-  if (is_registered()) {
-    get_registry()->unregister_format(this);
-  }
+  // unref() should have unregistered us.
+  nassertv(!is_registered());
+
   Columns::iterator ci;
   for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
     delete (*ci);
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayFormat::unref
+//       Access: Published
+//  Description: This method overrides ReferenceCount::unref() to
+//               unregister the object when its reference count goes
+//               to zero.
+////////////////////////////////////////////////////////////////////
+bool GeomVertexArrayFormat::
+unref() const {
+  Registry *registry = get_registry();
+  MutexHolder holder(registry->_lock);
+
+  if (ReferenceCount::unref()) {
+    return true;
+  }
+
+  if (is_registered()) {
+    registry->unregister_format((GeomVertexArrayFormat *)this);
+  }
+
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexArrayFormat::add_column
 //       Access: Published
@@ -759,12 +783,15 @@ register_format(GeomVertexArrayFormat *format) {
   // a zero reference count and is not added into the map below, it
   // will be automatically deleted when this function returns.
   PT(GeomVertexArrayFormat) pt_format = format;
-
-  ArrayFormats::iterator fi = _formats.insert(format).first;
-
-  GeomVertexArrayFormat *new_format = (*fi);
-  if (!new_format->is_registered()) {
-    new_format->do_register();
+  
+  GeomVertexArrayFormat *new_format;
+  {
+    MutexHolder holder(_lock);
+    ArrayFormats::iterator fi = _formats.insert(format).first;
+    new_format = (*fi);
+    if (!new_format->is_registered()) {
+      new_format->do_register();
+    }
   }
 
   return new_format;
@@ -776,6 +803,8 @@ register_format(GeomVertexArrayFormat *format) {
 //  Description: Removes the indicated format from the registry.
 //               Normally this should not be done until the format is
 //               destructing.
+//
+//               The lock should be held prior to calling this method.
 ////////////////////////////////////////////////////////////////////
 void GeomVertexArrayFormat::Registry::
 unregister_format(GeomVertexArrayFormat *format) {

+ 4 - 0
panda/src/gobj/geomVertexArrayFormat.h

@@ -26,6 +26,7 @@
 #include "indirectCompareTo.h"
 #include "pvector.h"
 #include "pmap.h"
+#include "pmutex.h"
 
 class GeomVertexFormat;
 class GeomVertexData;
@@ -80,6 +81,8 @@ PUBLISHED:
   void operator = (const GeomVertexArrayFormat &copy);
   ~GeomVertexArrayFormat();
 
+  bool unref() const;
+
   INLINE bool is_registered() const;
   INLINE static CPT(GeomVertexArrayFormat) register_format(const GeomVertexArrayFormat *format);
 
@@ -147,6 +150,7 @@ private:
     void unregister_format(GeomVertexArrayFormat *format);
 
     ArrayFormats _formats;
+    Mutex _lock;
   };
 
   static Registry *_registry;

+ 44 - 7
panda/src/gobj/geomVertexFormat.cxx

@@ -19,7 +19,7 @@
 #include "geomVertexFormat.h"
 #include "geomVertexData.h"
 #include "geomMunger.h"
-#include "mutexHolder.h"
+#include "reMutexHolder.h"
 #include "indent.h"
 #include "bamReader.h"
 #include "bamWriter.h"
@@ -86,9 +86,31 @@ operator = (const GeomVertexFormat &copy) {
 ////////////////////////////////////////////////////////////////////
 GeomVertexFormat::
 ~GeomVertexFormat() {
+  // unref() should have unregistered us.
+  nassertv(!is_registered());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexFormat::unref
+//       Access: Published
+//  Description: This method overrides ReferenceCount::unref() to
+//               unregister the object when its reference count goes
+//               to zero.
+////////////////////////////////////////////////////////////////////
+bool GeomVertexFormat::
+unref() const {
+  Registry *registry = get_registry();
+  ReMutexHolder holder(registry->_lock);
+
+  if (ReferenceCount::unref()) {
+    return true;
+  }
+
   if (is_registered()) {
-    get_registry()->unregister_format(this);
+    registry->unregister_format((GeomVertexFormat *)this);
   }
+
+  return false;
 }
 
 
@@ -669,6 +691,7 @@ void GeomVertexFormat::
 make_registry() {
   if (_registry == (Registry *)NULL) {
     _registry = new Registry;
+    _registry->make_standard_formats();
   }
 }
  
@@ -897,6 +920,15 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 GeomVertexFormat::Registry::
 Registry() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexFormat::Registry::make_standard_formats
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void GeomVertexFormat::Registry::
+make_standard_formats() {
   _v3 = register_format(new GeomVertexArrayFormat
                         (InternalName::get_vertex(), 3, 
                          NT_float32, C_point));
@@ -1016,11 +1048,14 @@ register_format(GeomVertexFormat *format) {
   // will be automatically deleted when this function returns.
   PT(GeomVertexFormat) pt_format = format;
 
-  Formats::iterator fi = _formats.insert(format).first;
-
-  GeomVertexFormat *new_format = (*fi);
-  if (!new_format->is_registered()) {
-    new_format->do_register();
+  GeomVertexFormat *new_format;
+  {
+    ReMutexHolder holder(_lock);
+    Formats::iterator fi = _formats.insert(format).first;
+    new_format = (*fi);
+    if (!new_format->is_registered()) {
+      new_format->do_register();
+    }
   }
 
   return new_format;
@@ -1032,6 +1067,8 @@ register_format(GeomVertexFormat *format) {
 //  Description: Removes the indicated format from the registry.
 //               Normally this should not be done until the format is
 //               destructing.
+//
+//               The lock should be held prior to calling this method.
 ////////////////////////////////////////////////////////////////////
 void GeomVertexFormat::Registry::
 unregister_format(GeomVertexFormat *format) {

+ 6 - 1
panda/src/gobj/geomVertexFormat.h

@@ -31,7 +31,7 @@
 #include "pset.h"
 #include "pvector.h"
 #include "indirectCompareTo.h"
-#include "pmutex.h"
+#include "reMutex.h"
 
 class FactoryParams;
 class GeomVertexData;
@@ -70,6 +70,8 @@ PUBLISHED:
   void operator = (const GeomVertexFormat &copy);
   virtual ~GeomVertexFormat();
 
+  bool unref() const;
+
   INLINE bool is_registered() const;
   INLINE static CPT(GeomVertexFormat) register_format(const GeomVertexFormat *format);
   INLINE static CPT(GeomVertexFormat) register_format(const GeomVertexArrayFormat *format);
@@ -213,11 +215,14 @@ private:
   class EXPCL_PANDA Registry {
   public:
     Registry();
+    void make_standard_formats();
+
     CPT(GeomVertexFormat) register_format(GeomVertexFormat *format);
     INLINE CPT(GeomVertexFormat) register_format(GeomVertexArrayFormat *format);
     void unregister_format(GeomVertexFormat *format);
 
     Formats _formats;
+    ReMutex _lock;
 
     CPT(GeomVertexFormat) _v3;
     CPT(GeomVertexFormat) _v3n3;

+ 39 - 4
panda/src/gobj/internalName.cxx

@@ -66,11 +66,44 @@ InternalName(InternalName *parent, const string &basename) :
 ////////////////////////////////////////////////////////////////////
 InternalName::
 ~InternalName() {
-  if (_parent != (const InternalName *)NULL) {
+#ifndef NDEBUG
+  {
+    // unref() should have removed us from our parent's table already.
+    MutexHolder holder(_parent->_name_table_lock);
     NameTable::iterator ni = _parent->_name_table.find(_basename);
-    nassertv(ni != _parent->_name_table.end());
-    _parent->_name_table.erase(ni);
+    nassertv(ni == _parent->_name_table.end());
   }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InternalName::unref
+//       Access: Published
+//  Description: This method overrides ReferenceCount::unref() to
+//               clear the pointer from its parent's table when
+//               its reference count goes to zero.
+////////////////////////////////////////////////////////////////////
+bool InternalName::
+unref() const {
+  if (_parent == (const InternalName *)NULL) {
+    // No parent; no problem.  This is the root InternalName.
+    // Actually, this probably shouldn't be destructing, but I guess
+    // it might at application shutdown.
+    return TypedWritableReferenceCount::unref();
+  }
+
+  MutexHolder holder(_parent->_name_table_lock);
+
+  if (ReferenceCount::unref()) {
+    return true;
+  }
+
+  // The reference count has just reached zero.
+  NameTable::iterator ni = _parent->_name_table.find(_basename);
+  nassertr(ni != _parent->_name_table.end(), false);
+  _parent->_name_table.erase(ni);
+
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -94,12 +127,14 @@ append(const string &name) {
     return append(name.substr(0, dot))->append(name.substr(dot + 1));
   }
 
+  MutexHolder holder(_name_table_lock);
+
   NameTable::iterator ni = _name_table.find(name);
   if (ni != _name_table.end()) {
     return (*ni).second;
   }
 
-  PT(InternalName) internal_name = new InternalName(this, name);
+  InternalName *internal_name = new InternalName(this, name);
   _name_table[name] = internal_name;
   return internal_name;
 }

+ 4 - 0
panda/src/gobj/internalName.h

@@ -24,6 +24,7 @@
 #include "typedWritableReferenceCount.h"
 #include "pointerTo.h"
 #include "pmap.h"
+#include "pmutex.h"
 
 class FactoryParams;
 
@@ -48,6 +49,8 @@ private:
 
 PUBLISHED:
   virtual ~InternalName();
+  bool unref() const;
+
   INLINE static PT(InternalName) make(const string &name);
   PT(InternalName) append(const string &basename);
 
@@ -93,6 +96,7 @@ private:
 
   typedef phash_map<string, InternalName *, string_hash> NameTable;
   NameTable _name_table;
+  Mutex _name_table_lock;
 
   static PT(InternalName) _root;
   static PT(InternalName) _error;

+ 84 - 24
panda/src/pgraph/renderAttrib.cxx

@@ -21,7 +21,9 @@
 #include "bamReader.h"
 #include "indent.h"
 #include "config_pgraph.h"
+#include "reMutexHolder.h"
 
+ReMutex *RenderAttrib::_attribs_lock = NULL;
 RenderAttrib::Attribs *RenderAttrib::_attribs = NULL;
 TypeHandle RenderAttrib::_type_handle;
 
@@ -33,12 +35,7 @@ TypeHandle RenderAttrib::_type_handle;
 RenderAttrib::
 RenderAttrib() {
   if (_attribs == (Attribs *)NULL) {
-    // Make sure the global _attribs map is allocated.  This only has
-    // to be done once.  We could make this map static, but then we
-    // run into problems if anyone creates a RenderState object at
-    // static init time; it also seems to cause problems when the
-    // Panda shared library is unloaded at application exit time.
-    _attribs = new Attribs;
+    init_attribs();
   }
   _saved_entry = _attribs->end();
 
@@ -73,24 +70,10 @@ operator = (const RenderAttrib &) {
 ////////////////////////////////////////////////////////////////////
 RenderAttrib::
 ~RenderAttrib() {
-  if (_saved_entry != _attribs->end()) {
-    // We cannot make this assertion, because the RenderAttrib has
-    // already partially destructed--this means we cannot look up the
-    // object in the map.  In fact, the map is temporarily invalid
-    // until we finish destructing, since we screwed up the ordering
-    // when we changed the return value of get_type().
-    //    nassertv(_attribs->find(this) == _saved_entry);
-
-    // Note: this isn't thread-safe, because once the derived class
-    // destructor exits and before this destructor completes, the map
-    // is invalid, and other threads may inadvertently attempt to read
-    // the invalid map.  To make it thread-safe, we need to move this
-    // functionality to a separate method, that is to be called from
-    // *each* derived class's destructor (and then we can put the
-    // above assert back in).
-    _attribs->erase(_saved_entry);
-    _saved_entry = _attribs->end();
-  }
+  ReMutexHolder holder(*_attribs_lock);
+
+  // unref() should have cleared this.
+  nassertv(_saved_entry == _attribs->end());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -157,6 +140,32 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderAttrib::unref
+//       Access: Published
+//  Description: This method overrides ReferenceCount::unref() to
+//               clear the pointer from the global object pool when
+//               its reference count goes to zero.
+////////////////////////////////////////////////////////////////////
+bool RenderAttrib::
+unref() const {
+  // We always have to grab the lock, since we will definitely need to
+  // be holding it if we happen to drop the reference count to 0.
+  ReMutexHolder holder(*_attribs_lock);
+
+  if (ReferenceCount::unref()) {
+    // The reference count is still nonzero.
+    return true;
+  }
+
+  // The reference count has just reached zero.  Make sure the object
+  // is removed from the global object pool, before anyone else finds
+  // it and tries to ref it.
+  ((RenderAttrib *)this)->release_new();
+
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::output
 //       Access: Published, Virtual
@@ -186,6 +195,8 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 int RenderAttrib::
 get_num_attribs() {
+  ReMutexHolder holder(*_attribs_lock);
+
   if (_attribs == (Attribs *)NULL) {
     return 0;
   }
@@ -201,6 +212,8 @@ get_num_attribs() {
 ////////////////////////////////////////////////////////////////////
 void RenderAttrib::
 list_attribs(ostream &out) {
+  ReMutexHolder holder(*_attribs_lock);
+
   out << _attribs->size() << " attribs:\n";
   Attribs::const_iterator si;
   for (si = _attribs->begin(); si != _attribs->end(); ++si) {
@@ -219,6 +232,8 @@ list_attribs(ostream &out) {
 ////////////////////////////////////////////////////////////////////
 bool RenderAttrib::
 validate_attribs() {
+  ReMutexHolder holder(*_attribs_lock);
+
   if (_attribs->empty()) {
     return true;
   }
@@ -263,6 +278,8 @@ return_new(RenderAttrib *attrib) {
     return attrib;
   }
 
+  ReMutexHolder holder(*_attribs_lock);
+
   // This should be a newly allocated pointer, not one that was used
   // for anything else.
   nassertr(attrib->_saved_entry == _attribs->end(), attrib);
@@ -399,6 +416,26 @@ output_comparefunc(ostream &out, PandaCompareFunc fn) const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderAttrib::release_new
+//       Access: Private
+//  Description: This inverse of return_new, this releases this object
+//               from the global RenderAttrib table.
+//
+//               You must already be holding _attribs_lock before you
+//               call this method.
+////////////////////////////////////////////////////////////////////
+void RenderAttrib::
+release_new() {
+  nassertv(_attribs_lock->debug_is_locked());
+
+  if (_saved_entry != _attribs->end()) {
+    nassertv(_attribs->find(this) == _saved_entry);
+    _attribs->erase(_saved_entry);
+    _saved_entry = _attribs->end();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::make_default_impl
 //       Access: Protected, Virtual
@@ -415,6 +452,29 @@ make_default_impl() const {
   return (RenderAttrib *)NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderAttrib::init_attribs
+//       Access: Public, Static
+//  Description: Make sure the global _attribs map is allocated.  This
+//               only has to be done once.  We could make this map
+//               static, but then we run into problems if anyone
+//               creates a RenderAttrib object at static init time;
+//               it also seems to cause problems when the Panda shared
+//               library is unloaded at application exit time.
+////////////////////////////////////////////////////////////////////
+void RenderAttrib::
+init_attribs() {
+  _attribs = new Attribs;
+
+  // TODO: we should have a global Panda mutex to allow us to safely
+  // create _attribs_lock without a startup race condition.  For the
+  // meantime, this is OK because we guarantee that this method is
+  // called at static init time, presumably when there is still only
+  // one thread in the world.
+  _attribs_lock = new ReMutex("RenderAttrib::_attribs_lock");
+  nassertv(Thread::get_current_thread() == Thread::get_main_thread());
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::write_datagram
 //       Access: Public, Virtual

+ 12 - 0
panda/src/pgraph/renderAttrib.h

@@ -24,6 +24,7 @@
 #include "typedWritableReferenceCount.h"
 #include "pointerTo.h"
 #include "pset.h"
+#include "reMutex.h"
 
 class AttribSlots;
 class GraphicsStateGuardianBase;
@@ -80,6 +81,9 @@ public:
 
 PUBLISHED:
   INLINE int compare_to(const RenderAttrib &other) const;
+
+  bool unref() const;
+
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level) const;
 
@@ -179,10 +183,18 @@ protected:
   virtual RenderAttrib *make_default_impl() const=0;
   void output_comparefunc(ostream &out, PandaCompareFunc fn) const;
 
+private:
+  void release_new();
+
 protected:
   bool _always_reissue;
 
+public:
+  static void init_attribs();
+
 private:
+  // This mutex protects _attribs.
+  static ReMutex *_attribs_lock;
   typedef pset<const RenderAttrib *, indirect_compare_to<const RenderAttrib *> > Attribs;
   static Attribs *_attribs;
 

+ 79 - 4
panda/src/pgraph/renderEffects.cxx

@@ -83,10 +83,9 @@ RenderEffects::
 ~RenderEffects() {
   // Remove the deleted RenderEffects object from the global pool.
   ReMutexHolder holder(*_states_lock);
-  if (_saved_entry != _states->end()) {
-    _states->erase(_saved_entry);
-    _saved_entry = _states->end();
-  }
+
+  // unref() should have cleared this.
+  nassertv(_saved_entry == _states->end());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -385,6 +384,40 @@ get_effect(TypeHandle type) const {
   return NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::unref
+//       Access: Published
+//  Description: This method overrides ReferenceCount::unref() to
+//               check whether the remaining reference count is
+//               entirely in the cache, and if so, it checks for and
+//               breaks a cycle in the cache involving this object.
+//               This is designed to prevent leaks from cyclical
+//               references within the cache.
+//
+//               Note that this is not a virtual method, and cannot be
+//               because ReferenceCount itself declares no virtual
+//               methods (it avoids the overhead of a virtual function
+//               pointer).  But this doesn't matter, because
+//               PT(TransformState) is a template class, and will call
+//               the appropriate method even though it is non-virtual.
+////////////////////////////////////////////////////////////////////
+bool RenderEffects::
+unref() const {
+  ReMutexHolder holder(*_states_lock);
+
+  if (ReferenceCount::unref()) {
+    // The reference count is still nonzero.
+    return true;
+  }
+
+  // The reference count has just reached zero.  Make sure the object
+  // is removed from the global object pool, before anyone else finds
+  // it and tries to ref it.
+  ((RenderEffects *)this)->release_new();
+  
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderEffects::output
 //       Access: Published, Virtual
@@ -603,6 +636,26 @@ return_new(RenderEffects *state) {
   return *(result.first);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::release_new
+//       Access: Private
+//  Description: This inverse of return_new, this releases this object
+//               from the global RenderEffects table.
+//
+//               You must already be holding _states_lock before you
+//               call this method.
+////////////////////////////////////////////////////////////////////
+void RenderEffects::
+release_new() {
+  nassertv(_states_lock->debug_is_locked());
+
+  if (_saved_entry != _states->end()) {
+    nassertv(_states->find(this) == _saved_entry);
+    _states->erase(_saved_entry);
+    _saved_entry = _states->end();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderEffects::determine_decal
 //       Access: Private
@@ -759,6 +812,28 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
   return pi;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderEffects::require_fully_complete
+//       Access: Public, Virtual
+//  Description: Some objects require all of their nested pointers to
+//               have been completed before the objects themselves can
+//               be completed.  If this is the case, override this
+//               method to return true, and be careful with circular
+//               references (which would make the object unreadable
+//               from a bam file).
+////////////////////////////////////////////////////////////////////
+bool RenderEffects::
+require_fully_complete() const {
+  // Since we sort _states based on each RenderEffects' operator <
+  // method, which in turn compares based on each nested RenderEffect
+  // object's compare_to() method, some of which depend on the
+  // RenderEffect's pointers having already been completed
+  // (e.g. CharacterJointEffect), we therefore require each of out our
+  // nested RenderEffect objects to have been completed before we can
+  // be completed.
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderEffects::change_this
 //       Access: Public, Static

+ 5 - 0
panda/src/pgraph/renderEffects.h

@@ -89,6 +89,8 @@ PUBLISHED:
 
   const RenderEffect *get_effect(TypeHandle type) const;
 
+  bool unref() const;
+
   void output(ostream &out) const;
   void write(ostream &out, int indent_level) const;
 
@@ -115,6 +117,8 @@ public:
 
 private:
   static CPT(RenderEffects) return_new(RenderEffects *state);
+  void release_new();
+
   void determine_decal();
   void determine_show_bounds();
   void determine_cull_callback();
@@ -174,6 +178,7 @@ public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
   virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+  virtual bool require_fully_complete() const;
   static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
   virtual void finalize(BamReader *manager);
 

+ 29 - 4
panda/src/pgraph/renderState.I

@@ -103,11 +103,11 @@ INLINE bool RenderState::
 cache_unref() const {
 #ifdef DO_PSTATS
   int old_referenced_bits = get_referenced_bits();
-  bool result = NodeCachedReferenceCount::cache_unref();
+  bool result = do_cache_unref();
   consider_update_pstats(old_referenced_bits);
   return result;
 #else  // DO_PSTATS
-  return NodeCachedReferenceCount::cache_unref();
+  return do_cache_unref();
 #endif  // DO_PSTATS
 }
 
@@ -136,11 +136,11 @@ INLINE bool RenderState::
 node_unref() const {
 #ifdef DO_PSTATS
   int old_referenced_bits = get_referenced_bits();
-  bool result = NodeCachedReferenceCount::node_unref();
+  bool result = do_node_unref();
   consider_update_pstats(old_referenced_bits);
   return result;
 #else  // DO_PSTATS
-  return NodeCachedReferenceCount::node_unref();
+  return do_node_unref();
 #endif  // DO_PSTATS
 }
 
@@ -632,6 +632,31 @@ flush_level() {
   _cache_counter.flush_level();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::do_node_unref
+//       Access: Private
+//  Description: Reimplements NodeReferenceCount::node_unref().  We do
+//               this because we have a non-virtual unref() method.
+////////////////////////////////////////////////////////////////////
+INLINE bool RenderState::
+do_node_unref() const {
+  node_unref_only();
+  return unref();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::do_cache_unref
+//       Access: Private
+//  Description: Reimplements
+//               CachedTypedWritableReferenceCount::cache_unref().  We
+//               do this because we have a non-virtual unref() method.
+////////////////////////////////////////////////////////////////////
+INLINE bool RenderState::
+do_cache_unref() const {
+  cache_unref_only();
+  return unref();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::CompositionCycleDescEntry::Constructor
 //       Access: Public

+ 56 - 41
panda/src/pgraph/renderState.cxx

@@ -104,13 +104,10 @@ RenderState::
   set_destructing();
 
   ReMutexHolder holder(*_states_lock);
-  if (_saved_entry != _states->end()) {
-    nassertv(_states->find(this) == _saved_entry);
-    _states->erase(_saved_entry);
-    _saved_entry = _states->end();
-  }
-   
-  remove_cache_pointers();
+
+  // unref() should have cleared these.
+  nassertv(_saved_entry == _states->end());
+  nassertv(_composition_cache.empty() && _invert_composition_cache.empty());
 
   // If this was true at the beginning of the destructor, but is no
   // longer true now, probably we've been double-deleted.
@@ -627,46 +624,44 @@ get_override(TypeHandle type) const {
 ////////////////////////////////////////////////////////////////////
 bool RenderState::
 unref() const {
-  // 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 (auto_break_cycles) {
-      ReMutexHolder holder(*_states_lock);
+  // We always have to grab the lock, since we will definitely need to
+  // be holding it if we happen to drop the reference count to 0.
+  ReMutexHolder holder(*_states_lock);
+
+  if (auto_break_cycles) {
+    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 (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.
-        
-        ++_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();
+      ++_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();
       }
     }
   }
 
-  return ReferenceCount::unref();
+  if (ReferenceCount::unref()) {
+    // The reference count is still nonzero.
+    return true;
+  }
+
+  // The reference count has just reached zero.  Make sure the object
+  // is removed from the global object pool, before anyone else finds
+  // it and tries to ref it.
+  ((RenderState *)this)->release_new();
+  ((RenderState *)this)->remove_cache_pointers();
+
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1371,6 +1366,26 @@ r_detect_cycles(const RenderState *start_state,
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::release_new
+//       Access: Private
+//  Description: This inverse of return_new, this releases this object
+//               from the global RenderState table.
+//
+//               You must already be holding _states_lock before you
+//               call this method.
+////////////////////////////////////////////////////////////////////
+void RenderState::
+release_new() {
+  nassertv(_states_lock->debug_is_locked());
+
+  if (_saved_entry != _states->end()) {
+    nassertv(_states->find(this) == _saved_entry);
+    _states->erase(_saved_entry);
+    _saved_entry = _states->end();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::remove_cache_pointers
 //       Access: Private

+ 4 - 0
panda/src/pgraph/renderState.h

@@ -163,6 +163,9 @@ public:
   INLINE static void flush_level();
 
 private:
+  INLINE bool do_cache_unref() const;
+  INLINE bool do_node_unref() const;
+
   class CompositionCycleDescEntry {
   public:
     INLINE CompositionCycleDescEntry(const RenderState *obj,
@@ -183,6 +186,7 @@ private:
                               int length, UpdateSeq this_seq,
                               CompositionCycleDesc *cycle_desc);
 
+  void release_new();
   void remove_cache_pointers();
 
   void determine_bin_index();

+ 29 - 4
panda/src/pgraph/transformState.I

@@ -733,11 +733,11 @@ INLINE bool TransformState::
 cache_unref() const {
 #ifdef DO_PSTATS
   int old_referenced_bits = get_referenced_bits();
-  bool result = NodeCachedReferenceCount::cache_unref();
+  bool result = do_cache_unref();
   consider_update_pstats(old_referenced_bits);
   return result;
 #else  // DO_PSTATS
-  return NodeCachedReferenceCount::cache_unref();
+  return do_cache_unref();
 #endif  // DO_PSTATS
 }
 
@@ -766,11 +766,11 @@ INLINE bool TransformState::
 node_unref() const {
 #ifdef DO_PSTATS
   int old_referenced_bits = get_referenced_bits();
-  bool result = NodeCachedReferenceCount::node_unref();
+  bool result = do_node_unref();
   consider_update_pstats(old_referenced_bits);
   return result;
 #else  // DO_PSTATS
-  return NodeCachedReferenceCount::node_unref();
+  return do_node_unref();
 #endif  // DO_PSTATS
 }
 
@@ -785,6 +785,31 @@ flush_level() {
   _cache_counter.flush_level();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::do_node_unref
+//       Access: Private
+//  Description: Reimplements NodeReferenceCount::node_unref().  We do
+//               this because we have a non-virtual unref() method.
+////////////////////////////////////////////////////////////////////
+INLINE bool TransformState::
+do_node_unref() const {
+  node_unref_only();
+  return unref();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::do_cache_unref
+//       Access: Private
+//  Description: Reimplements
+//               CachedTypedWritableReferenceCount::cache_unref().  We
+//               do this because we have a non-virtual unref() method.
+////////////////////////////////////////////////////////////////////
+INLINE bool TransformState::
+do_cache_unref() const {
+  cache_unref_only();
+  return unref();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformState::check_hash
 //       Access: Private

+ 59 - 43
panda/src/pgraph/transformState.cxx

@@ -102,13 +102,10 @@ TransformState::
   }
 
   ReMutexHolder holder(*_states_lock);
-  if (_saved_entry != _states->end()) {
-    nassertv(_states->find(this) == _saved_entry);
-    _states->erase(_saved_entry);
-    _saved_entry = _states->end();
-  }
-   
-  remove_cache_pointers();
+
+  // unref() should have cleared these.
+  nassertv(_saved_entry == _states->end());
+  nassertv(_composition_cache.empty() && _invert_composition_cache.empty());
 
   // If this was true at the beginning of the destructor, but is no
   // longer true now, probably we've been double-deleted.
@@ -765,48 +762,46 @@ invert_compose(const TransformState *other) const {
 ////////////////////////////////////////////////////////////////////
 bool TransformState::
 unref() const {
-  // 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 (auto_break_cycles) {
-      ReMutexHolder holder(*_states_lock);
+  // We always have to grab the lock, since we will definitely need to
+  // be holding it if we happen to drop the reference count to 0.
+  ReMutexHolder holder(*_states_lock);
+
+  if (auto_break_cycles) {
+    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 (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.
-
-        PStatTimer timer(_transform_break_cycles_pcollector);
+      PStatTimer timer(_transform_break_cycles_pcollector);
         
-        ++_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();
+      ++_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();
       }
     }
   }
 
-  return ReferenceCount::unref();
+  if (ReferenceCount::unref()) {
+    // The reference count is still nonzero.
+    return true;
+  }
+
+  // The reference count has just reached zero.  Make sure the object
+  // is removed from the global object pool, before anyone else finds
+  // it and tries to ref it.
+  ((TransformState *)this)->release_new();
+  ((TransformState *)this)->remove_cache_pointers();
+  
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1592,6 +1587,27 @@ r_detect_cycles(const TransformState *start_state,
   return false;
 }
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::release_new
+//       Access: Private
+//  Description: This inverse of return_new, this releases this object
+//               from the global TransformState table.
+//
+//               You must already be holding _states_lock before you
+//               call this method.
+////////////////////////////////////////////////////////////////////
+void TransformState::
+release_new() {
+  nassertv(_states_lock->debug_is_locked());
+   
+  if (_saved_entry != _states->end()) {
+    nassertv(_states->find(this) == _saved_entry);
+    _states->erase(_saved_entry);
+    _saved_entry = _states->end();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformState::remove_cache_pointers
 //       Access: Private
@@ -1606,7 +1622,7 @@ r_detect_cycles(const TransformState *start_state,
 void TransformState::
 remove_cache_pointers() {
   nassertv(_states_lock->debug_is_locked());
-
+   
   // Fortunately, since we added CompositionCache records in pairs, we
   // know exactly the set of TransformState objects that have us in their
   // cache: it's the same set of TransformState objects that we have in

+ 4 - 0
panda/src/pgraph/transformState.h

@@ -195,6 +195,9 @@ public:
   INLINE static void flush_level();
 
 private:
+  INLINE bool do_cache_unref() const;
+  INLINE bool do_node_unref() const;
+
   class CompositionCycleDescEntry {
   public:
     INLINE CompositionCycleDescEntry(const TransformState *obj,
@@ -215,6 +218,7 @@ private:
                               int length, UpdateSeq this_seq,
                               CompositionCycleDesc *cycle_desc);
 
+  void release_new();
   void remove_cache_pointers();
 
 private:

+ 0 - 9
panda/src/putil/Sources.pp

@@ -32,7 +32,6 @@
     config_util.N config_util.h configurable.h \
     datagramInputFile.I datagramInputFile.h \
     datagramOutputFile.I datagramOutputFile.h \
-    deferredDeletor.h \
     doubleBitMask.I doubleBitMask.h \
     drawMask.h \
     factoryBase.I factoryBase.h \
@@ -52,11 +51,9 @@
     modifierButtons.I modifierButtons.h mouseButton.h \
     mouseData.I mouseData.h nameUniquifier.I nameUniquifier.h \
     nodeCachedReferenceCount.h nodeCachedReferenceCount.I \
-    nonDeletor.h \
     portalMask.h \
     pta_double.h \
     pta_float.h pta_int.h \
-    spamDeletor.h \
     sparseArray.I sparseArray.h \
     string_utils.I string_utils.N string_utils.h \
     stringStreamBuf.I stringStreamBuf.h \
@@ -88,7 +85,6 @@
     copyOnWritePointer.cxx \
     config_util.cxx configurable.cxx \
     datagramInputFile.cxx datagramOutputFile.cxx \
-    deferredDeletor.cxx \
     doubleBitMask.cxx \
     factoryBase.cxx \
     factoryParam.cxx factoryParams.cxx \
@@ -101,10 +97,8 @@
     modifierButtons.cxx mouseButton.cxx mouseData.cxx \
     nameUniquifier.cxx \
     nodeCachedReferenceCount.cxx \
-    nonDeletor.cxx \
     pta_double.cxx pta_float.cxx \
     pta_int.cxx pta_ushort.cxx \
-    spamDeletor.cxx \
     sparseArray.cxx \
     string_utils.cxx \
     stringStreamBuf.cxx \
@@ -140,7 +134,6 @@
     config_util.h configurable.h factory.I factory.h \
     datagramInputFile.I datagramInputFile.h \
     datagramOutputFile.I datagramOutputFile.h \
-    deferredDeletor.h \
     doubleBitMask.I doubleBitMask.h \
     drawMask.h \
     factoryBase.I factoryBase.h factoryParam.I factoryParam.h \
@@ -160,11 +153,9 @@
     modifierButtons.h mouseButton.h mouseData.I mouseData.h \
     nameUniquifier.I nameUniquifier.h \
     nodeCachedReferenceCount.h nodeCachedReferenceCount.I \
-    nonDeletor.h \
     portalMask.h \
     pta_double.h \
     pta_float.h pta_int.h pta_ushort.h \
-    spamDeletor.h \
     sparseArray.I sparseArray.h \
     string_utils.I string_utils.h \
     stringStreamBuf.I stringStreamBuf.h \

+ 30 - 38
panda/src/putil/cachedTypedWritableReferenceCount.I

@@ -144,14 +144,8 @@ get_cache_ref_count() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: CachedTypedWritableReferenceCount::cache_ref
 //       Access: Published
-//  Description: Explicitly increments the reference count.
-//
-//               This function is const, even though it changes the
-//               object, because generally fiddling with an object's
-//               reference count isn't considered part of fiddling
-//               with the object.  An object might be const in other
-//               ways, but we still need to accurately count the
-//               number of references to it.
+//  Description: Explicitly increments the cache reference count and
+//               the normal reference count simultaneously.
 ////////////////////////////////////////////////////////////////////
 INLINE void CachedTypedWritableReferenceCount::
 cache_ref() const {
@@ -166,25 +160,8 @@ cache_ref() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: CachedTypedWritableReferenceCount::cache_unref
 //       Access: Published
-//  Description: Explicitly decrements the reference count.  Note that
-//               the object will not be implicitly deleted by unref()
-//               simply because the reference count drops to zero.
-//               (Having a member function delete itself is
-//               problematic; plus, we don't have a virtual destructor
-//               anyway.) However, see the helper function
-//               unref_delete().
-//
-//               User code should avoid using ref() and unref()
-//               directly, which can result in missed reference
-//               counts.  Instead, let a PointerTo object manage the
-//               reference counting automatically.
-//
-//               This function is const, even though it changes the
-//               object, because generally fiddling with an object's
-//               reference count isn't considered part of fiddling
-//               with the object.  An object might be const in other
-//               ways, but we still need to accurately count the
-//               number of references to it.
+//  Description: Explicitly decrements the cache reference count and
+//               the normal reference count simultaneously.
 //
 //               The return value is true if the new reference count
 //               is nonzero, false if it is zero.
@@ -199,9 +176,9 @@ cache_unref() const {
   // zero reference count.  Are you using ref() and unref()
   // directly?  Are you sure you can't use PointerTo's?
   nassertr(_cache_ref_count > 0, 0);
-
-  unref();
-  return AtomicAdjust::dec(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count);
+  
+  AtomicAdjust::dec(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count);
+  return unref();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -219,6 +196,28 @@ test_ref_count_integrity() const {
 #endif
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CachedTypedWritableReferenceCount::cache_unref_only
+//       Access: Protected
+//  Description: Decrements the cache reference count without affecting
+//               the normal reference count.  Intended to be called by
+//               derived classes only, presumably to reimplement
+//               cache_unref().
+////////////////////////////////////////////////////////////////////
+INLINE void CachedTypedWritableReferenceCount::
+cache_unref_only() const {
+#ifdef _DEBUG
+  nassertv(test_ref_count_integrity());
+#endif
+
+  // If this assertion fails, you tried to unref an object with a
+  // zero reference count.  Are you using ref() and unref()
+  // directly?  Are you sure you can't use PointerTo's?
+  nassertv(_cache_ref_count > 0);
+  
+  AtomicAdjust::dec(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: cache_unref_delete
 //  Description: This global helper function will unref the given
@@ -232,14 +231,7 @@ test_ref_count_integrity() const {
 template<class RefCountType>
 INLINE void
 cache_unref_delete(RefCountType *ptr) {
-  ptr->cache_unref();
-  if (ptr->get_ref_count() == 0) {
-    ObjectDeletor *deletor = ObjectDeletor::get_global_ptr();
-    if (deletor != (ObjectDeletor *)NULL) {
-      ptr->ref();
-      deletor->delete_object(RefCountDeleteWrapper<RefCountType>::do_delete, (void *)ptr);
-      return;
-    }
+  if (!ptr->cache_unref()) {
     delete ptr;
   }
 }

+ 1 - 0
panda/src/putil/cachedTypedWritableReferenceCount.h

@@ -56,6 +56,7 @@ PUBLISHED:
   INLINE bool test_ref_count_integrity() const;
 
 protected:
+  INLINE void cache_unref_only() const;
   bool do_test_ref_count_integrity() const;
 
 private:

+ 0 - 7
panda/src/putil/config_util.cxx

@@ -44,9 +44,6 @@
 #include "writableParam.h"
 #include "keyboardButton.h"
 #include "mouseButton.h"
-#include "deferredDeletor.h"
-#include "nonDeletor.h"
-#include "spamDeletor.h"
 
 #include "dconfig.h"
 
@@ -179,10 +176,6 @@ init_libputil() {
   KeyboardButton::init_keyboard_buttons();
   MouseButton::init_mouse_buttons();
 
-  DeferredDeletor::register_deletor();
-  NonDeletor::register_deletor();
-  SpamDeletor::register_deletor();
-
   register_type(BamReader::_remove_flag, "remove");
 
   BamCacheIndex::register_with_read_factory();

+ 0 - 76
panda/src/putil/deferredDeletor.cxx

@@ -1,76 +0,0 @@
-// 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
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#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);
-  if (util_cat.is_spam()) {
-    util_cat.spam()
-      << "Deferring deleting pointer " << ptr << "\n";
-  }
-  _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() {
-  Tokens new_tokens;
-  {
-    MutexHolder holder(_lock);
-    _tokens.swap(new_tokens);
-  }
-
-  Tokens::iterator ti;
-  for (ti = new_tokens.begin(); ti != new_tokens.end(); ++ti) {
-    (*ti).do_delete();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: DeferredDeletor::register_deletor
-//       Access: Public, Static
-//  Description: Registers this deletor with the global pool.
-////////////////////////////////////////////////////////////////////
-void DeferredDeletor::
-register_deletor() {
-  register_subclass(new DeferredDeletor, "deferred");
-}

+ 0 - 48
panda/src/putil/deferredDeletor.h

@@ -1,48 +0,0 @@
-// 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
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#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_PANDA DeferredDeletor : public ObjectDeletor {
-public:
-  DeferredDeletor();
-
-  virtual void delete_object(DeleteFunc *func, void *ptr);
-  virtual void flush();
-
-  static void register_deletor();
-
-private:
-  typedef pvector<DeleteToken> Tokens;
-  Tokens _tokens;
-  Mutex _lock;
-};
-
-#endif

+ 26 - 30
panda/src/putil/nodeCachedReferenceCount.I

@@ -166,42 +166,16 @@ node_ref() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeCachedReferenceCount::node_unref
 //       Access: Published
-//  Description: Explicitly decrements the reference count.  Note that
-//               the object will not be implicitly deleted by unref()
-//               simply because the reference count drops to zero.
-//               (Having a member function delete itself is
-//               problematic; plus, we don't have a virtual destructor
-//               anyway.) However, see the helper function
-//               unref_delete().
-//
-//               User code should avoid using ref() and unref()
-//               directly, which can result in missed reference
-//               counts.  Instead, let a PointerTo object manage the
-//               reference counting automatically.
-//
-//               This function is const, even though it changes the
-//               object, because generally fiddling with an object's
-//               reference count isn't considered part of fiddling
-//               with the object.  An object might be const in other
-//               ways, but we still need to accurately count the
-//               number of references to it.
+//  Description: Explicitly decrements the node reference count and
+//               the normal reference count simultaneously.
 //
 //               The return value is true if the new reference count
 //               is nonzero, false if it is zero.
 ////////////////////////////////////////////////////////////////////
 INLINE bool NodeCachedReferenceCount::
 node_unref() const {
-#ifdef _DEBUG
-  nassertr(test_ref_count_integrity(), 0);
-#endif
-
-  // If this assertion fails, you tried to unref an object with a
-  // zero reference count.  Are you using ref() and unref()
-  // directly?  Are you sure you can't use PointerTo's?
-  nassertr(_node_ref_count > 0, 0);
-
-  unref();
-  return AtomicAdjust::dec(((NodeCachedReferenceCount *)this)->_node_ref_count);
+  node_unref_only();
+  return unref();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -242,3 +216,25 @@ get_referenced_bits() const {
 
   return result;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeCachedReferenceCount::node_unref_only
+//       Access: Protected
+//  Description: Decrements the node reference count without affecting
+//               the normal reference count.  Intended to be called by
+//               derived classes only, presumably to reimplement
+//               node_unref().
+////////////////////////////////////////////////////////////////////
+INLINE void NodeCachedReferenceCount::
+node_unref_only() const {
+#ifdef _DEBUG
+  nassertv(test_ref_count_integrity());
+#endif
+
+  // If this assertion fails, you tried to unref an object with a
+  // zero reference count.  Are you using ref() and unref()
+  // directly?  Are you sure you can't use PointerTo's?
+  nassertv(_node_ref_count > 0);
+
+  AtomicAdjust::dec(((NodeCachedReferenceCount *)this)->_node_ref_count);
+}

+ 1 - 0
panda/src/putil/nodeCachedReferenceCount.h

@@ -84,6 +84,7 @@ PUBLISHED:
   INLINE int get_referenced_bits() const;
 
 protected:
+  INLINE void node_unref_only() const;
   bool do_test_ref_count_integrity() const;
   
 private:

+ 0 - 44
panda/src/putil/nonDeletor.cxx

@@ -1,44 +0,0 @@
-// 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
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#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");
-}

+ 0 - 40
panda/src/putil/nonDeletor.h

@@ -1,40 +0,0 @@
-// 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
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#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_PANDA NonDeletor : public ObjectDeletor {
-public:
-  virtual void delete_object(DeleteFunc *func, void *ptr);
-
-  static void register_deletor();
-};
-
-#endif

+ 0 - 1
panda/src/putil/putil_composite1.cxx

@@ -19,7 +19,6 @@
 #include "copyOnWritePointer.cxx"
 #include "datagramInputFile.cxx"
 #include "datagramOutputFile.cxx"
-#include "deferredDeletor.cxx"
 #include "doubleBitMask.cxx"
 #include "factoryBase.cxx"
 #include "factoryParam.cxx"

+ 0 - 2
panda/src/putil/putil_composite2.cxx

@@ -8,12 +8,10 @@
 #include "mouseData.cxx"
 #include "nameUniquifier.cxx"
 #include "nodeCachedReferenceCount.cxx"
-#include "nonDeletor.cxx"
 #include "pta_double.cxx"
 #include "pta_float.cxx"
 #include "pta_int.cxx"
 #include "pta_ushort.cxx"
-#include "spamDeletor.cxx"
 #include "sparseArray.cxx"
 #include "string_utils.cxx"
 #include "stringStreamBuf.cxx"

+ 0 - 45
panda/src/putil/spamDeletor.cxx

@@ -1,45 +0,0 @@
-// Filename: spamDeletor.cxx
-// Created by:  drose (11Apr07)
-//
-////////////////////////////////////////////////////////////////////
-//
-// 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
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include "spamDeletor.h"
-#include "config_util.h"
-
-////////////////////////////////////////////////////////////////////
-//     Function: SpamDeletor::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 SpamDeletor::
-delete_object(DeleteFunc *func, void *ptr) {
-  if (util_cat.is_spam()) {
-    util_cat.spam()
-      << "Deleting pointer " << ptr << "\n";
-  }
-  (*func)(ptr);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: SpamDeletor::register_deletor
-//       Access: Public, Static
-//  Description: Registers this deletor with the global pool.
-////////////////////////////////////////////////////////////////////
-void SpamDeletor::
-register_deletor() {
-  register_subclass(new SpamDeletor, "spam");
-}

+ 0 - 39
panda/src/putil/spamDeletor.h

@@ -1,39 +0,0 @@
-// Filename: spamDeletor.h
-// Created by:  drose (11Apr07)
-//
-////////////////////////////////////////////////////////////////////
-//
-// 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
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#ifndef SPAMDELETOR_H
-#define SPAMDELETOR_H
-
-#include "pandabase.h"
-#include "objectDeletor.h"
-
-////////////////////////////////////////////////////////////////////
-//       Class : SpamDeletor
-// Description : The only purpose of the SpamDeletor is to issue a
-//               spam Notify message as each object is deleted.  Maybe
-//               it will be useful to track down accidental deletions
-//               due to PT mismanagement.
-////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA SpamDeletor : public ObjectDeletor {
-public:
-  virtual void delete_object(DeleteFunc *func, void *ptr);
-
-  static void register_deletor();
-};
-
-#endif