Browse Source

reduced need for read locks

David Rose 19 years ago
parent
commit
c8eb6c5a82
77 changed files with 2333 additions and 335 deletions
  1. 3 3
      panda/src/chan/partBundle.cxx
  2. 5 0
      panda/src/chan/partBundle.h
  3. 2 2
      panda/src/display/displayRegion.cxx
  4. 2 0
      panda/src/display/displayRegion.h
  5. 9 0
      panda/src/express/Sources.pp
  6. 2 0
      panda/src/express/config_express.cxx
  7. 3 1
      panda/src/express/express_composite1.cxx
  8. 1 0
      panda/src/express/express_composite2.cxx
  9. 259 0
      panda/src/express/nodePointerTo.I
  10. 19 0
      panda/src/express/nodePointerTo.cxx
  11. 81 0
      panda/src/express/nodePointerTo.h
  12. 135 0
      panda/src/express/nodePointerToBase.I
  13. 19 0
      panda/src/express/nodePointerToBase.cxx
  14. 70 0
      panda/src/express/nodePointerToBase.h
  15. 295 0
      panda/src/express/nodeReferenceCount.I
  16. 48 0
      panda/src/express/nodeReferenceCount.cxx
  17. 107 0
      panda/src/express/nodeReferenceCount.h
  18. 101 27
      panda/src/express/pointerToArray.I
  19. 25 7
      panda/src/express/pointerToArray.h
  20. 3 16
      panda/src/gobj/geom.I
  21. 30 1
      panda/src/gobj/geom.cxx
  22. 4 2
      panda/src/gobj/geom.h
  23. 1 2
      panda/src/gobj/geomCacheEntry.cxx
  24. 2 15
      panda/src/gobj/geomPrimitive.I
  25. 28 0
      panda/src/gobj/geomPrimitive.cxx
  26. 2 2
      panda/src/gobj/geomPrimitive.h
  27. 4 2
      panda/src/gobj/geomVertexArrayData.I
  28. 25 5
      panda/src/gobj/geomVertexArrayData.cxx
  29. 2 1
      panda/src/gobj/geomVertexArrayData.h
  30. 2 2
      panda/src/gobj/geomVertexData.I
  31. 8 7
      panda/src/gobj/geomVertexData.cxx
  32. 3 1
      panda/src/gobj/geomVertexData.h
  33. 10 2
      panda/src/gobj/geomVertexReader.I
  34. 1 0
      panda/src/gobj/geomVertexReader.h
  35. 10 2
      panda/src/gobj/geomVertexWriter.I
  36. 1 0
      panda/src/gobj/geomVertexWriter.h
  37. 2 2
      panda/src/gobj/transformBlend.I
  38. 2 0
      panda/src/gobj/transformBlend.h
  39. 1 1
      panda/src/gobj/transformBlendTable.I
  40. 2 0
      panda/src/gobj/transformBlendTable.h
  41. 1 1
      panda/src/pgraph/nodePathComponent.cxx
  42. 2 0
      panda/src/pgraph/nodePathComponent.h
  43. 11 21
      panda/src/pgraph/pandaNode.I
  44. 61 26
      panda/src/pgraph/pandaNode.cxx
  45. 5 3
      panda/src/pgraph/pandaNode.h
  46. 1 1
      panda/src/pgraph/planeNode.cxx
  47. 2 0
      panda/src/pgraph/planeNode.h
  48. 6 0
      panda/src/pipeline/Sources.pp
  49. 6 3
      panda/src/pipeline/cycleData.h
  50. 242 0
      panda/src/pipeline/cycleDataLockedReader.I
  51. 19 0
      panda/src/pipeline/cycleDataLockedReader.cxx
  52. 81 0
      panda/src/pipeline/cycleDataLockedReader.h
  53. 247 0
      panda/src/pipeline/cycleDataLockedStageReader.I
  54. 19 0
      panda/src/pipeline/cycleDataLockedStageReader.cxx
  55. 64 0
      panda/src/pipeline/cycleDataLockedStageReader.h
  56. 1 48
      panda/src/pipeline/cycleDataReader.I
  57. 8 5
      panda/src/pipeline/cycleDataReader.h
  58. 1 48
      panda/src/pipeline/cycleDataStageReader.I
  59. 0 1
      panda/src/pipeline/cycleDataStageReader.h
  60. 8 8
      panda/src/pipeline/cycleDataStageWriter.I
  61. 3 3
      panda/src/pipeline/cycleDataStageWriter.h
  62. 8 8
      panda/src/pipeline/cycleDataWriter.I
  63. 3 3
      panda/src/pipeline/cycleDataWriter.h
  64. 45 1
      panda/src/pipeline/pipelineCycler.I
  65. 2 0
      panda/src/pipeline/pipelineCycler.h
  66. 36 1
      panda/src/pipeline/pipelineCyclerDummyImpl.I
  67. 2 0
      panda/src/pipeline/pipelineCyclerDummyImpl.h
  68. 40 0
      panda/src/pipeline/pipelineCyclerTrivialImpl.I
  69. 2 0
      panda/src/pipeline/pipelineCyclerTrivialImpl.h
  70. 44 3
      panda/src/pipeline/pipelineCyclerTrueImpl.I
  71. 12 8
      panda/src/pipeline/pipelineCyclerTrueImpl.cxx
  72. 6 2
      panda/src/pipeline/pipelineCyclerTrueImpl.h
  73. 2 0
      panda/src/pipeline/pipeline_composite1.cxx
  74. 0 6
      panda/src/putil/Sources.pp
  75. 0 27
      panda/src/putil/nodeCachedReferenceCount.I
  76. 14 3
      panda/src/putil/nodeCachedReferenceCount.h
  77. 0 2
      panda/src/putil/putil_composite2.cxx

+ 3 - 3
panda/src/chan/partBundle.cxx

@@ -94,7 +94,7 @@ void PartBundle::
 set_blend_type(BlendType bt) {
   nassertv(Thread::get_current_pipeline_stage() == 0);
 
-  CDReader cdata(_cycler);
+  CDLockedReader cdata(_cycler);
   if (cdata->_blend_type != bt) {
     CDWriter cdataw(_cycler, cdata);
     cdataw->_blend_type = bt;
@@ -131,7 +131,7 @@ void PartBundle::
 clear_control_effects() {
   nassertv(Thread::get_current_pipeline_stage() == 0);
 
-  CDReader cdata(_cycler);
+  CDLockedReader cdata(_cycler);
   if (!cdata->_blend.empty()) {
     CDWriter cdataw(_cycler, cdata);
     cdataw->_blend.clear();
@@ -299,7 +299,7 @@ control_activated(AnimControl *control) {
   nassertv(Thread::get_current_pipeline_stage() == 0);
   nassertv(control->get_part() == this);
 
-  CDReader cdata(_cycler);
+  CDLockedReader cdata(_cycler);
   // If (and only if) our blend type is BT_single, which means no
   // blending, then starting an animation implicitly enables it.
   if (cdata->_blend_type == BT_single) {

+ 5 - 0
panda/src/chan/partBundle.h

@@ -26,6 +26,10 @@
 #include "partSubset.h"
 #include "pointerTo.h"
 #include "thread.h"
+#include "cycleData.h"
+#include "cycleDataLockedReader.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
 
 class AnimBundle;
 class PartBundleNode;
@@ -150,6 +154,7 @@ private:
   };
 
   PipelineCycler<CData> _cycler;
+  typedef CycleDataLockedReader<CData> CDLockedReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
 

+ 2 - 2
panda/src/display/displayRegion.cxx

@@ -202,7 +202,7 @@ void DisplayRegion::
 set_active(bool active) {
   int pipeline_stage = Thread::get_current_pipeline_stage();
   nassertv(pipeline_stage == 0);
-  CDReader cdata(_cycler);
+  CDLockedReader cdata(_cycler);
 
   if (active != cdata->_active) {
     CDWriter cdataw(_cycler, cdata);
@@ -222,7 +222,7 @@ set_active(bool active) {
 void DisplayRegion::
 set_sort(int sort) {
   nassertv(Thread::get_current_pipeline_stage() == 0);
-  CDReader cdata(_cycler);
+  CDLockedReader cdata(_cycler);
 
   if (sort != cdata->_sort) {
     CDWriter cdataw(_cycler, cdata);

+ 2 - 0
panda/src/display/displayRegion.h

@@ -28,6 +28,7 @@
 #include "sceneSetup.h"
 #include "pointerTo.h"
 #include "cycleData.h"
+#include "cycleDataLockedReader.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "cycleDataStageWriter.h"
@@ -176,6 +177,7 @@ private:
   };
 
   PipelineCycler<CData> _cycler;
+  typedef CycleDataLockedReader<CData> CDLockedReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataStageWriter<CData> CDStageWriter;

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

@@ -30,6 +30,9 @@
     multifile.I multifile.h \
     namable.I \
     namable.h nativeNumericData.I nativeNumericData.h \
+    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 \
@@ -80,6 +83,9 @@
     memoryUsagePointers.cxx multifile.cxx \
     namable.cxx \
     nativeNumericData.cxx \
+    nodePointerToBase.cxx \
+    nodePointerTo.cxx \
+    nodeReferenceCount.cxx \
     objectDeletor.cxx \
     ordered_vector.cxx \
     password_hash.cxx \
@@ -136,6 +142,9 @@
     multifile.I multifile.h \
     namable.I \
     namable.h nativeNumericData.I nativeNumericData.h \
+    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 \

+ 2 - 0
panda/src/express/config_express.cxx

@@ -18,6 +18,7 @@
 
 #include "config_express.h"
 #include "datagram.h"
+#include "nodeReferenceCount.h"
 #include "referenceCount.h"
 #include "textEncoder.h"
 #include "typedObject.h"
@@ -147,6 +148,7 @@ init_libexpress() {
   initialized = true;
 
   Datagram::init_type();
+  NodeReferenceCount::init_type();
   ReferenceCount::init_type();
   TextEncoder::init_type();
   TypedObject::init_type();

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

@@ -18,6 +18,9 @@
 #include "multifile.cxx"
 #include "namable.cxx"
 #include "nativeNumericData.cxx"
+#include "nodePointerToBase.cxx"
+#include "nodePointerTo.cxx"
+#include "nodeReferenceCount.cxx"
 #include "objectDeletor.cxx"
 #include "ordered_vector.cxx"
 #include "patchfile.cxx"
@@ -27,4 +30,3 @@
 #include "pointerToBase.cxx"
 #include "pointerToVoid.cxx"
 #include "profileTimer.cxx"
-#include "pta_uchar.cxx"

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

@@ -1,3 +1,4 @@
+#include "pta_uchar.cxx"
 #include "ramfile.cxx"
 #include "referenceCount.cxx"
 #include "reversedNumericData.cxx"

+ 259 - 0
panda/src/express/nodePointerTo.I

@@ -0,0 +1,259 @@
+// Filename: nodePointerTo.I
+// Created by:  drose (07May05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: NodePointerTo::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodePointerTo<T>::
+NodePointerTo(To *ptr) : NodePointerToBase<T>(ptr) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerTo::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodePointerTo<T>::
+NodePointerTo(const NodePointerTo<T> &copy) :
+  NodePointerToBase<T>((const NodePointerToBase<T> &)copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerTo::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodePointerTo<T>::
+~NodePointerTo() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerTo::Dereference operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE TYPENAME NodePointerTo<T>::To &NodePointerTo<T>::
+operator *() const {
+  return *((To *)(this->_void_ptr));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerTo::Member access operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE TYPENAME NodePointerTo<T>::To *NodePointerTo<T>::
+operator -> () const {
+  return (To *)(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerTo::Typecast operator
+//       Access: Public
+//  Description: We also have the typecast operator to automatically
+//               convert NodePointerTo's to the required kind of actual
+//               pointer.  This introduces ambiguities which the
+//               compiler will resolve one way or the other, but we
+//               don't care which way it goes because either will be
+//               correct.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodePointerTo<T>::
+operator TYPENAME NodePointerToBase<T>::To *() const {
+  return (To *)(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerTo::p
+//       Access: Public
+//  Description: Returns an ordinary pointer instead of a NodePointerTo.
+//               Useful to work around compiler problems, particularly
+//               for implicit upcasts.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE TYPENAME NodePointerTo<T>::To *NodePointerTo<T>::
+p() const {
+  return (To *)(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerTo::Assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodePointerTo<T> &NodePointerTo<T>::
+operator = (To *ptr) {
+  reassign(ptr);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerTo::Assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodePointerTo<T> &NodePointerTo<T>::
+operator = (const NodePointerTo<T> &copy) {
+  reassign((const NodePointerToBase<T> &)copy);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodeConstPointerTo<T>::
+NodeConstPointerTo(const TYPENAME NodeConstPointerTo<T>::To *ptr) :
+  NodePointerToBase<T>((TYPENAME NodeConstPointerTo<T>::To *)ptr)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodeConstPointerTo<T>::
+NodeConstPointerTo(const NodePointerTo<T> &copy) :
+  NodePointerToBase<T>((const NodePointerToBase<T> &)copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodeConstPointerTo<T>::
+NodeConstPointerTo(const NodeConstPointerTo<T> &copy) :
+  NodePointerToBase<T>((const NodePointerToBase<T> &)copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodeConstPointerTo<T>::
+~NodeConstPointerTo() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::Dereference operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE const TYPENAME NodeConstPointerTo<T>::To &NodeConstPointerTo<T>::
+operator *() const {
+  return *((To *)(this->_void_ptr));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::Member access operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE const TYPENAME NodeConstPointerTo<T>::To *NodeConstPointerTo<T>::
+operator -> () const {
+  return (To *)(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::Typecast operator
+//       Access: Public
+//  Description: We also have the typecast operator to automatically
+//               convert NodeConstPointerTo's to the required kind of actual
+//               pointer.  This introduces ambiguities which the
+//               compiler will resolve one way or the other, but we
+//               don't care which way it goes because either will be
+//               correct.
+////////////////////////////////////////////////////////////////////
+
+template<class T>
+INLINE NodeConstPointerTo<T>::
+operator const TYPENAME NodePointerToBase<T>::To *() const {
+  return (To *)(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::p
+//       Access: Public
+//  Description: Returns an ordinary pointer instead of a NodeConstPointerTo.
+//               Useful to work around compiler problems, particularly
+//               for implicit upcasts.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE const TYPENAME NodeConstPointerTo<T>::To *NodeConstPointerTo<T>::
+p() const {
+  return (To *)(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::Assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodeConstPointerTo<T> &NodeConstPointerTo<T>::
+operator = (const To *ptr) {
+  reassign((To *)ptr);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::Assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodeConstPointerTo<T> &NodeConstPointerTo<T>::
+operator = (const NodePointerTo<T> &copy) {
+  reassign((const NodePointerToBase<T> &)copy);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeConstPointerTo::Assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodeConstPointerTo<T> &NodeConstPointerTo<T>::
+operator = (const NodeConstPointerTo<T> &copy) {
+  reassign((const NodePointerToBase<T> &)copy);
+  return *this;
+}

+ 19 - 0
panda/src/express/nodePointerTo.cxx

@@ -0,0 +1,19 @@
+// Filename: nodePointerTo.cxx
+// Created by:  drose (07May05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "nodePointerTo.h"

+ 81 - 0
panda/src/express/nodePointerTo.h

@@ -0,0 +1,81 @@
+// Filename: nodePointerTo.h
+// Created by:  drose (07May05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 NODEPOINTERTO_H
+#define NODEPOINTERTO_H
+
+#include "pandabase.h"
+#include "nodePointerToBase.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : NodePointerTo
+// Description : This implements the special NodePointerTo template
+//               class, which works just like PointerTo except it
+//               manages the objects node_ref_count instead of the
+//               normal ref_count.
+////////////////////////////////////////////////////////////////////
+template <class T>
+class NodePointerTo : public NodePointerToBase<T> {
+public:
+  typedef TYPENAME NodePointerToBase<T>::To To;
+  INLINE NodePointerTo(To *ptr = (To *)NULL);
+  INLINE NodePointerTo(const NodePointerTo<T> &copy);
+  INLINE ~NodePointerTo();
+
+  INLINE To &operator *() const;
+  INLINE To *operator -> () const;
+  INLINE operator TYPENAME NodePointerToBase<T>::To *() const;
+
+  INLINE To *p() const;
+
+  INLINE NodePointerTo<T> &operator = (To *ptr);
+  INLINE NodePointerTo<T> &operator = (const NodePointerTo<T> &copy);
+};
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : NodeConstPointerTo
+// Description : A NodeConstPointerTo is similar to a NodePointerTo,
+//               except it keeps a const pointer to the thing.
+////////////////////////////////////////////////////////////////////
+template <class T>
+class NodeConstPointerTo : public NodePointerToBase<T> {
+public:
+  typedef TYPENAME NodePointerToBase<T>::To To;
+  INLINE NodeConstPointerTo(const To *ptr = (const To *)NULL);
+  INLINE NodeConstPointerTo(const NodePointerTo<T> &copy);
+  INLINE NodeConstPointerTo(const NodeConstPointerTo<T> &copy);
+  INLINE ~NodeConstPointerTo();
+
+  INLINE const To &operator *() const;
+  INLINE const To *operator -> () const;
+  INLINE operator const TYPENAME NodePointerToBase<T>::To *() const;
+
+  INLINE const To *p() const;
+
+  INLINE NodeConstPointerTo<T> &operator = (const To *ptr);
+  INLINE NodeConstPointerTo<T> &operator = (const NodePointerTo<T> &copy);
+  INLINE NodeConstPointerTo<T> &operator = (const NodeConstPointerTo<T> &copy);
+};
+
+#define NPT(type) NodePointerTo< type >
+#define NCPT(type) NodeConstPointerTo< type >
+
+#include "nodePointerTo.I"
+
+#endif

+ 135 - 0
panda/src/express/nodePointerToBase.I

@@ -0,0 +1,135 @@
+// Filename: nodePointerToBase.I
+// Created by:  drose (07May05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: NodePointerToBase::Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodePointerToBase<T>::
+NodePointerToBase(To *ptr) {
+  reassign(ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerToBase::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodePointerToBase<T>::
+NodePointerToBase(const NodePointerToBase<T> &copy) {
+  reassign(copy);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerToBase::Destructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE NodePointerToBase<T>::
+~NodePointerToBase() {
+  reassign((To *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerToBase::reassign
+//       Access: Protected
+//  Description: This is the main work of the NodePointerTo family.  When
+//               the pointer is reassigned, decrement the old
+//               reference count and increment the new one.
+////////////////////////////////////////////////////////////////////
+template<class T>
+void NodePointerToBase<T>::
+reassign(To *ptr) {
+  if (ptr != (To *)_void_ptr) {
+    // First save the old pointer; we won't delete it until we have
+    // assigned the new one.  We do this just in case there are
+    // cascading effects from deleting this pointer that might
+    // inadvertently delete the new one.  (Don't laugh--it's
+    // happened!)
+    To *old_ptr = (To *)_void_ptr;
+
+    _void_ptr = (void *)ptr;
+    if (ptr != (To *)NULL) {
+      ptr->node_ref();
+#ifdef DO_MEMORY_USAGE
+      if (MemoryUsage::get_track_memory_usage()) {
+        // Make sure the MemoryUsage record knows what the TypeHandle
+        // is, if we know it ourselves.
+        TypeHandle type = get_type_handle(To);
+        if (type == TypeHandle::none()) {
+          do_init_type(To);
+          type = get_type_handle(To);
+        }
+        if (type != TypeHandle::none()) {
+          MemoryUsage::update_type(ptr, type);
+        }
+      }
+#endif
+    }
+
+    // Now delete the old pointer.
+    if (old_ptr != (To *)NULL) {
+      node_unref_delete(old_ptr);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerToBase::reassign
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE void NodePointerToBase<T>::
+reassign(const NodePointerToBase<To> &copy) {
+  reassign((To *)copy._void_ptr);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerToBase::clear
+//       Access: Published
+//  Description: A convenient way to set the NodePointerTo object to NULL.
+//               (Assignment to a NULL pointer also works, of course.)
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE void NodePointerToBase<T>::
+clear() {
+  reassign((To *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePointerToBase::output
+//       Access: Published
+//  Description: A handy function to output NodePointerTo's as a hex
+//               pointer followed by a reference count.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE void NodePointerToBase<T>::
+output(ostream &out) const {
+  out << _void_ptr;
+  if (_void_ptr != (void *)NULL) {
+    out << ":" << ((To *)_void_ptr)->get_node_ref_count() << "/"
+        << ((To *)_void_ptr)->get_ref_count();
+  }
+}

+ 19 - 0
panda/src/express/nodePointerToBase.cxx

@@ -0,0 +1,19 @@
+// Filename: nodePointerToBase.cxx
+// Created by:  drose (07May05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "nodePointerToBase.h"

+ 70 - 0
panda/src/express/nodePointerToBase.h

@@ -0,0 +1,70 @@
+// Filename: nodePointerToBase.h
+// Created by:  drose (07May05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 NODEPOINTERTOBASE_H
+#define NODEPOINTERTOBASE_H
+
+#include "pandabase.h"
+#include "pointerToVoid.h"
+#include "memoryUsage.h"
+#include "config_express.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : NodePointerToBase
+// Description : This is similar to PointerToBase, but it manages
+//               objects of type NodeReferenceCount or
+//               NodeCachedReferenceCount, and it updates the
+//               node_ref_count instead of the regular ref_count.  It
+//               is intended for use only in PandaNode, to hold a
+//               pointer to RenderState and TransformState, although
+//               it could be used by any object that wanted to
+//               maintain a separate reference count for reporting
+//               purposes.
+////////////////////////////////////////////////////////////////////
+template <class T>
+class NodePointerToBase : public PointerToVoid {
+public:
+  typedef T To;
+
+protected:
+  INLINE NodePointerToBase(To *ptr);
+  INLINE NodePointerToBase(const NodePointerToBase<T> &copy);
+  INLINE ~NodePointerToBase();
+
+  void reassign(To *ptr);
+  INLINE void reassign(const NodePointerToBase<To> &copy);
+
+  // No assignment or retrieval functions are declared in
+  // NodePointerToBase, because we will have to specialize on const
+  // vs. non-const later.
+
+PUBLISHED:
+  INLINE void clear();
+
+  void output(ostream &out) const;
+};
+
+template<class T>
+INLINE ostream &operator <<(ostream &out, const NodePointerToBase<T> &pointer) {
+  pointer.output(out);
+  return out;
+}
+
+#include "nodePointerToBase.I"
+
+#endif

+ 295 - 0
panda/src/express/nodeReferenceCount.I

@@ -0,0 +1,295 @@
+// Filename: nodeReferenceCount.I
+// Created by:  drose (01May06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+template<class Base>
+TypeHandle NodeRefCountObj<Base>::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeReferenceCount::Constructor
+//       Access: Protected
+//  Description: The ReferenceCount constructor is protected because
+//               you almost never want to create just a ReferenceCount
+//               object by itself, and it's probably a mistake if you
+//               try.
+//
+//               ReferenceCount doesn't store any useful information
+//               in its own right; its only purpose is to add
+//               reference-counting to some other class via
+//               inheritance.
+////////////////////////////////////////////////////////////////////
+INLINE NodeReferenceCount::
+NodeReferenceCount() {
+  _node_ref_count = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeReferenceCount::Copy Constructor
+//       Access: Protected
+//  Description: The copies of reference-counted objects do not
+//               themselves inherit the reference count!
+//
+//               This copy constructor is protected because you almost
+//               never want to create just a ReferenceCount object by
+//               itself, and it's probably a mistake if you try.
+////////////////////////////////////////////////////////////////////
+INLINE NodeReferenceCount::
+NodeReferenceCount(const NodeReferenceCount &copy) : ReferenceCount(copy) {
+  _node_ref_count = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeReferenceCount::Copy Assignment Operator
+//       Access: Protected
+//  Description: The copies of reference-counted objects do not
+//               themselves inherit the reference count!
+//
+//               This copy assignment operator is protected because
+//               you almost never want to copy just a ReferenceCount
+//               object by itself, and it's probably a mistake if you
+//               try.  Instead, this should only be called from a
+//               derived class that implements this operator and then
+//               calls up the inheritance chain.
+////////////////////////////////////////////////////////////////////
+INLINE void NodeReferenceCount::
+operator = (const NodeReferenceCount &copy) {
+  nassertv(this != NULL);
+
+  // If this assertion fails, our own pointer was recently deleted.
+  // Possibly you used a real pointer instead of a PointerTo at some
+  // point, and the object was deleted when the PointerTo went out of
+  // scope.  Maybe you tried to create an automatic (local variable)
+  // instance of a class that derives from ReferenceCount.  Or maybe
+  // your headers are out of sync, and you need to make clean in
+  // direct or some higher tree.
+  nassertv(_node_ref_count != -100);
+
+  ReferenceCount::operator = (copy);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeReferenceCount::Destructor
+//       Access: Protected
+//  Description: The ReferenceCount destructor is protected to
+//               discourage users from accidentally trying to delete a
+//               ReferenceCount pointer directly.  This is almost
+//               always a bad idea, since the destructor is not
+//               virtual, and you've almost certainly got some pointer
+//               to something that inherits from ReferenceCount, not
+//               just a plain old ReferenceCount object.
+////////////////////////////////////////////////////////////////////
+INLINE NodeReferenceCount::
+~NodeReferenceCount() {
+  nassertv(this != NULL);
+
+  // If this assertion fails, we're trying to delete an object that
+  // was just deleted.  Possibly you used a real pointer instead of a
+  // PointerTo at some point, and the object was deleted when the
+  // PointerTo went out of scope.  Maybe you tried to create an
+  // automatic (local variable) instance of a class that derives from
+  // ReferenceCount.  Or maybe your headers are out of sync, and you
+  // need to make clean in direct or some higher tree.
+  nassertv(_node_ref_count != -100);
+
+  // If this assertion fails, the reference counts are all screwed
+  // up altogether.  Maybe some errant code stomped all over memory
+  // somewhere.
+  nassertv(_node_ref_count >= 0);
+
+  // If this assertion fails, someone tried to delete this object
+  // while its reference count was still positive.  Maybe you tried
+  // to point a PointerTo at a static object (a local variable,
+  // instead of one allocated via new)?  The test below against 0x7f
+  // is supposed to check for that, but it's a pretty hokey test.
+
+  // Another possibility is you inadvertently omitted a copy
+  // constructor for a ReferenceCount object, and then bitwise
+  // copied a dynamically allocated value--reference count and
+  // all--onto a locally allocated one.
+  nassertv(_node_ref_count == 0);
+
+#ifndef NDEBUG
+  // Ok, all clear to delete.  Now set the reference count to -100,
+  // so we'll have a better chance of noticing if we happen to have
+  // a stray pointer to it still out there.
+  _node_ref_count = -100;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeReferenceCount::get_node_ref_count
+//       Access: Published
+//  Description: Returns the current reference count.
+////////////////////////////////////////////////////////////////////
+INLINE int NodeReferenceCount::
+get_node_ref_count() const {
+#ifdef _DEBUG
+  test_ref_count_integrity();
+#endif
+  return AtomicAdjust::get(_node_ref_count);
+}
+
+////////////////////////////////////////////////////////////////////
+//     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.
+////////////////////////////////////////////////////////////////////
+INLINE void NodeReferenceCount::
+node_ref() const {
+#ifdef _DEBUG
+  nassertv(test_ref_count_integrity());
+#endif
+
+  ref();
+  AtomicAdjust::inc(((NodeReferenceCount *)this)->_node_ref_count);
+}
+
+////////////////////////////////////////////////////////////////////
+//     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.
+//
+//               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);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeReferenceCount::test_ref_count_integrity
+//       Access: Published
+//  Description: Does some easy checks to make sure that the reference
+//               count isn't completely bogus.
+////////////////////////////////////////////////////////////////////
+INLINE bool NodeReferenceCount::
+test_ref_count_integrity() const {
+#ifndef NDEBUG
+  return do_test_ref_count_integrity();
+#else
+  return true;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: node_unref_delete
+//  Description: This global helper function will unref the given
+//               ReferenceCount object, and if the reference count
+//               reaches zero, automatically delete it.  It can't be a
+//               member function because it's usually a bad idea to
+//               delete an object from within its own member function.
+//               It's a template function so the destructor doesn't
+//               have to be virtual.
+////////////////////////////////////////////////////////////////////
+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;
+    }
+
+    delete ptr;
+  }
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRefCountObj::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class Base>
+INLINE NodeRefCountObj<Base>::
+NodeRefCountObj() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRefCountObj::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class Base>
+INLINE NodeRefCountObj<Base>::
+NodeRefCountObj(const Base &copy) : Base(copy) {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeRefCountObj::init_type
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class Base>
+void NodeRefCountObj<Base>::
+init_type() {
+#if defined(HAVE_RTTI) && !defined(__EDG__)
+  // If we have RTTI, we can determine the name of the base type.
+  string base_name = typeid(Base).name();
+#else
+  string base_name = "unknown";
+#endif
+
+  TypeHandle base_type = register_dynamic_type(base_name);
+
+  ReferenceCount::init_type();
+  _type_handle =
+    register_dynamic_type("NodeRefCountObj<" + base_name + ">",
+                          base_type, ReferenceCount::get_class_type());
+}

+ 48 - 0
panda/src/express/nodeReferenceCount.cxx

@@ -0,0 +1,48 @@
+// Filename: nodeReferenceCount.cxx
+// Created by:  drose (01May06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "nodeReferenceCount.h"
+
+TypeHandle NodeReferenceCount::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodeReferenceCount::do_test_ref_count_integrity
+//       Access: Protected
+//  Description: Does some easy checks to make sure that the reference
+//               count isn't completely bogus.
+////////////////////////////////////////////////////////////////////
+bool NodeReferenceCount::
+do_test_ref_count_integrity() const {
+  nassertr(this != NULL, false);
+
+  // If this assertion fails, we're trying to delete an object that
+  // was just deleted.  Possibly you used a real pointer instead of a
+  // PointerTo at some point, and the object was deleted when the
+  // PointerTo went out of scope.  Maybe you tried to create an
+  // automatic (local variable) instance of a class that derives from
+  // ReferenceCount.  Or maybe your headers are out of sync, and you
+  // need to make clean in direct or some higher tree.
+  nassertr(_node_ref_count != -100, false);
+
+  // If this assertion fails, the reference counts are all screwed
+  // up altogether.  Maybe some errant code stomped all over memory
+  // somewhere.
+  nassertr(_node_ref_count >= 0, false);
+
+  return ReferenceCount::do_test_ref_count_integrity();
+}

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

@@ -0,0 +1,107 @@
+// Filename: nodeReferenceCount.h
+// Created by:  drose (01May06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 NODEREFERENCECOUNT_H
+#define NODEREFERENCECOUNT_H
+
+#include "pandabase.h"
+
+#include "referenceCount.h"
+#include "objectDeletor.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : NodeReferenceCount
+// Description : This class specializes ReferenceCount to add an
+//               additional counter, called node_ref_count, for the
+//               purposes of counting the number of times the object
+//               is referenced by a "node", whatever that may mean in
+//               context.
+//
+//               The new methods node_ref() and node_unref()
+//               automatically increment and decrement the primary
+//               reference count as well.  There also exists a
+//               NodePointerTo<> class to maintain the node_ref
+//               counters automatically.
+//
+//               See also CachedTypedWritableReferenceCount, which is
+//               similar in principle, as well as
+//               NodeCachedReferenceCount, which combines both of
+//               these.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS NodeReferenceCount : public ReferenceCount {
+protected:
+  INLINE NodeReferenceCount();
+  INLINE NodeReferenceCount(const NodeReferenceCount &copy);
+  INLINE void operator = (const NodeReferenceCount &copy);
+  INLINE ~NodeReferenceCount();
+
+PUBLISHED:
+  INLINE int get_node_ref_count() const;
+  INLINE void node_ref() const;
+  INLINE bool node_unref() const;
+  INLINE bool test_ref_count_integrity() const;
+
+protected:
+  bool do_test_ref_count_integrity() const;
+  
+private:
+  PN_int32 _node_ref_count;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+
+  static void init_type() {
+    ReferenceCount::init_type();
+    register_type(_type_handle, "NodeReferenceCount",
+                  ReferenceCount::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+template<class RefCountType>
+INLINE void node_unref_delete(RefCountType *ptr);
+
+////////////////////////////////////////////////////////////////////
+//       Class : NodeRefCountObj
+// Description : This works like RefCountObj, but it inherits from
+//               NodeReferenceCount instead of ReferenceCount.
+////////////////////////////////////////////////////////////////////
+template<class Base>
+class NodeRefCountObj : public NodeReferenceCount, public Base {
+public:
+  INLINE NodeRefCountObj();
+  INLINE NodeRefCountObj(const Base &copy);
+  ALLOC_DELETED_CHAIN(NodeRefCountObj<Base>);
+
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type();
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "nodeReferenceCount.I"
+
+#endif  
+

+ 101 - 27
panda/src/express/pointerToArray.I

@@ -30,7 +30,7 @@ pvector<Element> ConstPointerToArray<Element>::_empty_array;
 template<class Element>
 INLINE PointerToArray<Element>::
 PointerToArray() :
-  PointerToBase<RefCountObj<pvector<Element> > >((RefCountObj<pvector<Element> > *)NULL)
+  PointerToBase<NodeRefCountObj<pvector<Element> > >((NodeRefCountObj<pvector<Element> > *)NULL)
 {
 }
 
@@ -52,7 +52,7 @@ PointerToArray<Element>::empty_array(size_type n) {
 template<class Element>
 INLINE PointerToArray<Element>::
 PointerToArray(size_type n, const Element &value) :
-  PointerToBase<RefCountObj<pvector<Element> > >(new RefCountObj<pvector<Element> >) {
+  PointerToBase<NodeRefCountObj<pvector<Element> > >(new NodeRefCountObj<pvector<Element> >) {
   ((To *)(this->_void_ptr))->reserve(n);
   insert(begin(), n, value);
 }
@@ -65,7 +65,7 @@ PointerToArray(size_type n, const Element &value) :
 template<class Element>
 INLINE PointerToArray<Element>::
 PointerToArray(const PointerToArray<Element> &copy) :
-  PointerToBase<RefCountObj<pvector<Element> > >(copy)
+  PointerToBase<NodeRefCountObj<pvector<Element> > >(copy)
 {
 }
 
@@ -145,7 +145,7 @@ template<class Element>
 INLINE TYPENAME PointerToArray<Element>::size_type PointerToArray<Element>::
 max_size() const {
   nassertd((this->_void_ptr) != NULL) {
-    ((PointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((PointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   return ((To *)(this->_void_ptr))->max_size();
 }
@@ -170,7 +170,7 @@ template<class Element>
 INLINE void PointerToArray<Element>::
 reserve(TYPENAME PointerToArray<Element>::size_type n) {
   if ((this->_void_ptr) == NULL) {
-    reassign(new RefCountObj<pvector<Element> >);
+    reassign(new NodeRefCountObj<pvector<Element> >);
   }
   ((To *)(this->_void_ptr))->reserve(n);
 }
@@ -196,7 +196,7 @@ template<class Element>
 INLINE TYPENAME PointerToArray<Element>::reference PointerToArray<Element>::
 front() const {
   nassertd((this->_void_ptr) != NULL) {
-    ((PointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((PointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   nassertd(!((To *)(this->_void_ptr))->empty()) {
     ((To *)(this->_void_ptr))->push_back(Element());
@@ -213,7 +213,7 @@ template<class Element>
 INLINE TYPENAME PointerToArray<Element>::reference PointerToArray<Element>::
 back() const {
   nassertd((this->_void_ptr) != NULL) {
-    ((PointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((PointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   nassertd(!((To *)(this->_void_ptr))->empty()) {
     ((To *)(this->_void_ptr))->push_back(Element());
@@ -230,7 +230,7 @@ template<class Element>
 INLINE TYPENAME PointerToArray<Element>::iterator PointerToArray<Element>::
 insert(iterator position, const Element &x) {
   if ((this->_void_ptr) == NULL) {
-    reassign(new RefCountObj<pvector<Element> >);
+    reassign(new NodeRefCountObj<pvector<Element> >);
     position = end();
   }
   nassertr(position >= ((To *)(this->_void_ptr))->begin() &&
@@ -247,7 +247,7 @@ template<class Element>
 INLINE void PointerToArray<Element>::
 insert(iterator position, size_type n, const Element &x) {
   if ((this->_void_ptr) == NULL) {
-    reassign(new RefCountObj<pvector<Element> >);
+    reassign(new NodeRefCountObj<pvector<Element> >);
     position = end();
   }
   nassertv(position >= ((To *)(this->_void_ptr))->begin() &&
@@ -293,7 +293,7 @@ template<class Element>
 INLINE TYPENAME PointerToArray<Element>::reference PointerToArray<Element>::
 operator [](size_type n) const {
   nassertd((this->_void_ptr) != NULL) {
-    ((PointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((PointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   nassertd(!((To *)(this->_void_ptr))->empty()) {
     ((To *)(this->_void_ptr))->push_back(Element());
@@ -354,7 +354,7 @@ template<class Element>
 INLINE void PointerToArray<Element>::
 push_back(const Element &x) {
   if ((this->_void_ptr) == NULL) {
-    reassign(new RefCountObj<pvector<Element> >);
+    reassign(new NodeRefCountObj<pvector<Element> >);
   }
   ((To *)(this->_void_ptr))->push_back(x);
 }
@@ -368,7 +368,7 @@ template<class Element>
 INLINE void PointerToArray<Element>::
 pop_back() {
   nassertd((this->_void_ptr) != NULL) {
-    ((PointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((PointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   nassertv(!((To *)(this->_void_ptr))->empty());
   ((To *)(this->_void_ptr))->pop_back();
@@ -385,7 +385,7 @@ template<class Element>
 INLINE void PointerToArray<Element>::
 make_empty() {
   nassertd((this->_void_ptr) != NULL) {
-    ((PointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((PointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   nassertv(!((To *)(this->_void_ptr))->empty());
   ((To *)(this->_void_ptr))->clear();
@@ -429,7 +429,7 @@ template<class Element>
 INLINE pvector<Element> &PointerToArray<Element>::
 v() const {
   if ((this->_void_ptr) == NULL) {
-    ((PointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((PointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   return *((To *)(this->_void_ptr));
 }
@@ -468,6 +468,43 @@ get_ref_count() const {
   return ((this->_void_ptr) == NULL) ? 0 : ((To *)(this->_void_ptr))->get_ref_count();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PointerToArray::get_node_ref_count
+//       Access: Public
+//  Description: Returns the node_ref of the underlying vector.
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE int PointerToArray<Element>::
+get_node_ref_count() const {
+  return ((this->_void_ptr) == NULL) ? 0 : ((To *)(this->_void_ptr))->get_node_ref_count();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PointerToArray::node_ref
+//       Access: Public
+//  Description: Increments the node_ref of the underlying vector.
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE void PointerToArray<Element>::
+node_ref() const {
+  if ((this->_void_ptr) == NULL) {
+    ((PointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
+  }
+  ((To *)(this->_void_ptr))->node_ref();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PointerToArray::node_unref
+//       Access: Public
+//  Description: Decrements the node_ref of the underlying vector.
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE bool PointerToArray<Element>::
+node_unref() const {
+  nassertr((this->_void_ptr) != NULL, true);
+  return ((To *)(this->_void_ptr))->node_unref();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PointerToArray::Assignment operator
 //       Access: Public
@@ -475,7 +512,7 @@ get_ref_count() const {
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 INLINE PointerToArray<Element> &PointerToArray<Element>::
-operator = (RefCountObj<pvector<Element> > *ptr) {
+operator = (NodeRefCountObj<pvector<Element> > *ptr) {
   reassign(ptr);
   return *this;
 }
@@ -502,7 +539,7 @@ operator = (const PointerToArray<Element> &copy) {
 template<class Element>
 INLINE void PointerToArray<Element>::
 clear() {
-  reassign((RefCountObj<pvector<Element> > *)NULL);
+  reassign((NodeRefCountObj<pvector<Element> > *)NULL);
 }
 
 
@@ -515,7 +552,7 @@ clear() {
 template<class Element>
 INLINE ConstPointerToArray<Element>::
 ConstPointerToArray() :
-  PointerToBase<RefCountObj<pvector<Element> > >((RefCountObj<pvector<Element> > *)NULL)
+  PointerToBase<NodeRefCountObj<pvector<Element> > >((NodeRefCountObj<pvector<Element> > *)NULL)
 {
 }
 
@@ -527,7 +564,7 @@ ConstPointerToArray() :
 template<class Element>
 INLINE ConstPointerToArray<Element>::
 ConstPointerToArray(const PointerToArray<Element> &copy) :
-  PointerToBase<RefCountObj<pvector<Element> > >(copy)
+  PointerToBase<NodeRefCountObj<pvector<Element> > >(copy)
 {
 }
 
@@ -539,7 +576,7 @@ ConstPointerToArray(const PointerToArray<Element> &copy) :
 template<class Element>
 INLINE ConstPointerToArray<Element>::
 ConstPointerToArray(const ConstPointerToArray<Element> &copy) :
-  PointerToBase<RefCountObj<pvector<Element> > >(copy)
+  PointerToBase<NodeRefCountObj<pvector<Element> > >(copy)
 {
 }
 
@@ -619,7 +656,7 @@ template<class Element>
 INLINE TYPENAME ConstPointerToArray<Element>::size_type ConstPointerToArray<Element>::
 max_size() const {
   nassertd((this->_void_ptr) != NULL) {
-    ((ConstPointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((ConstPointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   return ((To *)(this->_void_ptr))->max_size();
 }
@@ -644,7 +681,7 @@ template<class Element>
 INLINE TYPENAME ConstPointerToArray<Element>::size_type ConstPointerToArray<Element>::
 capacity() const {
   nassertd((this->_void_ptr) != NULL) {
-    ((ConstPointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((ConstPointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   return ((To *)(this->_void_ptr))->capacity();
 }
@@ -658,7 +695,7 @@ template<class Element>
 INLINE TYPENAME ConstPointerToArray<Element>::reference ConstPointerToArray<Element>::
 front() const {
   nassertd((this->_void_ptr) != NULL) {
-    ((ConstPointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((ConstPointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   nassertd(!((To *)(this->_void_ptr))->empty()) {
     ((To *)(this->_void_ptr))->push_back(Element());
@@ -675,7 +712,7 @@ template<class Element>
 INLINE TYPENAME ConstPointerToArray<Element>::reference ConstPointerToArray<Element>::
 back() const {
   nassertd((this->_void_ptr) != NULL) {
-    ((ConstPointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((ConstPointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   nassertd(!((To *)(this->_void_ptr))->empty()) {
     ((To *)(this->_void_ptr))->push_back(Element());
@@ -693,7 +730,7 @@ template<class Element>
 INLINE TYPENAME ConstPointerToArray<Element>::reference ConstPointerToArray<Element>::
 operator [](size_type n) const {
   nassertd((this->_void_ptr) != NULL) {
-    ((ConstPointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((ConstPointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   nassertd(!((To *)(this->_void_ptr))->empty()) {
     ((To *)(this->_void_ptr))->push_back(Element());
@@ -767,7 +804,7 @@ template<class Element>
 INLINE const pvector<Element> &ConstPointerToArray<Element>::
 v() const {
   nassertd((this->_void_ptr) != NULL) {
-    ((ConstPointerToArray<Element> *)this)->reassign(new RefCountObj<pvector<Element> >);
+    ((ConstPointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
   }
   return *(To *)(this->_void_ptr);
 }
@@ -783,6 +820,43 @@ get_ref_count() const {
   return ((this->_void_ptr) == NULL) ? 0 : ((To *)(this->_void_ptr))->get_ref_count();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ConstPointerToArray::get_node_ref_count
+//       Access: Public
+//  Description: Returns the node_ref of the underlying vector.
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE int ConstPointerToArray<Element>::
+get_node_ref_count() const {
+  return ((this->_void_ptr) == NULL) ? 0 : ((To *)(this->_void_ptr))->get_node_ref_count();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConstPointerToArray::node_ref
+//       Access: Public
+//  Description: Increments the node_ref of the underlying vector.
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE void ConstPointerToArray<Element>::
+node_ref() const {
+  if ((this->_void_ptr) == NULL) {
+    ((PointerToArray<Element> *)this)->reassign(new NodeRefCountObj<pvector<Element> >);
+  }
+  ((To *)(this->_void_ptr))->node_ref();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConstPointerToArray::node_unref
+//       Access: Public
+//  Description: Decrements the node_ref of the underlying vector.
+////////////////////////////////////////////////////////////////////
+template<class Element>
+INLINE bool ConstPointerToArray<Element>::
+node_unref() const {
+  nassertr((this->_void_ptr) != NULL, true);
+  return ((To *)(this->_void_ptr))->node_unref();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ConstPointerToArray::Assignment operator
 //       Access: Public
@@ -790,7 +864,7 @@ get_ref_count() const {
 ////////////////////////////////////////////////////////////////////
 template<class Element>
 INLINE ConstPointerToArray<Element> &ConstPointerToArray<Element>::
-operator = (RefCountObj<pvector<Element> > *ptr) {
+operator = (NodeRefCountObj<pvector<Element> > *ptr) {
   reassign(ptr);
   return *this;
 }
@@ -829,6 +903,6 @@ operator = (const ConstPointerToArray<Element> &copy) {
 template<class Element>
 INLINE void ConstPointerToArray<Element>::
 clear() {
-  reassign((RefCountObj<pvector<Element> > *)NULL);
+  reassign((NodeRefCountObj<pvector<Element> > *)NULL);
 }
 

+ 25 - 7
panda/src/express/pointerToArray.h

@@ -70,7 +70,7 @@
 
 #include "pandabase.h"
 
-#include "referenceCount.h"
+#include "nodeReferenceCount.h"
 #include "pointerTo.h"
 #include "pvector.h"
 
@@ -87,11 +87,21 @@
 //               element.  This is actually implemented as an STL
 //               vector, using the RefCountObj class to wrap it up
 //               with a reference count.
+//
+//               We actually inherit from NodeRefCountObj these days,
+//               which adds node_ref() and node_unref() to the
+//               standard ref() and unref().  This is particularly
+//               useful for GeomVertexArrayData; other classes may or
+//               may not find this additional counter useful, but
+//               since it adds relatively little overhead (compared
+//               with what is presumably a largish array), we go ahead
+//               and add it here, even though it is inherited by many
+//               different parts of the system that may not use it.
 ////////////////////////////////////////////////////////////////////
 template <class Element>
-class PointerToArray : public PointerToBase<RefCountObj<pvector<Element> > > {
+class PointerToArray : public PointerToBase<NodeRefCountObj<pvector<Element> > > {
 public:
-  typedef TYPENAME PointerToBase<RefCountObj<pvector<Element> > >::To To;
+  typedef TYPENAME PointerToBase<NodeRefCountObj<pvector<Element> > >::To To;
   typedef TYPENAME pvector<Element>::value_type value_type;
   typedef TYPENAME pvector<Element>::reference reference;
   typedef TYPENAME pvector<Element>::const_reference const_reference;
@@ -176,9 +186,13 @@ public:
 
   INLINE int get_ref_count() const;
 
+  INLINE int get_node_ref_count() const;
+  INLINE void node_ref() const;
+  INLINE bool node_unref() const;
+
   // Reassignment is by pointer, not memberwise as with a vector.
   INLINE PointerToArray<Element> &
-  operator = (RefCountObj<pvector<Element> > *ptr);
+  operator = (NodeRefCountObj<pvector<Element> > *ptr);
   INLINE PointerToArray<Element> &
   operator = (const PointerToArray<Element> &copy);
   INLINE void clear();
@@ -198,9 +212,9 @@ private:
 //               may not be modified.
 ////////////////////////////////////////////////////////////////////
 template <class Element>
-class ConstPointerToArray : public PointerToBase<RefCountObj<pvector<Element> > > {
+class ConstPointerToArray : public PointerToBase<NodeRefCountObj<pvector<Element> > > {
 public:
-  typedef TYPENAME PointerToBase<RefCountObj<pvector<Element> > >::To To;
+  typedef TYPENAME PointerToBase<NodeRefCountObj<pvector<Element> > >::To To;
   typedef TYPENAME pvector<Element>::value_type value_type;
   typedef TYPENAME pvector<Element>::const_reference reference;
   typedef TYPENAME pvector<Element>::const_reference const_reference;
@@ -258,9 +272,13 @@ public:
 
   INLINE int get_ref_count() const;
 
+  INLINE int get_node_ref_count() const;
+  INLINE void node_ref() const;
+  INLINE bool node_unref() const;
+
   // Reassignment is by pointer, not memberwise as with a vector.
   INLINE ConstPointerToArray<Element> &
-  operator = (RefCountObj<pvector<Element> > *ptr);
+  operator = (NodeRefCountObj<pvector<Element> > *ptr);
   INLINE ConstPointerToArray<Element> &
   operator = (const PointerToArray<Element> &copy);
   INLINE ConstPointerToArray<Element> &

+ 3 - 16
panda/src/gobj/geom.I

@@ -69,7 +69,7 @@ get_geom_rendering() const {
 ////////////////////////////////////////////////////////////////////
 INLINE Geom::UsageHint Geom::
 get_usage_hint() const {
-  CDReader cdata(_cycler);
+  CDLockedReader cdata(_cycler);
   if (!cdata->_got_usage_hint) {
     CDWriter cdataw(((Geom *)this)->_cycler, cdata, false);
     ((Geom *)this)->reset_usage_hint(cdataw);
@@ -463,7 +463,7 @@ INLINE GeomPipelineReader::
 GeomPipelineReader(const Geom *object, Thread *current_thread) :
   _object(object),
   _current_thread(current_thread),
-  _cdata(object->_cycler.read(current_thread))
+  _cdata(object->_cycler.read_unlocked(current_thread))
 {
 #ifdef _DEBUG
   nassertv(_object->test_ref_count_nonzero());
@@ -506,7 +506,7 @@ INLINE GeomPipelineReader::
   nassertv(_cdata->test_ref_count_nonzero());
 #endif  // DO_PIPELINING
 #endif // _DEBUG
-  _object->_cycler.release_read(_cdata);
+  //  _object->_cycler.release_read(_cdata);
 
 #ifdef _DEBUG
   _object = NULL;
@@ -534,19 +534,6 @@ get_current_thread() const {
   return _current_thread;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPipelineReader::check_usage_hint
-//       Access: Public
-//  Description: Ensures that the Geom's usage_hint cache has been
-//               computed.
-////////////////////////////////////////////////////////////////////
-INLINE void GeomPipelineReader::
-check_usage_hint() const {
-  if (!_cdata->_got_usage_hint) {
-    ((Geom *)_object)->reset_usage_hint((Geom::CData *)_cdata);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPipelineReader::get_primitive_type
 //       Access: Public

+ 30 - 1
panda/src/gobj/geom.cxx

@@ -729,7 +729,7 @@ check_valid(const GeomVertexData *vertex_data) const {
 ////////////////////////////////////////////////////////////////////
 CPT(BoundingVolume) Geom::
 get_bounds(Thread *current_thread) const {
-  CDReader cdata(_cycler, current_thread);
+  CDLockedReader cdata(_cycler, current_thread);
   if (cdata->_internal_bounds_stale) {
     CDWriter cdataw(((Geom *)this)->_cycler, cdata, false);
     if (cdataw->_user_bounds != (BoundingVolume *)NULL) {
@@ -1346,6 +1346,35 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _modified = Geom::get_next_modified();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::check_usage_hint
+//       Access: Public
+//  Description: Ensures that the Geom's usage_hint cache has been
+//               computed.
+////////////////////////////////////////////////////////////////////
+void GeomPipelineReader::
+check_usage_hint() const {
+  if (!_cdata->_got_usage_hint) {
+    // We'll need to get a fresh pointer, since another thread might
+    // already have modified the pointer on the object since we
+    // queried it.
+    {
+      Geom::CDWriter fresh_cdata(((Geom *)_object)->_cycler, _current_thread);
+      if (!fresh_cdata->_got_usage_hint) {
+        // The cache is still stale.  We have to do the work of
+        // freshening it.
+        ((Geom *)_object)->reset_usage_hint(fresh_cdata);
+        nassertv(fresh_cdata->_got_usage_hint);
+      }
+
+      // Save the new pointer, and then let the lock release itself.
+      ((GeomPipelineReader *)this)->_cdata = fresh_cdata;
+    }
+  }
+
+  nassertv(_cdata->_got_usage_hint);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPipelineReader::check_valid
 //       Access: Public

+ 4 - 2
panda/src/gobj/geom.h

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 #include "typedWritableReferenceCount.h"
 #include "cycleData.h"
+#include "cycleDataLockedReader.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "cycleDataStageReader.h"
@@ -261,6 +262,7 @@ private:
   };
  
   PipelineCycler<CData> _cycler;
+  typedef CycleDataLockedReader<CData> CDLockedReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataStageReader<CData> CDStageReader;
@@ -330,7 +332,7 @@ public:
   INLINE const Geom *get_object() const;
   INLINE Thread *get_current_thread() const;
 
-  INLINE void check_usage_hint() const;
+  void check_usage_hint() const;
 
   INLINE PrimitiveType get_primitive_type() const;
   INLINE ShadeModel get_shade_model() const;
@@ -350,7 +352,7 @@ public:
 private:
   const Geom *_object;
   Thread *_current_thread;
-  const Geom::CData *_cdata;
+  CPT(Geom::CData) _cdata;
 };
 
 INLINE ostream &operator << (ostream &out, const Geom &obj);

+ 1 - 2
panda/src/gobj/geomCacheEntry.cxx

@@ -82,10 +82,9 @@ record(Thread *current_thread) {
 ////////////////////////////////////////////////////////////////////
 void GeomCacheEntry::
 refresh(Thread *current_thread) {
-  nassertv(_next != (GeomCacheEntry *)NULL && _prev != (GeomCacheEntry *)NULL);
-
   GeomCacheManager *cache_mgr = GeomCacheManager::get_global_ptr();
   MutexHolder holder(cache_mgr->_lock);
+  nassertv(_next != (GeomCacheEntry *)NULL && _prev != (GeomCacheEntry *)NULL);
 
   remove_from_list();
   insert_before(cache_mgr->_list);

+ 2 - 15
panda/src/gobj/geomPrimitive.I

@@ -478,7 +478,7 @@ GeomPrimitivePipelineReader(const GeomPrimitive *object,
                             Thread *current_thread) :
   _object(object),
   _current_thread(current_thread),
-  _cdata(object->_cycler.read(current_thread)),
+  _cdata(object->_cycler.read_unlocked(current_thread)),
   _vertices_reader(NULL)
 {
   nassertv(_object->test_ref_count_nonzero());
@@ -529,7 +529,7 @@ INLINE GeomPrimitivePipelineReader::
   nassertv(_cdata->test_ref_count_nonzero());
 #endif  // DO_PIPELINING
 #endif // _DEBUG
-  _object->_cycler.release_read(_cdata);
+  //  _object->_cycler.release_read(_cdata);
 
 #ifdef _DEBUG
   _vertices_reader = NULL;
@@ -558,19 +558,6 @@ get_current_thread() const {
   return _current_thread;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitivePipelineReader::check_minmax
-//       Access: Public
-//  Description: Ensures that the primitive's minmax cache has been
-//               computed.
-////////////////////////////////////////////////////////////////////
-INLINE void GeomPrimitivePipelineReader::
-check_minmax() const {
-  if (!_cdata->_got_minmax) {
-    ((GeomPrimitive *)_object)->recompute_minmax((GeomPrimitive::CData *)_cdata);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitivePipelineReader::get_shade_model
 //       Access: Public

+ 28 - 0
panda/src/gobj/geomPrimitive.cxx

@@ -1491,6 +1491,34 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _got_minmax = false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::check_minmax
+//       Access: Public
+//  Description: Ensures that the primitive's minmax cache has been
+//               computed.
+////////////////////////////////////////////////////////////////////
+void GeomPrimitivePipelineReader::
+check_minmax() const {
+  if (!_cdata->_got_minmax) {
+    // We'll need to get a fresh pointer, since another thread might
+    // already have modified the pointer on the object since we
+    // queried it.
+    {
+      GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object)->_cycler, _current_thread);
+      if (!fresh_cdata->_got_minmax) {
+        // The cache is still stale.  We have to do the work of
+        // freshening it.
+        ((GeomPrimitive *)_object)->recompute_minmax(fresh_cdata);
+        nassertv(fresh_cdata->_got_minmax);
+      }
+
+      // Save the new pointer, and then let the lock release itself.
+      ((GeomPrimitivePipelineReader *)this)->_cdata = fresh_cdata;
+    }
+  }
+
+  nassertv(_cdata->_got_minmax);
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitivePipelineReader::get_first_vertex

+ 2 - 2
panda/src/gobj/geomPrimitive.h

@@ -309,7 +309,7 @@ public:
   INLINE const GeomPrimitive *get_object() const;
   INLINE Thread *get_current_thread() const;
 
-  INLINE void check_minmax() const;
+  void check_minmax() const;
 
   INLINE ShadeModel get_shade_model() const;
   INLINE UsageHint get_usage_hint() const;
@@ -334,7 +334,7 @@ public:
 private:
   const GeomPrimitive *_object;
   Thread *_current_thread;
-  const GeomPrimitive::CData *_cdata;
+  CPT(GeomPrimitive::CData) _cdata;
   GeomVertexArrayDataPipelineReader *_vertices_reader;
 };
 

+ 4 - 2
panda/src/gobj/geomVertexArrayData.I

@@ -183,6 +183,7 @@ INLINE GeomVertexArrayData::CData::
 CData() :
   _usage_hint(UH_unspecified)
 {
+  _data.node_ref();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -196,6 +197,7 @@ CData(const GeomVertexArrayData::CData &copy) :
   _data(copy._data),
   _modified(copy._modified)
 {
+  _data.node_ref();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -293,7 +295,7 @@ GeomVertexArrayDataPipelineReader(const GeomVertexArrayData *object,
                                   Thread *current_thread) :
   GeomVertexArrayDataPipelineBase((GeomVertexArrayData *)object,
                                   current_thread,
-                                  (GeomVertexArrayData::CData *)object->_cycler.read(current_thread))
+                                  (GeomVertexArrayData::CData *)object->_cycler.read_unlocked(current_thread))
 {
 #ifdef _DEBUG
   nassertv(_object->test_ref_count_nonzero());
@@ -338,7 +340,7 @@ INLINE GeomVertexArrayDataPipelineReader::
   nassertv(_cdata->test_ref_count_nonzero());
 #endif  // DO_PIPELINING
 #endif // _DEBUG
-  _object->_cycler.release_read(_cdata);
+  //  _object->_cycler.release_read(_cdata);
 
 #ifdef _DEBUG
   _object = NULL;

+ 25 - 5
panda/src/gobj/geomVertexArrayData.cxx

@@ -432,6 +432,9 @@ finalize(BamReader *manager) {
     // Now is the time to endian-reverse the data.
     cdata->_data = reverse_data_endianness(cdata->_data);
   }
+
+  // Now is also the time to node_ref the data.
+  cdata->_data.node_ref();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -470,6 +473,16 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   manager->read_cdata(scan, _cycler, this);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayData::CData::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+GeomVertexArrayData::CData::
+~CData() {
+  _data.node_unref();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexArrayData::CData::make_copy
 //       Access: Public, Virtual
@@ -520,7 +533,7 @@ set_num_rows(int n) {
   int delta = n - (_cdata->_data.size() / stride);
   
   if (delta != 0) {
-    if (_cdata->_data.get_ref_count() > 1) {
+    if (_cdata->_data.get_node_ref_count() > 1) {
       // Copy-on-write: the data is already reffed somewhere else,
       // so we're just going to make a copy.
       PTA_uchar new_data;
@@ -528,7 +541,9 @@ set_num_rows(int n) {
       new_data.insert(new_data.end(), n * stride, 0);
       memcpy(new_data, _cdata->_data, 
              min((size_t)(n * stride), _cdata->_data.size()));
+      _cdata->_data.node_unref();
       _cdata->_data = new_data;
+      _cdata->_data.node_ref();
       
     } else {
       // We've got the only reference to the data, so we can change
@@ -557,13 +572,16 @@ set_num_rows(int n) {
 ////////////////////////////////////////////////////////////////////
 PTA_uchar GeomVertexArrayDataPipelineWriter::
 modify_data() {
-  // Perform copy-on-write: if the reference count on the vertex data
-  // is greater than 1, assume some other GeomVertexData has the same
-  // pointer, so make a copy of it first.
-  if (_cdata->_data.get_ref_count() > 1) {
+  // Perform copy-on-write: if the *node* reference count on the
+  // vertex data is greater than 1, assume some other
+  // GeomVertexArrayData has the same pointer, so make a copy of it
+  // first.
+  if (_cdata->_data.get_node_ref_count() > 1) {
     PTA_uchar orig_data = _cdata->_data;
+    _cdata->_data.node_unref();
     _cdata->_data = PTA_uchar();
     _cdata->_data.v() = orig_data.v();
+    _cdata->_data.node_ref();
   }
   _cdata->_modified = Geom::get_next_modified();
 
@@ -577,6 +595,8 @@ modify_data() {
 ////////////////////////////////////////////////////////////////////
 void GeomVertexArrayDataPipelineWriter::
 set_data(CPTA_uchar array) {
+  _cdata->_data.node_unref();
   _cdata->_data = (PTA_uchar &)array;
+  _cdata->_data.node_ref();
   _cdata->_modified = Geom::get_next_modified();
 }

+ 2 - 1
panda/src/gobj/geomVertexArrayData.h

@@ -120,6 +120,7 @@ private:
   public:
     INLINE CData();
     INLINE CData(const CData &copy);
+    virtual ~CData();
     ALLOC_DELETED_CHAIN(CData);
     virtual CycleData *make_copy() const;
     virtual void write_datagram(BamWriter *manager, Datagram &dg,
@@ -207,7 +208,7 @@ public:
 protected:
   GeomVertexArrayData *_object;
   Thread *_current_thread;
-  GeomVertexArrayData::CData *_cdata;
+  PT(GeomVertexArrayData::CData) _cdata;
 };
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/gobj/geomVertexData.I

@@ -590,7 +590,7 @@ INLINE GeomVertexDataPipelineReader::
 GeomVertexDataPipelineReader(const GeomVertexData *object, 
                              Thread *current_thread) :
   GeomVertexDataPipelineBase((GeomVertexData *)object, current_thread,
-                             (GeomVertexData::CData *)object->_cycler.read(current_thread)),
+                             (GeomVertexData::CData *)object->_cycler.read_unlocked(current_thread)),
   _got_array_readers(false)
 {
   nassertv(_object->test_ref_count_nonzero());
@@ -637,7 +637,7 @@ INLINE GeomVertexDataPipelineReader::
   nassertv(_cdata->test_ref_count_nonzero());
 #endif  // DO_PIPELINING
 #endif // _DEBUG
-  _object->_cycler.release_read(_cdata);
+  //  _object->_cycler.release_read(_cdata);
 
 #ifdef _DEBUG
   _object = NULL;

+ 8 - 7
panda/src/gobj/geomVertexData.cxx

@@ -243,7 +243,7 @@ set_format(const GeomVertexFormat *format) {
   Thread *current_thread = Thread::get_current_thread();
   nassertv(format->is_registered());
 
-  CDReader cdata(_cycler, current_thread);
+  CDLockedReader cdata(_cycler, current_thread);
 
   if (format == cdata->_format) {
     // Trivially no-op.
@@ -902,7 +902,7 @@ set_color(const Colorf &color, int num_components,
 ////////////////////////////////////////////////////////////////////
 CPT(GeomVertexData) GeomVertexData::
 animate_vertices(Thread *current_thread) const {
-  CDReader cdata(_cycler, current_thread);
+  CDLockedReader cdata(_cycler, current_thread);
 
   if (cdata->_format->get_animation().get_animation_type() != AT_panda) {
     return this;
@@ -1812,13 +1812,14 @@ set_num_rows(int n) {
   if (color_array >= 0 && orig_color_rows < n) {
     // We have just added some rows, fill the "color" column with
     // (1, 1, 1, 1), for the programmer's convenience.
-    GeomVertexArrayData *array_data = _cdata->_arrays[color_array];
+    GeomVertexArrayDataPipelineWriter *array_writer = _array_writers[color_array];
+    const GeomVertexArrayFormat *array_format = array_writer->get_array_format();
     const GeomVertexColumn *column = 
-      array_data->get_array_format()->get_column(InternalName::get_color());
-    int stride = array_data->get_array_format()->get_stride();
+      array_format->get_column(InternalName::get_color());
+    int stride = array_format->get_stride();
     unsigned char *start = 
-      array_data->modify_data() + column->get_start();
-    unsigned char *stop = start + array_data->get_data_size_bytes();
+      array_writer->modify_data() + column->get_start();
+    unsigned char *stop = start + array_writer->get_data_size_bytes();
     unsigned char *pointer = start + stride * orig_color_rows;
     int num_values = column->get_num_values();
 

+ 3 - 1
panda/src/gobj/geomVertexData.h

@@ -31,6 +31,7 @@
 #include "sliderTable.h"
 #include "internalName.h"
 #include "cycleData.h"
+#include "cycleDataLockedReader.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "cycleDataStageReader.h"
@@ -257,6 +258,7 @@ private:
   };
 
   PipelineCycler<CData> _cycler;
+  typedef CycleDataLockedReader<CData> CDLockedReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataStageReader<CData> CDStageReader;
@@ -341,7 +343,7 @@ public:
 protected:
   GeomVertexData *_object;
   Thread *_current_thread;
-  GeomVertexData::CData *_cdata;
+  PT(GeomVertexData::CData) _cdata;
 };
 
 ////////////////////////////////////////////////////////////////////

+ 10 - 2
panda/src/gobj/geomVertexReader.I

@@ -147,6 +147,7 @@ GeomVertexReader(const GeomVertexReader &copy) :
   _current_thread(copy._current_thread),
   _packer(copy._packer),
   _stride(copy._stride),
+  _data(copy._data),
   _pointer_begin(copy._pointer_begin),
   _pointer_end(copy._pointer_end),
   _pointer(copy._pointer),
@@ -167,6 +168,7 @@ operator = (const GeomVertexReader &copy) {
   _current_thread = copy._current_thread;
   _packer = copy._packer;
   _stride = copy._stride;
+  _data = copy._data;
   _pointer_begin = copy._pointer_begin;
   _pointer_end = copy._pointer_end;
   _pointer = copy._pointer;
@@ -510,7 +512,8 @@ get_packer() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomVertexReader::
 set_pointer(int row, const GeomVertexArrayDataPipelineReader *array_reader) {
-  _pointer_begin = array_reader->get_data();
+  _data = array_reader->get_data();
+  _pointer_begin = _data;
   _pointer_end = _pointer_begin + array_reader->get_data_size_bytes();
   quick_set_pointer(row);
 }
@@ -526,7 +529,12 @@ INLINE void GeomVertexReader::
 quick_set_pointer(int row) {
   nassertv(has_column());
 
-#ifdef _DEBUG
+#if defined(_DEBUG) && !defined(HAVE_THREADS)
+  // Make sure we still have the same pointer as stored the array.  We
+  // only perform this test in debug mode, because it is expensive,
+  // and only in single-threaded mode, because in threaded mode
+  // another thread might have reallocated the other pointer outside
+  // of our control anyway.
   if (_vertex_data != (const GeomVertexData *)NULL) {
     const GeomVertexArrayData *array_data = _vertex_data->get_array(_array);
     nassertv(_pointer_begin == array_data->get_data());

+ 1 - 0
panda/src/gobj/geomVertexReader.h

@@ -143,6 +143,7 @@ private:
   GeomVertexColumn::Packer *_packer;
   int _stride;
 
+  CPTA_uchar _data;
   const unsigned char *_pointer_begin;
   const unsigned char *_pointer_end;
   const unsigned char *_pointer;

+ 10 - 2
panda/src/gobj/geomVertexWriter.I

@@ -145,6 +145,7 @@ GeomVertexWriter(const GeomVertexWriter &copy) :
   _current_thread(copy._current_thread),
   _packer(copy._packer),
   _stride(copy._stride),
+  _data(copy._data),
   _pointer_begin(copy._pointer_begin),
   _pointer_end(copy._pointer_end),
   _pointer(copy._pointer),
@@ -165,6 +166,7 @@ operator = (const GeomVertexWriter &copy) {
   _current_thread = copy._current_thread;
   _packer = copy._packer;
   _stride = copy._stride;
+  _data = copy._data;
   _pointer_begin = copy._pointer_begin;
   _pointer_end = copy._pointer_end;
   _pointer = copy._pointer;
@@ -813,7 +815,8 @@ get_packer() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomVertexWriter::
 set_pointer(int row, GeomVertexArrayDataPipelineWriter *array_writer) {
-  _pointer_begin = array_writer->modify_data();
+  _data = array_writer->modify_data();
+  _pointer_begin = _data;
   _pointer_end = _pointer_begin + array_writer->get_data_size_bytes();
   quick_set_pointer(row);
 }
@@ -829,7 +832,12 @@ INLINE void GeomVertexWriter::
 quick_set_pointer(int row) {
   nassertv(has_column());
 
-#ifdef _DEBUG
+#if defined(_DEBUG) && !defined(HAVE_THREADS)
+  // Make sure we still have the same pointer as stored the array.  We
+  // only perform this test in debug mode, because it is expensive,
+  // and only in single-threaded mode, because in threaded mode
+  // another thread might have reallocated the other pointer outside
+  // of our control anyway.
   if (_vertex_data != (const GeomVertexData *)NULL) {
     const GeomVertexArrayData *array_data = _vertex_data->get_array(_array);
     nassertv(_pointer_begin == array_data->get_data());

+ 1 - 0
panda/src/gobj/geomVertexWriter.h

@@ -181,6 +181,7 @@ private:
   GeomVertexColumn::Packer *_packer;
   int _stride;
 
+  PTA_uchar _data;
   unsigned char *_pointer_begin;
   unsigned char *_pointer_end;
   unsigned char *_pointer;

+ 2 - 2
panda/src/gobj/transformBlend.I

@@ -211,7 +211,7 @@ set_weight(int n, float weight) {
 ////////////////////////////////////////////////////////////////////
 INLINE void TransformBlend::
 update_blend(Thread *current_thread) const {
-  CDReader cdata(_cycler, current_thread);
+  CDLockedReader cdata(_cycler, current_thread);
   if (cdata->_global_modified != VertexTransform::get_global_modified(current_thread)) {
     CDWriter cdataw(((TransformBlend *)this)->_cycler, cdata, false);
     ((TransformBlend *)this)->recompute_result(cdataw, current_thread);
@@ -290,7 +290,7 @@ transform_vector(LVector3f &vector, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 INLINE UpdateSeq TransformBlend::
 get_modified(Thread *current_thread) const {
-  CDReader cdata(_cycler, current_thread);
+  CDLockedReader cdata(_cycler, current_thread);
   if (cdata->_global_modified != VertexTransform::get_global_modified(current_thread)) {
     CDWriter cdataw(((TransformBlend *)this)->_cycler, cdata, false);
     ((TransformBlend *)this)->recompute_result(cdataw, current_thread);

+ 2 - 0
panda/src/gobj/transformBlend.h

@@ -25,6 +25,7 @@
 #include "pvector.h"
 #include "ordered_vector.h"
 #include "cycleData.h"
+#include "cycleDataLockedReader.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "pipelineCycler.h"
@@ -114,6 +115,7 @@ private:
   };
 
   PipelineCycler<CData> _cycler;
+  typedef CycleDataLockedReader<CData> CDLockedReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
 

+ 1 - 1
panda/src/gobj/transformBlendTable.I

@@ -48,7 +48,7 @@ get_blend(int n) const {
 ////////////////////////////////////////////////////////////////////
 INLINE UpdateSeq TransformBlendTable::
 get_modified(Thread *current_thread) const {
-  CDReader cdata(_cycler);
+  CDLockedReader cdata(_cycler);
   if (cdata->_global_modified != VertexTransform::get_global_modified(current_thread)) {
     CDWriter cdataw(((TransformBlendTable *)this)->_cycler, cdata, false);
     ((TransformBlendTable *)this)->recompute_modified(cdataw, current_thread);

+ 2 - 0
panda/src/gobj/transformBlendTable.h

@@ -28,6 +28,7 @@
 #include "pmap.h"
 #include "indirectLess.h"
 #include "cycleData.h"
+#include "cycleDataLockedReader.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "pipelineCycler.h"
@@ -115,6 +116,7 @@ private:
   };
 
   PipelineCycler<CData> _cycler;
+  typedef CycleDataLockedReader<CData> CDLockedReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
 

+ 1 - 1
panda/src/pgraph/nodePathComponent.cxx

@@ -133,7 +133,7 @@ get_next(int pipeline_stage, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 bool NodePathComponent::
 fix_length(int pipeline_stage, Thread *current_thread) {
-  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
 
   int length_should_be = 1;
   if (cdata->_next != (NodePathComponent *)NULL) {

+ 2 - 0
panda/src/pgraph/nodePathComponent.h

@@ -27,6 +27,7 @@
 #include "cycleData.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
+#include "cycleDataLockedStageReader.h"
 #include "cycleDataStageReader.h"
 #include "cycleDataStageWriter.h"
 #include "pmutex.h"
@@ -100,6 +101,7 @@ private:
   PipelineCycler<CData> _cycler;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
+  typedef CycleDataLockedStageReader<CData> CDLockedStageReader;
   typedef CycleDataStageReader<CData> CDStageReader;
   typedef CycleDataStageWriter<CData> CDStageWriter;
 

+ 11 - 21
panda/src/pgraph/pandaNode.I

@@ -1161,7 +1161,7 @@ INLINE PandaNodePipelineReader::
 PandaNodePipelineReader(const PandaNode *object, Thread *current_thread) :
   _object(object),
   _current_thread(current_thread),
-  _cdata(object->_cycler.read(current_thread))
+  _cdata(object->_cycler.read_unlocked(current_thread))
 {
 #ifdef _DEBUG
   nassertv(_object->test_ref_count_nonzero());
@@ -1182,9 +1182,11 @@ PandaNodePipelineReader(const PandaNodePipelineReader &copy) :
   _current_thread(copy._current_thread),
   _cdata(copy._cdata)
 {
+  /*
   if (_cdata != (PandaNode::CData *)NULL) {
     _object->_cycler.increment_read(_cdata);
   }
+  */
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1196,16 +1198,20 @@ INLINE void PandaNodePipelineReader::
 operator = (const PandaNodePipelineReader &copy) {
   nassertv(_current_thread == copy._current_thread);
 
+  /*
   if (_cdata != (PandaNode::CData *)NULL) {
     _object->_cycler.release_read(_cdata);
   }
+  */
 
   _object = copy._object;
   _cdata = copy._cdata;
 
+  /*
   if (_cdata != (PandaNode::CData *)NULL) {
     _object->_cycler.increment_read(_cdata);
   }
+  */
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1215,9 +1221,11 @@ operator = (const PandaNodePipelineReader &copy) {
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNodePipelineReader::
 ~PandaNodePipelineReader() {
+  /*
   if (_cdata != (PandaNode::CData *)NULL) {
     _object->_cycler.release_read(_cdata);
   }
+  */
 
 #ifdef _DEBUG
   _object = NULL;
@@ -1253,30 +1261,12 @@ get_current_thread() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNodePipelineReader::
 release() {
+  /*
   if (_cdata != (PandaNode::CData *)NULL) {
     _object->_cycler.release_read(_cdata);
     _cdata = NULL;
   }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNodePipelineReader::check_bounds
-//       Access: Public
-//  Description: Ensures that the bounding volume is properly computed
-//               on this node.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNodePipelineReader::
-check_bounds() const {
-  nassertv(_cdata != (PandaNode::CData *)NULL);
-  if (_cdata->_last_update != _cdata->_next_update) {
-    // The cache is stale; it needs to be rebuilt.
-
-    // We should technically drop _cdata while we do this operation,
-    // but at the moment, it doesn't really matter.
-    int pipeline_stage = _current_thread->get_pipeline_stage();
-    PandaNode::CDStageReader fresh_cdata(_object->_cycler, pipeline_stage, _current_thread);
-    ((PandaNode *)_object)->update_bounds(pipeline_stage, fresh_cdata); 
-  }
+  */
 }
 
 ////////////////////////////////////////////////////////////////////

+ 61 - 26
panda/src/pgraph/pandaNode.cxx

@@ -1404,8 +1404,7 @@ copy_tags(PandaNode *other) {
   Thread *current_thread = Thread::get_current_thread();
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
     CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
-    CDStageReader cdatar(other->_cycler, pipeline_stage,
-                              current_thread);
+    CDStageReader cdatar(other->_cycler, pipeline_stage, current_thread);
       
     TagData::const_iterator ti;
     for (ti = cdatar->_tag_data.begin();
@@ -1565,7 +1564,7 @@ DrawMask PandaNode::
 get_net_draw_control_mask() const {
   Thread *current_thread = Thread::get_current_thread();
   int pipeline_stage = current_thread->get_pipeline_stage();
-  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
   if (cdata->_last_update != cdata->_next_update) {
     // The cache is stale; it needs to be rebuilt.
     CDStageWriter cdataw = 
@@ -1595,7 +1594,7 @@ DrawMask PandaNode::
 get_net_draw_show_mask() const {
   Thread *current_thread = Thread::get_current_thread();
   int pipeline_stage = current_thread->get_pipeline_stage();
-  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
   if (cdata->_last_update != cdata->_next_update) {
     // The cache is stale; it needs to be rebuilt.
     CDStageWriter cdataw = 
@@ -1667,7 +1666,7 @@ get_legal_collide_mask() const {
 CollideMask PandaNode::
 get_net_collide_mask(Thread *current_thread) const {
   int pipeline_stage = current_thread->get_pipeline_stage();
-  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
   if (cdata->_last_update != cdata->_next_update) {
     // The cache is stale; it needs to be rebuilt.
     CDStageWriter cdataw = 
@@ -1687,7 +1686,7 @@ get_net_collide_mask(Thread *current_thread) const {
 CPT(RenderAttrib) PandaNode::
 get_off_clip_planes(Thread *current_thread) const {
   int pipeline_stage = current_thread->get_pipeline_stage();
-  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
   if (cdata->_last_update != cdata->_next_update) {
     // The cache is stale; it needs to be rebuilt.
     CDStageWriter cdataw = 
@@ -1808,7 +1807,7 @@ set_bound(const BoundingVolume *volume) {
 CPT(BoundingVolume) PandaNode::
 get_bounds(Thread *current_thread) const {
   int pipeline_stage = current_thread->get_pipeline_stage();
-  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
   if (cdata->_last_update != cdata->_next_update) {
     // The cache is stale; it needs to be rebuilt.
     CPT(BoundingVolume) result;
@@ -1900,7 +1899,7 @@ as_light() {
 ////////////////////////////////////////////////////////////////////
 CPT(BoundingVolume) PandaNode::
 get_internal_bounds(int pipeline_stage, Thread *current_thread) const {
-  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
   if (cdata->_internal_bounds_stale) {
     CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage, cdata);
     if (cdataw->_user_bounds != (BoundingVolume *)NULL) {
@@ -2868,7 +2867,7 @@ r_list_descendants(ostream &out, int indent_level) const {
 //               released.  The new value is returned.
 ////////////////////////////////////////////////////////////////////
 PandaNode::CDStageWriter PandaNode::
-update_bounds(int pipeline_stage, PandaNode::CDStageReader &cdata) {
+update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
   // We might need to try this a couple of times, in case someone else
   // steps on our result.
   if (drawmask_cat.is_debug()) {
@@ -2878,24 +2877,20 @@ update_bounds(int pipeline_stage, PandaNode::CDStageReader &cdata) {
   Thread *current_thread = cdata.get_current_thread();
 
   do {
-    // Grab the last_update counter, then release the lock.
+    // Grab the last_update counter.
     UpdateSeq last_update = cdata->_last_update;
     UpdateSeq next_update = cdata->_next_update;
     nassertr(last_update != next_update, CDStageWriter(_cycler, pipeline_stage, cdata));
-    _cycler.release_read_stage(pipeline_stage, cdata.take_pointer());
 
     // Start with a clean slate, or at least with the contents of the
     // node itself.
-    CollideMask net_collide_mask;
+    CollideMask net_collide_mask = cdata->_into_collide_mask;
     DrawMask net_draw_control_mask, net_draw_show_mask;
     DrawMask draw_control_mask, draw_show_mask;
+    draw_control_mask = net_draw_control_mask = cdata->_draw_control_mask;
+    draw_show_mask = net_draw_show_mask = cdata->_draw_show_mask;
     bool renderable = is_renderable();
-    {
-      CDStageReader cdata(_cycler, pipeline_stage, current_thread);
-      net_collide_mask = cdata->_into_collide_mask;
-      draw_control_mask = net_draw_control_mask = cdata->_draw_control_mask;
-      draw_show_mask = net_draw_show_mask = cdata->_draw_show_mask;
-    }
+
     if (!renderable) {
       // This is not a "renderable" node.  This means that this node
       // does not itself contribute any bits to net_draw_show_mask or
@@ -2914,15 +2909,18 @@ update_bounds(int pipeline_stage, PandaNode::CDStageReader &cdata) {
         << "\ndraw_show_mask = " << draw_show_mask
         << "\n";
     }
-    CPT(RenderAttrib) off_clip_planes;
-    {
-      CDStageReader cdata(_cycler, pipeline_stage, current_thread);
-      off_clip_planes = cdata->_state->get_clip_plane();
-    }
+    CPT(RenderAttrib) off_clip_planes = cdata->_state->get_clip_plane();
     if (off_clip_planes == (RenderAttrib *)NULL) {
       off_clip_planes = ClipPlaneAttrib::make();
     }
 
+    // Also get the list of the node's children.
+    Children children(cdata);
+
+    // Now that we've got all the data we need from the node, we can
+    // release the lock.
+    _cycler.release_read_stage(pipeline_stage, cdata.take_pointer());
+
     // We need to keep references to the bounding volumes, since in a
     // threaded environment the pointers might go away while we're
     // working (since we're not holding a lock on our set of children
@@ -2937,7 +2935,6 @@ update_bounds(int pipeline_stage, PandaNode::CDStageReader &cdata) {
     child_volumes.push_back(internal_bounds);
 
     // Now expand those contents to include all of our children.
-    Children children = get_children(current_thread);
     int num_children = children.get_num_children();
 
     for (int i = 0; i < num_children; ++i) {
@@ -2945,7 +2942,7 @@ update_bounds(int pipeline_stage, PandaNode::CDStageReader &cdata) {
 
       const ClipPlaneAttrib *orig_cp = DCAST(ClipPlaneAttrib, off_clip_planes);
       
-      CDStageReader child_cdata(child->_cycler, pipeline_stage, current_thread);
+      CDLockedStageReader child_cdata(child->_cycler, pipeline_stage, current_thread);
       if (child_cdata->_last_update != child_cdata->_next_update) {
 	// Child needs update.
 	CDStageWriter child_cdataw = child->update_bounds(pipeline_stage, child_cdata);
@@ -3089,7 +3086,7 @@ update_bounds(int pipeline_stage, PandaNode::CDStageReader &cdata) {
 
     // We need to go around again.  Release the write lock, and grab
     // the read lock back.
-    cdata = CDStageReader(_cycler, pipeline_stage, current_thread);
+    cdata = CDLockedStageReader(_cycler, pipeline_stage, current_thread);
 
     if (cdata->_last_update == cdata->_next_update) {
       // Someone else has computed the cache for us while we were
@@ -3633,3 +3630,41 @@ fillin_down_list(PandaNode::Down &down_list,
     down_list.push_back(DownConnection(NULL, sort));
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNodePipelineReader::check_bounds
+//       Access: Public
+//  Description: Ensures that the bounding volume is properly computed
+//               on this node.
+////////////////////////////////////////////////////////////////////
+void PandaNodePipelineReader::
+check_bounds() const {
+  if (_cdata->_last_update != _cdata->_next_update) {
+    // The cache is stale; it needs to be rebuilt.
+
+    // We'll need to get a fresh read pointer, since another thread
+    // might already have modified the pointer on the object since we
+    // queried it.
+    {
+      int pipeline_stage = _current_thread->get_pipeline_stage();
+      PandaNode::CDLockedStageReader fresh_cdata(_object->_cycler, pipeline_stage, _current_thread);
+      if (fresh_cdata->_last_update == fresh_cdata->_next_update) {
+        // What luck, some other thread has already freshened the
+        // cache for us.  Save the new pointer, and let the lock
+        // release itself.
+        ((PandaNodePipelineReader *)this)->_cdata = fresh_cdata;
+
+      } else {
+        // No, the cache is still stale.  We have to do the work of
+        // freshening it.
+        PandaNode::CDStageWriter cdataw = ((PandaNode *)_object)->update_bounds(pipeline_stage, fresh_cdata);
+        nassertv(cdataw->_last_update == cdataw->_next_update);
+        // As above, we save the new pointer, and then let the lock
+        // release itself.
+        ((PandaNodePipelineReader *)this)->_cdata = cdataw;
+      }
+    }
+  }
+
+  nassertv(_cdata->_last_update == _cdata->_next_update);
+}

+ 5 - 3
panda/src/pgraph/pandaNode.h

@@ -24,6 +24,7 @@
 #include "cycleData.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
+#include "cycleDataLockedStageReader.h"
 #include "cycleDataStageReader.h"
 #include "cycleDataStageWriter.h"
 #include "pipelineCycler.h"
@@ -505,10 +506,11 @@ private:
   PipelineCycler<CData> _cycler;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
+  typedef CycleDataLockedStageReader<CData> CDLockedStageReader;
   typedef CycleDataStageReader<CData> CDStageReader;
   typedef CycleDataStageWriter<CData> CDStageWriter;
 
-  CDStageWriter update_bounds(int pipeline_stage, CDStageReader &cdata);
+  CDStageWriter update_bounds(int pipeline_stage, CDLockedStageReader &cdata);
 
   static DrawMask _overall_bit;
 
@@ -631,7 +633,7 @@ public:
 
   INLINE void release();
 
-  INLINE void check_bounds() const;
+  void check_bounds() const;
 
   INLINE void compose_draw_mask(DrawMask &running_draw_mask) const;
   INLINE bool compare_draw_mask(DrawMask running_draw_mask,
@@ -671,7 +673,7 @@ public:
 private:
   const PandaNode *_object;
   Thread *_current_thread;
-  const PandaNode::CData *_cdata;
+  CPT(PandaNode::CData) _cdata;
 };
 
 INLINE ostream &operator << (ostream &out, const PandaNode &node) {

+ 1 - 1
panda/src/pgraph/planeNode.cxx

@@ -222,7 +222,7 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 PT(Geom) PlaneNode::
 get_viz(CullTraverser *trav, CullTraverserData &data) {
-  CDReader cdata(_cycler);
+  CDLockedReader cdata(_cycler);
   
   // Figure out whether we are looking at the front or the back of the
   // plane.

+ 2 - 0
panda/src/pgraph/planeNode.h

@@ -26,6 +26,7 @@
 #include "updateSeq.h"
 #include "geom.h"
 #include "cycleData.h"
+#include "cycleDataLockedReader.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "cycleDataStageReader.h"
@@ -97,6 +98,7 @@ private:
   };
 
   PipelineCycler<CData> _cycler;
+  typedef CycleDataLockedReader<CData> CDLockedReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataStageReader<CData> CDStageReader;

+ 6 - 0
panda/src/pipeline/Sources.pp

@@ -22,6 +22,8 @@
     conditionVarSpinlockImpl.h conditionVarSpinlockImpl.I \
     config_pipeline.h \
     cycleData.h cycleData.I \
+    cycleDataLockedReader.h cycleDataLockedReader.I \
+    cycleDataLockedStageReader.h cycleDataLockedStageReader.I \
     cycleDataReader.h cycleDataReader.I \
     cycleDataStageReader.h cycleDataStageReader.I \
     cycleDataStageWriter.h cycleDataStageWriter.I \
@@ -62,6 +64,8 @@
     conditionVarSpinlockImpl.cxx \
     config_pipeline.cxx \
     cycleData.cxx \
+    cycleDataLockedReader.cxx \
+    cycleDataLockedStageReader.cxx \
     cycleDataReader.cxx \
     cycleDataStageReader.cxx \
     cycleDataStageWriter.cxx \
@@ -100,6 +104,8 @@
     conditionVarSpinlockImpl.h conditionVarSpinlockImpl.I \
     config_pipeline.h \
     cycleData.h cycleData.I \
+    cycleDataLockedReader.h cycleDataLockedReader.I \
+    cycleDataLockedStageReader.h cycleDataLockedStageReader.I \
     cycleDataReader.h cycleDataReader.I \
     cycleDataStageReader.h cycleDataStageReader.I \
     cycleDataStageWriter.h cycleDataStageWriter.I \

+ 6 - 3
panda/src/pipeline/cycleData.h

@@ -21,7 +21,7 @@
 
 #include "pandabase.h"
 #include "typeHandle.h"
-#include "referenceCount.h"
+#include "nodeReferenceCount.h"
 
 class BamWriter;
 class BamReader;
@@ -40,8 +40,11 @@ class DatagramIterator;
 
 // If we are compiling in pipelining support, we maintain a pointer to
 // a CycleData object in each containing class, instead of the object
-// itself.  Thus, it should be a ReferenceCount object.
-class EXPCL_PANDA CycleData : public ReferenceCount 
+// itself.  Thus, it should be a ReferenceCount object.  Furthermore,
+// since we want to make a distinction between references within the
+// cycler, and references outside the cycler
+// (e.g. GeomPipelineReader), we make it a NodeReferenceCount.
+class EXPCL_PANDA CycleData : public NodeReferenceCount 
 
 #else  // !DO_PIPELINING
 

+ 242 - 0
panda/src/pipeline/cycleDataLockedReader.I

@@ -0,0 +1,242 @@
+// Filename: cycleDataLockedReader.I
+// Created by:  drose (30Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifdef DO_PIPELINING
+// This is the implementation for full support of pipelining (as well
+// as the sanity-check only implementation).
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedReader<CycleDataType>::
+CycleDataLockedReader(const PipelineCycler<CycleDataType> &cycler,
+                      Thread *current_thread) :
+  _cycler(&cycler),
+  _current_thread(current_thread)
+{
+  _pointer = _cycler->read(_current_thread);
+  nassertv(_pointer != (const CycleDataType *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::Copy Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedReader<CycleDataType>::
+CycleDataLockedReader(const CycleDataLockedReader<CycleDataType> &copy) :
+  _cycler(copy._cycler),
+  _current_thread(copy._current_thread),
+  _pointer(copy._pointer)
+{
+  nassertv(_pointer != (const CycleDataType *)NULL);
+  _cycler->increment_read(_pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::Copy Assignment (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void CycleDataLockedReader<CycleDataType>::
+operator = (const CycleDataLockedReader<CycleDataType> &copy) {
+  nassertv(_pointer == (CycleDataType *)NULL);
+  nassertv(_current_thread == copy._current_thread);
+
+  _cycler = copy._cycler;
+  _pointer = copy._pointer;
+
+  nassertv(_pointer != (const CycleDataType *)NULL);
+  _cycler->increment_read(_pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::Destructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedReader<CycleDataType>::
+~CycleDataLockedReader() {
+  if (_pointer != NULL) {
+    _cycler->release_read(_pointer);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::operator -> (full)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataLockedReader<CycleDataType>::
+operator -> () const {
+  nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::Typecast pointer (full)
+//       Access: Public
+//  Description: This allows the CycleDataLockedReader to be passed to any
+//               function that expects a const CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedReader<CycleDataType>::
+operator const CycleDataType * () const {
+  nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::take_pointer (full)
+//       Access: Public
+//  Description: This is intended to be called only from
+//               CycleDataWriter when it elevates the pointer from
+//               read to write status.  This function returns the
+//               reader's pointer and relinquishes ownership of the
+//               pointer, rendering the reader invalid for future
+//               reads.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataLockedReader<CycleDataType>::
+take_pointer() {
+  const CycleDataType *pointer = _pointer;
+  _pointer = (CycleDataType *)NULL;
+  nassertr(pointer != (const CycleDataType *)NULL, _cycler->cheat());
+  return pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::get_current_thread (full)
+//       Access: Public
+//  Description: Returns the Thread pointer of the currently-executing
+//               thread, as passed to the constructor of this object.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE Thread *CycleDataLockedReader<CycleDataType>::
+get_current_thread() const {
+  return _current_thread;
+}
+
+#else  // !DO_PIPELINING
+// This is the trivial, do-nothing implementation.
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedReader<CycleDataType>::
+CycleDataLockedReader(const PipelineCycler<CycleDataType> &cycler, Thread *) {
+  _pointer = cycler.cheat();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::Copy Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedReader<CycleDataType>::
+CycleDataLockedReader(const CycleDataLockedReader<CycleDataType> &copy) :
+  _pointer(copy._pointer)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::Copy Assignment (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void CycleDataLockedReader<CycleDataType>::
+operator = (const CycleDataLockedReader<CycleDataType> &copy) {
+  _pointer = copy._pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::Destructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedReader<CycleDataType>::
+~CycleDataLockedReader() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::operator -> (trivial)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataLockedReader<CycleDataType>::
+operator -> () const {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::Typecast pointer (trivial)
+//       Access: Public
+//  Description: This allows the CycleDataLockedReader to be passed to any
+//               function that expects a const CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedReader<CycleDataType>::
+operator const CycleDataType * () const {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::take_pointer (trivial)
+//       Access: Public
+//  Description: This is intended to be called only from
+//               CycleDataWriter when it elevates the pointer from
+//               read to write status.  This function returns the
+//               reader's pointer and relinquishes ownership of the
+//               pointer, rendering the reader invalid for future
+//               reads.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataLockedReader<CycleDataType>::
+take_pointer() {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedReader::get_current_thread (trivial)
+//       Access: Public
+//  Description: Returns the Thread pointer of the currently-executing
+//               thread, as passed to the constructor of this object.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE Thread *CycleDataLockedReader<CycleDataType>::
+get_current_thread() const {
+  return Thread::get_current_thread();
+}
+
+#endif  // DO_PIPELINING

+ 19 - 0
panda/src/pipeline/cycleDataLockedReader.cxx

@@ -0,0 +1,19 @@
+// Filename: cycleDataLockedReader.cxx
+// Created by:  drose (30Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "cycleDataLockedReader.h"

+ 81 - 0
panda/src/pipeline/cycleDataLockedReader.h

@@ -0,0 +1,81 @@
+// Filename: cycleDataLockedReader.h
+// Created by:  drose (30Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CYCLEDATALOCKEDREADER_H
+#define CYCLEDATALOCKEDREADER_H
+
+#include "pandabase.h"
+
+#include "cycleData.h"
+#include "pipelineCycler.h"
+#include "thread.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CycleDataLockedReader
+// Description : This template class calls PipelineCycler::read() in
+//               the constructor and PipelineCycler::release_read() in
+//               the destructor.  In the interim, it provides a
+//               transparent read-only access to the CycleData.
+//
+//               Since a lock is held on the data while the instance
+//               of this class exists, no other thread may modify any
+//               stage of the pipeline during that time.  Thus, this
+//               class is appropriate to use for cases in which you
+//               might want to read and then modify the data.  It is
+//               possible to pass an instance of CycleDataLockedReader
+//               to the CycleDataWriter constructor, which
+//               automatically elevates the read lock into a write
+//               lock.
+//
+//               It exists as a syntactic convenience to access the
+//               data in the CycleData.  It also allows the whole
+//               system to compile down to nothing if
+//               SUPPORT_PIPELINING is not defined.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+class CycleDataLockedReader {
+public:
+  INLINE CycleDataLockedReader(const PipelineCycler<CycleDataType> &cycler,
+                               Thread *current_thread = Thread::get_current_thread());
+  INLINE CycleDataLockedReader(const CycleDataLockedReader<CycleDataType> &copy);
+  INLINE void operator = (const CycleDataLockedReader<CycleDataType> &copy);
+  
+  INLINE ~CycleDataLockedReader();
+
+  INLINE const CycleDataType *operator -> () const;
+  INLINE operator const CycleDataType * () const;
+
+  INLINE const CycleDataType *take_pointer();
+  INLINE Thread *get_current_thread() const;
+
+private:
+#ifdef DO_PIPELINING
+  // This is the data stored for a real pipelining implementation.
+  const PipelineCycler<CycleDataType> *_cycler;
+  Thread *_current_thread;
+  const CycleDataType *_pointer;
+  CycleDataType *_write_pointer;
+#else  // !DO_PIPELINING
+  // This is all we need for the trivial, do-nothing implementation.
+  const CycleDataType *_pointer;
+#endif  // DO_PIPELINING
+};
+
+#include "cycleDataLockedReader.I"
+
+#endif

+ 247 - 0
panda/src/pipeline/cycleDataLockedStageReader.I

@@ -0,0 +1,247 @@
+// Filename: cycleDataLockedStageReader.I
+// Created by:  drose (30Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+#ifdef DO_PIPELINING
+// This is the implementation for full support of pipelining (as well
+// as the sanity-check only implementation).
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedStageReader<CycleDataType>::
+CycleDataLockedStageReader(const PipelineCycler<CycleDataType> &cycler,
+                           int stage, Thread *current_thread) :
+  _cycler(&cycler),
+  _current_thread(current_thread),
+  _stage(stage)
+{
+  _pointer = _cycler->read_stage(_stage, _current_thread);
+  nassertv(_pointer != (const CycleDataType *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::Copy Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedStageReader<CycleDataType>::
+CycleDataLockedStageReader(const CycleDataLockedStageReader<CycleDataType> &copy) :
+  _cycler(copy._cycler),
+  _current_thread(copy._current_thread),
+  _pointer(copy._pointer),
+  _stage(copy._stage)
+{
+  nassertv(_pointer != (const CycleDataType *)NULL);
+  _cycler->increment_read(_pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::Copy Assignment (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void CycleDataLockedStageReader<CycleDataType>::
+operator = (const CycleDataLockedStageReader<CycleDataType> &copy) {
+  nassertv(_pointer == (CycleDataType *)NULL);
+  nassertv(_current_thread == copy._current_thread);
+
+  _cycler = copy._cycler;
+  _pointer = copy._pointer;
+  _stage = copy._stage;
+
+  nassertv(_pointer != (const CycleDataType *)NULL);
+  _cycler->increment_read(_pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::Destructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedStageReader<CycleDataType>::
+~CycleDataLockedStageReader() {
+  if (_pointer != NULL) {
+    _cycler->release_read_stage(_stage, _pointer);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::operator -> (full)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataLockedStageReader<CycleDataType>::
+operator -> () const {
+  nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::Typecast pointer (full)
+//       Access: Public
+//  Description: This allows the CycleDataLockedStageReader to be passed to any
+//               function that expects a const CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedStageReader<CycleDataType>::
+operator const CycleDataType * () const {
+  nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::take_pointer (full)
+//       Access: Public
+//  Description: This is intended to be called only from
+//               CycleDataStageWriter when it elevates the pointer from
+//               read to write status.  This function returns the
+//               reader's pointer and relinquishes ownership of the
+//               pointer, rendering the reader invalid for future
+//               reads.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataLockedStageReader<CycleDataType>::
+take_pointer() {
+  const CycleDataType *pointer = _pointer;
+  _pointer = (CycleDataType *)NULL;
+  nassertr(pointer != (const CycleDataType *)NULL, _cycler->cheat());
+  return pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::get_current_thread (full)
+//       Access: Public
+//  Description: Returns the Thread pointer of the currently-executing
+//               thread, as passed to the constructor of this object.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE Thread *CycleDataLockedStageReader<CycleDataType>::
+get_current_thread() const {
+  return _current_thread;
+}
+
+#else  // !DO_PIPELINING
+// This is the trivial, do-nothing implementation.
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedStageReader<CycleDataType>::
+CycleDataLockedStageReader(const PipelineCycler<CycleDataType> &cycler, int,
+                           Thread *) {
+  _pointer = cycler.cheat();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::Copy Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedStageReader<CycleDataType>::
+CycleDataLockedStageReader(const CycleDataLockedStageReader<CycleDataType> &copy) :
+  _pointer(copy._pointer)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::Copy Assignment (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void CycleDataLockedStageReader<CycleDataType>::
+operator = (const CycleDataLockedStageReader<CycleDataType> &copy) {
+  _pointer = copy._pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::Destructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedStageReader<CycleDataType>::
+~CycleDataLockedStageReader() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::operator -> (trivial)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataLockedStageReader<CycleDataType>::
+operator -> () const {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::Typecast pointer (trivial)
+//       Access: Public
+//  Description: This allows the CycleDataLockedStageReader to be passed to any
+//               function that expects a const CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataLockedStageReader<CycleDataType>::
+operator const CycleDataType * () const {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::take_pointer (trivial)
+//       Access: Public
+//  Description: This is intended to be called only from
+//               CycleDataStageWriter when it elevates the pointer from
+//               read to write status.  This function returns the
+//               reader's pointer and relinquishes ownership of the
+//               pointer, rendering the reader invalid for future
+//               reads.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataLockedStageReader<CycleDataType>::
+take_pointer() {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataLockedStageReader::get_current_thread (trivial)
+//       Access: Public
+//  Description: Returns the Thread pointer of the currently-executing
+//               thread, as passed to the constructor of this object.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE Thread *CycleDataLockedStageReader<CycleDataType>::
+get_current_thread() const {
+  return Thread::get_current_thread();
+}
+
+#endif  // DO_PIPELINING

+ 19 - 0
panda/src/pipeline/cycleDataLockedStageReader.cxx

@@ -0,0 +1,19 @@
+// Filename: cycleDataLockedStageReader.cxx
+// Created by:  drose (30Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "cycleDataLockedStageReader.h"

+ 64 - 0
panda/src/pipeline/cycleDataLockedStageReader.h

@@ -0,0 +1,64 @@
+// Filename: cycleDataLockedStageReader.h
+// Created by:  drose (30Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CYCLEDATALOCKEDSTAGEREADER_H
+#define CYCLEDATALOCKEDSTAGEREADER_H
+
+#include "pandabase.h"
+
+#include "cycleData.h"
+#include "pipelineCycler.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CycleDataLockedStageReader
+// Description : This class is similar to CycleDataLockedReader,
+//               except it allows reading from a particular stage of
+//               the pipeline.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+class CycleDataLockedStageReader {
+public:
+  INLINE CycleDataLockedStageReader(const PipelineCycler<CycleDataType> &cycler, 
+                                    int stage, Thread *current_thread = Thread::get_current_thread());
+  INLINE CycleDataLockedStageReader(const CycleDataLockedStageReader<CycleDataType> &copy);
+  INLINE void operator = (const CycleDataLockedStageReader<CycleDataType> &copy);
+  
+  INLINE ~CycleDataLockedStageReader();
+
+  INLINE const CycleDataType *operator -> () const;
+  INLINE operator const CycleDataType * () const;
+
+  INLINE const CycleDataType *take_pointer();
+  INLINE Thread *get_current_thread() const;
+
+private:
+#ifdef DO_PIPELINING
+  // This is the data stored for a real pipelining implementation.
+  const PipelineCycler<CycleDataType> *_cycler;
+  Thread *_current_thread;
+  const CycleDataType *_pointer;
+  int _stage;
+#else  // !DO_PIPELINING
+  // This is all we need for the trivial, do-nothing implementation.
+  const CycleDataType *_pointer;
+#endif  // DO_PIPELINING
+};
+
+#include "cycleDataLockedStageReader.I"
+
+#endif

+ 1 - 48
panda/src/pipeline/cycleDataReader.I

@@ -32,8 +32,7 @@ CycleDataReader(const PipelineCycler<CycleDataType> &cycler,
   _cycler(&cycler),
   _current_thread(current_thread)
 {
-  _pointer = _cycler->read(_current_thread);
-  nassertv(_pointer != (const CycleDataType *)NULL);
+  _pointer = _cycler->read_unlocked(_current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -48,8 +47,6 @@ CycleDataReader(const CycleDataReader<CycleDataType> &copy) :
   _current_thread(copy._current_thread),
   _pointer(copy._pointer)
 {
-  nassertv(_pointer != (const CycleDataType *)NULL);
-  _cycler->increment_read(_pointer);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -60,14 +57,10 @@ CycleDataReader(const CycleDataReader<CycleDataType> &copy) :
 template<class CycleDataType>
 INLINE void CycleDataReader<CycleDataType>::
 operator = (const CycleDataReader<CycleDataType> &copy) {
-  nassertv(_pointer == (CycleDataType *)NULL);
   nassertv(_current_thread == copy._current_thread);
 
   _cycler = copy._cycler;
   _pointer = copy._pointer;
-
-  nassertv(_pointer != (const CycleDataType *)NULL);
-  _cycler->increment_read(_pointer);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -78,9 +71,6 @@ operator = (const CycleDataReader<CycleDataType> &copy) {
 template<class CycleDataType>
 INLINE CycleDataReader<CycleDataType>::
 ~CycleDataReader() {
-  if (_pointer != NULL) {
-    _cycler->release_read(_pointer);
-  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -92,7 +82,6 @@ INLINE CycleDataReader<CycleDataType>::
 template<class CycleDataType>
 INLINE const CycleDataType *CycleDataReader<CycleDataType>::
 operator -> () const {
-  nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
   return _pointer;
 }
 
@@ -105,29 +94,9 @@ operator -> () const {
 template<class CycleDataType>
 INLINE CycleDataReader<CycleDataType>::
 operator const CycleDataType * () const {
-  nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
   return _pointer;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CycleDataReader::take_pointer (full)
-//       Access: Public
-//  Description: This is intended to be called only from
-//               CycleDataWriter when it elevates the pointer from
-//               read to write status.  This function returns the
-//               reader's pointer and relinquishes ownership of the
-//               pointer, rendering the reader invalid for future
-//               reads.
-////////////////////////////////////////////////////////////////////
-template<class CycleDataType>
-INLINE const CycleDataType *CycleDataReader<CycleDataType>::
-take_pointer() {
-  const CycleDataType *pointer = _pointer;
-  _pointer = (CycleDataType *)NULL;
-  nassertr(pointer != (const CycleDataType *)NULL, _cycler->cheat());
-  return pointer;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CycleDataReader::get_current_thread (full)
 //       Access: Public
@@ -211,22 +180,6 @@ operator const CycleDataType * () const {
   return _pointer;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CycleDataReader::take_pointer (trivial)
-//       Access: Public
-//  Description: This is intended to be called only from
-//               CycleDataWriter when it elevates the pointer from
-//               read to write status.  This function returns the
-//               reader's pointer and relinquishes ownership of the
-//               pointer, rendering the reader invalid for future
-//               reads.
-////////////////////////////////////////////////////////////////////
-template<class CycleDataType>
-INLINE const CycleDataType *CycleDataReader<CycleDataType>::
-take_pointer() {
-  return _pointer;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CycleDataReader::get_current_thread (trivial)
 //       Access: Public

+ 8 - 5
panda/src/pipeline/cycleDataReader.h

@@ -27,10 +27,14 @@
 
 ////////////////////////////////////////////////////////////////////
 //       Class : CycleDataReader
-// Description : This template class calls PipelineCycler::read() in
-//               the constructor and PipelineCycler::release_read() in
-//               the destructor.  In the interim, it provides a
-//               transparent read-only access to the CycleData.
+// Description : This template class calls
+//               PipelineCycler::read_unlocked(), and then provides a
+//               transparent read-only access to the CycleData.  It is
+//               used to access the data quickly, without holding a
+//               lock, for a thread that does not intend to modify the
+//               data and write it back out.  For cases where the data
+//               might be subsequently modified, you should use
+//               CycleDataLockedReader.
 //
 //               It exists as a syntactic convenience to access the
 //               data in the CycleData.  It also allows the whole
@@ -50,7 +54,6 @@ public:
   INLINE const CycleDataType *operator -> () const;
   INLINE operator const CycleDataType * () const;
 
-  INLINE const CycleDataType *take_pointer();
   INLINE Thread *get_current_thread() const;
 
 private:

+ 1 - 48
panda/src/pipeline/cycleDataStageReader.I

@@ -34,8 +34,7 @@ CycleDataStageReader(const PipelineCycler<CycleDataType> &cycler,
   _current_thread(current_thread),
   _stage(stage)
 {
-  _pointer = _cycler->read_stage(_stage, _current_thread);
-  nassertv(_pointer != (const CycleDataType *)NULL);
+  _pointer = _cycler->read_stage_unlocked(_stage);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -51,8 +50,6 @@ CycleDataStageReader(const CycleDataStageReader<CycleDataType> &copy) :
   _pointer(copy._pointer),
   _stage(copy._stage)
 {
-  nassertv(_pointer != (const CycleDataType *)NULL);
-  _cycler->increment_read(_pointer);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -63,15 +60,11 @@ CycleDataStageReader(const CycleDataStageReader<CycleDataType> &copy) :
 template<class CycleDataType>
 INLINE void CycleDataStageReader<CycleDataType>::
 operator = (const CycleDataStageReader<CycleDataType> &copy) {
-  nassertv(_pointer == (CycleDataType *)NULL);
   nassertv(_current_thread == copy._current_thread);
 
   _cycler = copy._cycler;
   _pointer = copy._pointer;
   _stage = copy._stage;
-
-  nassertv(_pointer != (const CycleDataType *)NULL);
-  _cycler->increment_read(_pointer);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -82,9 +75,6 @@ operator = (const CycleDataStageReader<CycleDataType> &copy) {
 template<class CycleDataType>
 INLINE CycleDataStageReader<CycleDataType>::
 ~CycleDataStageReader() {
-  if (_pointer != NULL) {
-    _cycler->release_read_stage(_stage, _pointer);
-  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -96,7 +86,6 @@ INLINE CycleDataStageReader<CycleDataType>::
 template<class CycleDataType>
 INLINE const CycleDataType *CycleDataStageReader<CycleDataType>::
 operator -> () const {
-  nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
   return _pointer;
 }
 
@@ -109,29 +98,9 @@ operator -> () const {
 template<class CycleDataType>
 INLINE CycleDataStageReader<CycleDataType>::
 operator const CycleDataType * () const {
-  nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
   return _pointer;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CycleDataStageReader::take_pointer (full)
-//       Access: Public
-//  Description: This is intended to be called only from
-//               CycleDataStageWriter when it elevates the pointer from
-//               read to write status.  This function returns the
-//               reader's pointer and relinquishes ownership of the
-//               pointer, rendering the reader invalid for future
-//               reads.
-////////////////////////////////////////////////////////////////////
-template<class CycleDataType>
-INLINE const CycleDataType *CycleDataStageReader<CycleDataType>::
-take_pointer() {
-  const CycleDataType *pointer = _pointer;
-  _pointer = (CycleDataType *)NULL;
-  nassertr(pointer != (const CycleDataType *)NULL, _cycler->cheat());
-  return pointer;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CycleDataStageReader::get_current_thread (full)
 //       Access: Public
@@ -216,22 +185,6 @@ operator const CycleDataType * () const {
   return _pointer;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CycleDataStageReader::take_pointer (trivial)
-//       Access: Public
-//  Description: This is intended to be called only from
-//               CycleDataStageWriter when it elevates the pointer from
-//               read to write status.  This function returns the
-//               reader's pointer and relinquishes ownership of the
-//               pointer, rendering the reader invalid for future
-//               reads.
-////////////////////////////////////////////////////////////////////
-template<class CycleDataType>
-INLINE const CycleDataType *CycleDataStageReader<CycleDataType>::
-take_pointer() {
-  return _pointer;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CycleDataStageReader::get_current_thread (trivial)
 //       Access: Public

+ 0 - 1
panda/src/pipeline/cycleDataStageReader.h

@@ -43,7 +43,6 @@ public:
   INLINE const CycleDataType *operator -> () const;
   INLINE operator const CycleDataType * () const;
 
-  INLINE const CycleDataType *take_pointer();
   INLINE Thread *get_current_thread() const;
 
 private:

+ 8 - 8
panda/src/pipeline/cycleDataStageWriter.I

@@ -94,13 +94,13 @@ operator = (const CycleDataStageWriter<CycleDataType> &copy) {
 //     Function: CycleDataStageWriter::Constructor (full)
 //       Access: Public
 //  Description: This flavor of the constructor elevates the pointer
-//               from the CycleDataStageReader from a read to a write
+//               from the CycleDataLockedStageReader from a read to a write
 //               pointer (and invalidates the reader).
 ////////////////////////////////////////////////////////////////////
 template<class CycleDataType>
 INLINE CycleDataStageWriter<CycleDataType>::
 CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
-                     CycleDataStageReader<CycleDataType> &take_from) :
+                     CycleDataLockedStageReader<CycleDataType> &take_from) :
   _cycler(&cycler),
   _current_thread(take_from.get_current_thread()),
   _stage(stage)
@@ -113,13 +113,13 @@ CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
 //     Function: CycleDataStageWriter::Constructor (full)
 //       Access: Public
 //  Description: This flavor of the constructor elevates the pointer
-//               from the CycleDataStageReader from a read to a write
+//               from the CycleDataLockedStageReader from a read to a write
 //               pointer (and invalidates the reader).
 ////////////////////////////////////////////////////////////////////
 template<class CycleDataType>
 INLINE CycleDataStageWriter<CycleDataType>::
 CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
-                     CycleDataStageReader<CycleDataType> &take_from,
+                     CycleDataLockedStageReader<CycleDataType> &take_from,
                      bool force_to_0) :
   _cycler(&cycler),
   _current_thread(take_from.get_current_thread()),
@@ -245,13 +245,13 @@ operator = (const CycleDataStageWriter<CycleDataType> &copy) {
 //     Function: CycleDataStageWriter::Constructor (trivial)
 //       Access: Public
 //  Description: This flavor of the constructor elevates the pointer
-//               from the CycleDataStageReader from a read to a write
+//               from the CycleDataLockedStageReader from a read to a write
 //               pointer (and invalidates the reader).
 ////////////////////////////////////////////////////////////////////
 template<class CycleDataType>
 INLINE CycleDataStageWriter<CycleDataType>::
 CycleDataStageWriter(PipelineCycler<CycleDataType> &, int,
-                     CycleDataStageReader<CycleDataType> &take_from) :
+                     CycleDataLockedStageReader<CycleDataType> &take_from) :
   _pointer((CycleDataType *)take_from.take_pointer())
 {
 }
@@ -260,13 +260,13 @@ CycleDataStageWriter(PipelineCycler<CycleDataType> &, int,
 //     Function: CycleDataStageWriter::Constructor (trivial)
 //       Access: Public
 //  Description: This flavor of the constructor elevates the pointer
-//               from the CycleDataStageReader from a read to a write
+//               from the CycleDataLockedStageReader from a read to a write
 //               pointer (and invalidates the reader).
 ////////////////////////////////////////////////////////////////////
 template<class CycleDataType>
 INLINE CycleDataStageWriter<CycleDataType>::
 CycleDataStageWriter(PipelineCycler<CycleDataType> &, int,
-                     CycleDataStageReader<CycleDataType> &take_from,
+                     CycleDataLockedStageReader<CycleDataType> &take_from,
                      bool) :
   _pointer((CycleDataType *)take_from.take_pointer())
 {

+ 3 - 3
panda/src/pipeline/cycleDataStageWriter.h

@@ -23,7 +23,7 @@
 
 #include "cycleData.h"
 #include "pipelineCycler.h"
-#include "cycleDataStageReader.h"
+#include "cycleDataLockedStageReader.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : CycleDataStageWriter
@@ -46,9 +46,9 @@ public:
   INLINE void operator = (const CycleDataStageWriter<CycleDataType> &copy);
 
   INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
-                              CycleDataStageReader<CycleDataType> &take_from);
+                              CycleDataLockedStageReader<CycleDataType> &take_from);
   INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
-                              CycleDataStageReader<CycleDataType> &take_from,
+                              CycleDataLockedStageReader<CycleDataType> &take_from,
                               bool force_to_0);
 
   INLINE ~CycleDataStageWriter();

+ 8 - 8
panda/src/pipeline/cycleDataWriter.I

@@ -95,13 +95,13 @@ operator = (const CycleDataWriter<CycleDataType> &copy) {
 //     Function: CycleDataWriter::Constructor (full)
 //       Access: Public
 //  Description: This flavor of the constructor elevates the pointer
-//               from the CycleDataReader from a read to a write
+//               from the CycleDataLockedReader from a read to a write
 //               pointer (and invalidates the reader).
 ////////////////////////////////////////////////////////////////////
 template<class CycleDataType>
 INLINE CycleDataWriter<CycleDataType>::
 CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
-                CycleDataReader<CycleDataType> &take_from) :
+                CycleDataLockedReader<CycleDataType> &take_from) :
   _cycler(&cycler),
   _current_thread(take_from.get_current_thread())
 {
@@ -112,7 +112,7 @@ CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
 //     Function: CycleDataWriter::Constructor (full)
 //       Access: Public
 //  Description: This flavor of the constructor elevates the pointer
-//               from the CycleDataReader from a read to a write
+//               from the CycleDataLockedReader from a read to a write
 //               pointer (and invalidates the reader).  It also
 //               propagates the pointer back upstream; see
 //               PipelineCycler::write_upstream().
@@ -120,7 +120,7 @@ CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
 template<class CycleDataType>
 INLINE CycleDataWriter<CycleDataType>::
 CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
-                CycleDataReader<CycleDataType> &take_from,
+                CycleDataLockedReader<CycleDataType> &take_from,
 		bool force_to_0) :
   _cycler(&cycler),
   _current_thread(take_from.get_current_thread())
@@ -245,13 +245,13 @@ operator = (const CycleDataWriter<CycleDataType> &copy) {
 //     Function: CycleDataWriter::Constructor (trivial)
 //       Access: Public
 //  Description: This flavor of the constructor elevates the pointer
-//               from the CycleDataReader from a read to a write
+//               from the CycleDataLockedReader from a read to a write
 //               pointer (and invalidates the reader).
 ////////////////////////////////////////////////////////////////////
 template<class CycleDataType>
 INLINE CycleDataWriter<CycleDataType>::
 CycleDataWriter(PipelineCycler<CycleDataType> &,
-                CycleDataReader<CycleDataType> &take_from) :
+                CycleDataLockedReader<CycleDataType> &take_from) :
   _pointer((CycleDataType *)take_from.take_pointer())
 {
 }
@@ -260,7 +260,7 @@ CycleDataWriter(PipelineCycler<CycleDataType> &,
 //     Function: CycleDataWriter::Constructor (trivial)
 //       Access: Public
 //  Description: This flavor of the constructor elevates the pointer
-//               from the CycleDataReader from a read to a write
+//               from the CycleDataLockedReader from a read to a write
 //               pointer (and invalidates the reader).  It also
 //               propagates the pointer back upstream; see
 //               PipelineCycler::write_upstream().
@@ -268,7 +268,7 @@ CycleDataWriter(PipelineCycler<CycleDataType> &,
 template<class CycleDataType>
 INLINE CycleDataWriter<CycleDataType>::
 CycleDataWriter(PipelineCycler<CycleDataType> &,
-                CycleDataReader<CycleDataType> &take_from,
+                CycleDataLockedReader<CycleDataType> &take_from,
 		bool force_to_0) :
   _pointer((CycleDataType *)take_from.take_pointer())
 {

+ 3 - 3
panda/src/pipeline/cycleDataWriter.h

@@ -23,7 +23,7 @@
 
 #include "cycleData.h"
 #include "pipelineCycler.h"
-#include "cycleDataReader.h"
+#include "cycleDataLockedReader.h"
 #include "thread.h"
 
 ////////////////////////////////////////////////////////////////////
@@ -48,8 +48,8 @@ public:
   INLINE CycleDataWriter(const CycleDataWriter<CycleDataType> &copy);
   INLINE void operator = (const CycleDataWriter<CycleDataType> &copy);
 
-  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, CycleDataReader<CycleDataType> &take_from);
-  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, CycleDataReader<CycleDataType> &take_from, bool force_to_0);
+  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, CycleDataLockedReader<CycleDataType> &take_from);
+  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, CycleDataLockedReader<CycleDataType> &take_from, bool force_to_0);
 
   INLINE ~CycleDataWriter();
 

+ 45 - 1
panda/src/pipeline/pipelineCycler.I

@@ -56,6 +56,17 @@ operator = (const PipelineCycler<CycleDataType> &copy) {
   PipelineCyclerBase::operator = (copy);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::read_unlocked (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::read_unlocked().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *PipelineCycler<CycleDataType>::
+read_unlocked(Thread *current_thread) const {
+  return (const CycleDataType *)PipelineCyclerBase::read_unlocked(current_thread);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::read (dummy or true)
 //       Access: Public
@@ -113,7 +124,18 @@ elevate_read_upstream(const CycleDataType *pointer, bool force_to_0,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCycler::read (dummy or true)
+//     Function: PipelineCycler::read_stage_unlocked (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::read_stage_unlocked().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *PipelineCycler<CycleDataType>::
+read_stage_unlocked(int pipeline_stage) const {
+  return (const CycleDataType *)PipelineCyclerBase::read_stage_unlocked(pipeline_stage);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::read_stage (dummy or true)
 //       Access: Public
 //  Description: See PipelineCyclerBase::read_stage().
 ////////////////////////////////////////////////////////////////////
@@ -227,6 +249,17 @@ operator = (const PipelineCycler<CycleDataType> &copy) {
   _typed_data = copy._typed_data;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::read_unlocked (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::read_unlocked().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *PipelineCycler<CycleDataType>::
+read_unlocked(Thread *) const {
+  return &_typed_data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::read (trivial)
 //       Access: Public
@@ -282,6 +315,17 @@ elevate_read_upstream(const CycleDataType *, bool, Thread *) {
   return &_typed_data;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::read_stage_unlocked (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::read_stage_unlocked().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *PipelineCycler<CycleDataType>::
+read_stage_unlocked(int) const {
+  return &_typed_data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::read_stage (trivial)
 //       Access: Public

+ 2 - 0
panda/src/pipeline/pipelineCycler.h

@@ -63,12 +63,14 @@ public:
   INLINE PipelineCycler(const PipelineCycler<CycleDataType> &copy);
   INLINE void operator = (const PipelineCycler<CycleDataType> &copy);
 
+  INLINE const CycleDataType *read_unlocked(Thread *current_thread) const;
   INLINE const CycleDataType *read(Thread *current_thread) const;
   INLINE CycleDataType *write(Thread *current_thread);
   INLINE CycleDataType *write_upstream(bool force_to_0, Thread *current_thread);
   INLINE CycleDataType *elevate_read(const CycleDataType *pointer, Thread *current_thread);
   INLINE CycleDataType *elevate_read_upstream(const CycleDataType *pointer, bool force_to_0, Thread *current_thread);
 
+  INLINE const CycleDataType *read_stage_unlocked(int pipeline_stage) const;
   INLINE const CycleDataType *read_stage(int pipeline_stage, Thread *current_thread) const;
   INLINE CycleDataType *elevate_read_stage(int pipeline_stage, const CycleDataType *pointer, Thread *current_thread);
   INLINE CycleDataType *elevate_read_stage_upstream(int pipeline_stage, const CycleDataType *pointer, bool force_to_0, Thread *current_thread);

+ 36 - 1
panda/src/pipeline/pipelineCyclerDummyImpl.I

@@ -99,6 +99,26 @@ release() {
   _locked = false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::read_unlocked
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the current stage of the pipeline as seen by
+//               this thread.  No lock is made on the contents; there
+//               is no guarantee that some other thread won't modify
+//               this object's data while you are working on it.
+//               (However, the data within the returned CycleData
+//               object itself is safe from modification; if another
+//               thread modifies the data, it will perform a
+//               copy-on-write, and thereby change the pointer stored
+//               within the object.)
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerDummyImpl::
+read_unlocked(Thread *current_thread) const {
+  TAU_PROFILE("const CycleData *PipelineCyclerDummyImpl::read_unlocked()", " ", TAU_USER);
+  return _data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerDummyImpl::read
 //       Access: Public
@@ -294,6 +314,21 @@ get_num_stages() {
   return 1;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::read_stage_unlocked
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the indicated stage of the pipeline.  As in
+//               read_unlocked(), no lock is held on the returned
+//               pointer.
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerDummyImpl::
+read_stage_unlocked(int pipeline_stage) const {
+  TAU_PROFILE("const CycleData *PipelineCyclerDummyImpl::read_stage_unlocked(int)", " ", TAU_USER);
+  nassertr(pipeline_stage == 0, NULL);
+  return _data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerDummyImpl::read_stage
 //       Access: Public
@@ -307,7 +342,7 @@ get_num_stages() {
 ////////////////////////////////////////////////////////////////////
 INLINE const CycleData *PipelineCyclerDummyImpl::
 read_stage(int pipeline_stage, Thread *) const {
-  TAU_PROFILE("const CycleData *PipelineCyclerDummyImpl::read_stage(int)", " ", TAU_USER);
+  TAU_PROFILE("const CycleData *PipelineCyclerDummyImpl::read_stage(int, Thread *)", " ", TAU_USER);
   // This function isn't truly const, but it doesn't change the data
   // in any meaningful way, so we pretend it is.
   nassertr(pipeline_stage == 0, NULL);

+ 2 - 0
panda/src/pipeline/pipelineCyclerDummyImpl.h

@@ -55,6 +55,7 @@ public:
   INLINE void lock(Thread *current_thread = NULL);
   INLINE void release();
 
+  INLINE const CycleData *read_unlocked(Thread *current_thread) const;
   INLINE const CycleData *read(Thread *current_thread) const;
   INLINE void increment_read(const CycleData *pointer) const;
   INLINE void release_read(const CycleData *pointer) const;
@@ -67,6 +68,7 @@ public:
   INLINE void release_write(CycleData *pointer);
 
   INLINE int get_num_stages();
+  INLINE const CycleData *read_stage_unlocked(int pipeline_stage) const;
   INLINE const CycleData *read_stage(int pipeline_stage, Thread *current_thread) const;
   INLINE void release_read_stage(int pipeline_stage, const CycleData *pointer) const;
   INLINE CycleData *write_stage(int pipeline_stage, Thread *current_thread);

+ 40 - 0
panda/src/pipeline/pipelineCyclerTrivialImpl.I

@@ -98,6 +98,29 @@ INLINE void PipelineCyclerTrivialImpl::
 release() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::read_unlocked
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the current stage of the pipeline as seen by
+//               this thread.  No lock is made on the contents; there
+//               is no guarantee that some other thread won't modify
+//               this object's data while you are working on it.
+//               (However, the data within the returned CycleData
+//               object itself is safe from modification; if another
+//               thread modifies the data, it will perform a
+//               copy-on-write, and thereby change the pointer stored
+//               within the object.)
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerTrivialImpl::
+read_unlocked(Thread *) const {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (const CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerTrivialImpl::read
 //       Access: Public
@@ -265,6 +288,23 @@ get_num_stages() {
   return 1;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::read_stage_unlocked
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the indicated stage of the pipeline.  As in
+//               read_unlocked(), no lock is held on the returned
+//               pointer.
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerTrivialImpl::
+read_stage_unlocked(int) const {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (const CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerTrivialImpl::read_stage
 //       Access: Public

+ 2 - 0
panda/src/pipeline/pipelineCyclerTrivialImpl.h

@@ -60,6 +60,7 @@ public:
   INLINE void lock(Thread *current_thread = NULL);
   INLINE void release();
 
+  INLINE const CycleData *read_unlocked(Thread *current_thread) const;
   INLINE const CycleData *read(Thread *current_thread) const;
   INLINE void increment_read(const CycleData *pointer) const;
   INLINE void release_read(const CycleData *pointer) const;
@@ -73,6 +74,7 @@ public:
   INLINE void release_write(CycleData *pointer);
 
   INLINE int get_num_stages();
+  INLINE const CycleData *read_stage_unlocked(int pipeline_stage) const;
   INLINE const CycleData *read_stage(int pipeline_stage, Thread *current_thread) const;
   INLINE void release_read_stage(int pipeline_stage, const CycleData *pointer) const;
   INLINE CycleData *write_stage(int pipeline_stage, Thread *current_thread);

+ 44 - 3
panda/src/pipeline/pipelineCyclerTrueImpl.I

@@ -55,6 +55,30 @@ release() {
   _lock.release();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::read_unlocked
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the current stage of the pipeline as seen by
+//               this thread.  No lock is made on the contents; there
+//               is no guarantee that some other thread won't modify
+//               this object's data while you are working on it.
+//               (However, the data within the returned CycleData
+//               object itself is safe from modification; if another
+//               thread modifies the data, it will perform a
+//               copy-on-write, and thereby change the pointer stored
+//               within the object.)
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerTrueImpl::
+read_unlocked(Thread *current_thread) const {
+  TAU_PROFILE("const CycleData *PipelineCyclerTrueImpl::read_unlocked(Thread *)", " ", TAU_USER);
+  int pipeline_stage = current_thread->get_pipeline_stage();
+#ifdef _DEBUG
+  nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+#endif
+  return _data[pipeline_stage];
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerTrueImpl::read
 //       Access: Public
@@ -258,6 +282,23 @@ get_num_stages() {
   return _num_stages;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::read_stage_unlocked
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the indicated stage of the pipeline.  As in
+//               read_unlocked(), no lock is held on the returned
+//               pointer.
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerTrueImpl::
+read_stage_unlocked(int pipeline_stage) const {
+  TAU_PROFILE("const CycleData *PipelineCyclerTrueImpl::read_stage_unlocked(int)", " ", TAU_USER);
+#ifdef _DEBUG
+  nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+#endif
+  return _data[pipeline_stage];
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerTrueImpl::read_stage
 //       Access: Public
@@ -271,7 +312,7 @@ get_num_stages() {
 ////////////////////////////////////////////////////////////////////
 INLINE const CycleData *PipelineCyclerTrueImpl::
 read_stage(int pipeline_stage, Thread *current_thread) const {
-  TAU_PROFILE("const CycleData *PipelineCyclerTrueImpl::read_stage(int)", " ", TAU_USER);
+  TAU_PROFILE("const CycleData *PipelineCyclerTrueImpl::read_stage(int, Thread *)", " ", TAU_USER);
 #ifdef _DEBUG
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
 #endif
@@ -421,7 +462,7 @@ get_write_count() const {
 INLINE PT(CycleData) PipelineCyclerTrueImpl::
 cycle_2() {
   TAU_PROFILE("PT(CycleData) PipelineCyclerTrueImpl::cycle_2()", " ", TAU_USER);
-  PT(CycleData) last_val = _data[1];
+  PT(CycleData) last_val = _data[1].p();
   nassertr(_lock.debug_is_locked(), last_val);
   nassertr(_dirty, last_val);
   nassertr(_num_stages == 2, last_val);
@@ -445,7 +486,7 @@ cycle_2() {
 INLINE PT(CycleData) PipelineCyclerTrueImpl::
 cycle_3() {
   TAU_PROFILE("PT(CycleData) PipelineCyclerTrueImpl::cycle_3()", " ", TAU_USER);
-  PT(CycleData) last_val = _data[2];
+  PT(CycleData) last_val = _data[2].p();
   nassertr(_lock.debug_is_locked(), last_val);
   nassertr(_dirty, last_val);
   nassertr(_num_stages == 3, last_val);

+ 12 - 8
panda/src/pipeline/pipelineCyclerTrueImpl.cxx

@@ -39,7 +39,7 @@ PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) :
   }
 
   _num_stages = _pipeline->get_num_stages();
-  _data = new PT(CycleData)[_num_stages];
+  _data = new NPT(CycleData)[_num_stages];
   for (int i = 0; i < _num_stages; ++i) {
     _data[i] = initial_data;
   }
@@ -63,7 +63,7 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy) :
   
   _num_stages = _pipeline->get_num_stages();
   nassertv(_num_stages == copy._num_stages);
-  _data = new PT(CycleData)[_num_stages];
+  _data = new NPT(CycleData)[_num_stages];
   
   // It's no longer critically important that we preserve pointerwise
   // equivalence between different stages in the copy, but it doesn't
@@ -77,7 +77,7 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy) :
     if (new_pt == NULL) {
       new_pt = copy._data[i]->make_copy();
     }
-    _data[i] = new_pt;
+    _data[i] = new_pt.p();
   }
 
   _pipeline->add_cycler(this);
@@ -105,7 +105,7 @@ operator = (const PipelineCyclerTrueImpl &copy) {
     if (new_pt == NULL) {
       new_pt = copy._data[i]->make_copy();
     }
-    _data[i] = new_pt;
+    _data[i] = new_pt.p();
   }
 
   if (copy._dirty && !_dirty) {
@@ -152,7 +152,11 @@ write_stage(int pipeline_stage, Thread *current_thread) {
 
   CycleData *old_data = _data[pipeline_stage];
 
-  if (old_data->get_ref_count() != 1) {
+  // Only the node reference count is considered an important count
+  // for copy-on-write purposes.  A standard reference of other than 1
+  // just means that some code (other that the PipelineCycler) has a
+  // pointer, which is safe to modify.
+  if (old_data->get_node_ref_count() != 1) {
     // Copy-on-write.
     _data[pipeline_stage] = old_data->make_copy();
 
@@ -203,7 +207,7 @@ write_stage_upstream(int pipeline_stage, bool force_to_0, Thread *current_thread
       
       k = pipeline_stage - 1;
       while (k >= 0 && (_data[k] == old_data || force_to_0)) {
-        _data[k] = new_data;
+        _data[k] = new_data.p();
         --k;
       }
       
@@ -251,7 +255,7 @@ write_stage_upstream(int pipeline_stage, bool force_to_0, Thread *current_thread
 ////////////////////////////////////////////////////////////////////
 PT(CycleData) PipelineCyclerTrueImpl::
 cycle() {
-  PT(CycleData) last_val = _data[_num_stages - 1];
+  PT(CycleData) last_val = _data[_num_stages - 1].p();
   nassertr(_lock.debug_is_locked(), last_val);
   nassertr(_dirty, last_val);
 
@@ -294,7 +298,7 @@ set_num_stages(int num_stages) {
 
   } else {
     // To increase the array, we must reallocate it larger.
-    PT(CycleData) *new_data = new PT(CycleData)[num_stages];
+    NPT(CycleData) *new_data = new NPT(CycleData)[num_stages];
     int i;
     for (i = 0; i < _num_stages; ++i) {
       new_data[i] = _data[i];

+ 6 - 2
panda/src/pipeline/pipelineCyclerTrueImpl.h

@@ -27,6 +27,7 @@
 #include "pipelineCyclerLinks.h"
 #include "cycleData.h"
 #include "pointerTo.h"
+#include "nodePointerTo.h"
 #include "thread.h"
 #include "reMutex.h"
 #include "reMutexHolder.h"
@@ -61,6 +62,8 @@ public:
   INLINE void lock(Thread *current_thread);
   INLINE void release();
 
+  INLINE const CycleData *read_unlocked(Thread *current_thread) const;
+
   INLINE const CycleData *read(Thread *current_thread) const;
   INLINE void increment_read(const CycleData *pointer) const;
   INLINE void release_read(const CycleData *pointer) const;
@@ -73,6 +76,7 @@ public:
   INLINE void release_write(CycleData *pointer);
 
   INLINE int get_num_stages();
+  INLINE const CycleData *read_stage_unlocked(int pipeline_stage) const;
   INLINE const CycleData *read_stage(int pipeline_stage, Thread *current_thread) const;
   INLINE void release_read_stage(int pipeline_stage, const CycleData *pointer) const;
   CycleData *write_stage(int pipeline_stage, Thread *current_thread);
@@ -113,8 +117,8 @@ private:
 private:
   Pipeline *_pipeline;
 
-  // An array of PT(CycleData) objects.
-  PT(CycleData) *_data;
+  // An array of NPT(CycleData) objects.
+  NPT(CycleData) *_data;
   int _num_stages;
   bool _dirty;
 

+ 2 - 0
panda/src/pipeline/pipeline_composite1.cxx

@@ -9,6 +9,8 @@
 #include "conditionVarSpinlockImpl.cxx"
 #include "config_pipeline.cxx"
 #include "cycleData.cxx"
+#include "cycleDataLockedReader.cxx"
+#include "cycleDataLockedStageReader.cxx"
 #include "cycleDataReader.cxx"
 #include "cycleDataStageReader.cxx"
 #include "cycleDataStageWriter.cxx"

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

@@ -46,8 +46,6 @@
     modifierButtons.I modifierButtons.h mouseButton.h \
     mouseData.I mouseData.h nameUniquifier.I nameUniquifier.h \
     nodeCachedReferenceCount.h nodeCachedReferenceCount.I \
-    nodePointerToBase.h nodePointerToBase.I \
-    nodePointerTo.h nodePointerTo.I \
     nonDeletor.h \
     pta_double.h \
     pta_float.h pta_int.h \
@@ -86,8 +84,6 @@
     modifierButtons.cxx mouseButton.cxx mouseData.cxx \
     nameUniquifier.cxx \
     nodeCachedReferenceCount.cxx \
-    nodePointerToBase.cxx \
-    nodePointerTo.cxx \
     nonDeletor.cxx \
     pta_double.cxx pta_float.cxx \
     pta_int.cxx pta_ushort.cxx \
@@ -135,8 +131,6 @@
     modifierButtons.h mouseButton.h mouseData.I mouseData.h \
     nameUniquifier.I nameUniquifier.h \
     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 \

+ 0 - 27
panda/src/putil/nodeCachedReferenceCount.I

@@ -242,30 +242,3 @@ get_referenced_bits() const {
 
   return result;
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: node_unref_delete
-//  Description: This global helper function will unref the given
-//               ReferenceCount object, and if the reference count
-//               reaches zero, automatically delete it.  It can't be a
-//               member function because it's usually a bad idea to
-//               delete an object from within its own member function.
-//               It's a template function so the destructor doesn't
-//               have to be virtual.
-////////////////////////////////////////////////////////////////////
-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;
-    }
-
-    delete ptr;
-  }
-}
-

+ 14 - 3
panda/src/putil/nodeCachedReferenceCount.h

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 
 #include "cachedTypedWritableReferenceCount.h"
+#include "nodeReferenceCount.h" // for node_unref_delete()
 #include "objectDeletor.h"
 
 ////////////////////////////////////////////////////////////////////
@@ -32,6 +33,19 @@
 //               number of times the object is referenced by a "node",
 //               presumably a PandaNode.
 //
+//               This essentially combines the functionality of
+//               NodeReferenceCount and
+//               CachedTypedWritableReferenceCount, so that a
+//               derivative of this object actually has three
+//               counters: the standard reference count, the "cache"
+//               reference count, and the "node" reference count.
+//               Rather than multiply inheriting from the two
+//               reference count classes, we inherit only from
+//               CachedTypedWritableReferenceCount and simply
+//               duplicate the functionality of NodeReferenceCount, to
+//               avoid all of the problems associated with multiple
+//               inheritance.
+//
 //               The intended design is to use this as a base class
 //               for RenderState and TransformState, both of which are
 //               held by PandaNodes, and also have caches which are
@@ -90,9 +104,6 @@ private:
   static TypeHandle _type_handle;
 };
 
-template<class RefCountType>
-INLINE void node_unref_delete(RefCountType *ptr);
-
 #include "nodeCachedReferenceCount.I"
 
 #endif  

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

@@ -3,8 +3,6 @@
 #include "mouseData.cxx"
 #include "nameUniquifier.cxx"
 #include "nodeCachedReferenceCount.cxx"
-#include "nodePointerToBase.cxx"
-#include "nodePointerTo.cxx"
 #include "nonDeletor.cxx"
 #include "pta_double.cxx"
 #include "pta_float.cxx"