Browse Source

merge cow_thread_2007_04_12: make copy-on-write operations thread-safe

David Rose 18 years ago
parent
commit
936412fae2
100 changed files with 1120 additions and 738 deletions
  1. 25 1
      panda/src/collide/collisionLevelState.I
  2. 4 0
      panda/src/collide/collisionLevelState.h
  3. 1 1
      panda/src/collide/collisionLevelStateBase.I
  4. 1 1
      panda/src/collide/collisionLevelStateBase.cxx
  5. 2 2
      panda/src/collide/collisionLevelStateBase.h
  6. 13 2
      panda/src/collide/collisionNode.I
  7. 9 16
      panda/src/collide/collisionNode.cxx
  8. 3 2
      panda/src/collide/collisionNode.h
  9. 11 0
      panda/src/collide/collisionSolid.cxx
  10. 6 4
      panda/src/collide/collisionSolid.h
  11. 1 1
      panda/src/collide/collisionSphere.cxx
  12. 5 5
      panda/src/collide/collisionTraverser.cxx
  13. 1 1
      panda/src/display/graphicsEngine.cxx
  14. 4 4
      panda/src/display/graphicsEngine.h
  15. 12 12
      panda/src/downloader/httpChannel.h
  16. 2 2
      panda/src/event/asyncTaskManager.cxx
  17. 10 10
      panda/src/express/multifile.h
  18. 40 0
      panda/src/express/nodePointerTo.I
  19. 8 0
      panda/src/express/nodePointerTo.h
  20. 3 0
      panda/src/express/pointerToArray.I
  21. 9 15
      panda/src/express/pointerToArray.h
  22. 10 10
      panda/src/express/virtualFile.h
  23. 25 25
      panda/src/express/virtualFileSystem.h
  24. 8 11
      panda/src/gobj/geom.I
  25. 57 50
      panda/src/gobj/geom.cxx
  26. 16 10
      panda/src/gobj/geom.h
  27. 14 16
      panda/src/gobj/geomPrimitive.I
  28. 56 50
      panda/src/gobj/geomPrimitive.cxx
  29. 16 15
      panda/src/gobj/geomPrimitive.h
  30. 15 5
      panda/src/gobj/geomVertexArrayData.cxx
  31. 8 5
      panda/src/gobj/geomVertexArrayData.h
  32. 9 9
      panda/src/gobj/geomVertexData.I
  33. 69 78
      panda/src/gobj/geomVertexData.cxx
  34. 18 14
      panda/src/gobj/geomVertexData.h
  35. 10 0
      panda/src/gobj/transformBlendTable.cxx
  36. 7 4
      panda/src/gobj/transformBlendTable.h
  37. 1 1
      panda/src/net/connectionReader.cxx
  38. 1 1
      panda/src/net/connectionWriter.cxx
  39. 26 47
      panda/src/pgraph/geomNode.I
  40. 52 42
      panda/src/pgraph/geomNode.cxx
  41. 10 9
      panda/src/pgraph/geomNode.h
  42. 5 5
      panda/src/pgraph/geomTransformer.cxx
  43. 2 2
      panda/src/pgraph/loader.h
  44. 11 1
      panda/src/pgraph/nodePath.cxx
  45. 1 1
      panda/src/pgraph/nodePath.h
  46. 50 59
      panda/src/pgraph/pandaNode.I
  47. 56 78
      panda/src/pgraph/pandaNode.cxx
  48. 15 15
      panda/src/pgraph/pandaNode.h
  49. 3 0
      panda/src/pipeline/Sources.pp
  50. 3 3
      panda/src/pipeline/conditionVar.I
  51. 2 2
      panda/src/pipeline/conditionVar.h
  52. 1 1
      panda/src/pipeline/conditionVarDebug.I
  53. 3 3
      panda/src/pipeline/conditionVarDebug.cxx
  54. 1 1
      panda/src/pipeline/conditionVarDebug.h
  55. 2 2
      panda/src/pipeline/conditionVarDirect.h
  56. 3 3
      panda/src/pipeline/conditionVarFull.I
  57. 2 2
      panda/src/pipeline/conditionVarFull.h
  58. 1 1
      panda/src/pipeline/conditionVarFullDebug.I
  59. 4 4
      panda/src/pipeline/conditionVarFullDebug.cxx
  60. 1 1
      panda/src/pipeline/conditionVarFullDebug.h
  61. 4 4
      panda/src/pipeline/conditionVarFullDirect.I
  62. 1 1
      panda/src/pipeline/conditionVarFullDirect.h
  63. 5 13
      panda/src/pipeline/config_pipeline.cxx
  64. 0 2
      panda/src/pipeline/config_pipeline.h
  65. 3 0
      panda/src/pipeline/cycleDataLockedReader.I
  66. 4 0
      panda/src/pipeline/cycleDataLockedReader.h
  67. 2 0
      panda/src/pipeline/cycleDataLockedStageReader.I
  68. 4 0
      panda/src/pipeline/cycleDataLockedStageReader.h
  69. 3 0
      panda/src/pipeline/cycleDataReader.I
  70. 4 0
      panda/src/pipeline/cycleDataReader.h
  71. 2 0
      panda/src/pipeline/cycleDataStageReader.I
  72. 4 0
      panda/src/pipeline/cycleDataStageReader.h
  73. 3 0
      panda/src/pipeline/cycleDataStageWriter.I
  74. 4 0
      panda/src/pipeline/cycleDataStageWriter.h
  75. 3 0
      panda/src/pipeline/cycleDataWriter.I
  76. 4 0
      panda/src/pipeline/cycleDataWriter.h
  77. 5 5
      panda/src/pipeline/mutexDebug.I
  78. 3 3
      panda/src/pipeline/mutexDebug.h
  79. 3 3
      panda/src/pipeline/mutexDirect.I
  80. 2 2
      panda/src/pipeline/mutexDirect.h
  81. 1 0
      panda/src/pipeline/pipeline_composite2.cxx
  82. 3 3
      panda/src/pipeline/pmutex.I
  83. 3 1
      panda/src/pipeline/pmutex.h
  84. 165 0
      panda/src/pipeline/pythonThread.cxx
  85. 80 0
      panda/src/pipeline/pythonThread.h
  86. 3 1
      panda/src/pipeline/reMutex.h
  87. 5 5
      panda/src/pipeline/reMutexDirect.I
  88. 1 1
      panda/src/pipeline/reMutexDirect.cxx
  89. 3 3
      panda/src/pipeline/reMutexDirect.h
  90. 4 14
      panda/src/pipeline/thread.cxx
  91. 4 4
      panda/src/pipeline/thread.h
  92. 1 1
      panda/src/pipeline/threadDummyImpl.I
  93. 1 1
      panda/src/pipeline/threadDummyImpl.h
  94. 1 1
      panda/src/pipeline/threadPosixImpl.cxx
  95. 1 1
      panda/src/pipeline/threadPosixImpl.h
  96. 2 0
      panda/src/pipeline/threadPriority.h
  97. 1 1
      panda/src/pipeline/threadWin32Impl.cxx
  98. 1 1
      panda/src/pipeline/threadWin32Impl.h
  99. 9 2
      panda/src/putil/Sources.pp
  100. 4 0
      panda/src/putil/config_util.cxx

+ 25 - 1
panda/src/collide/collisionLevelState.I

@@ -16,7 +16,7 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
-#ifndef CPPPARSER  // interrogate has trouble with these initializers.
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::Constructor
 //     Function: CollisionLevelState::Constructor
 //       Access: Public
 //       Access: Public
@@ -31,6 +31,7 @@ CollisionLevelState(const NodePath &node_path) :
 }
 }
 #endif  // CPPPARSER
 #endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::Constructor
 //     Function: CollisionLevelState::Constructor
 //       Access: Public
 //       Access: Public
@@ -44,7 +45,9 @@ CollisionLevelState(const CollisionLevelState<MaskType> &parent, PandaNode *chil
   _current(parent._current)
   _current(parent._current)
 {
 {
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::Copy Constructor
 //     Function: CollisionLevelState::Copy Constructor
 //       Access: Public
 //       Access: Public
@@ -57,7 +60,9 @@ CollisionLevelState(const CollisionLevelState<MaskType> &copy) :
   _current(copy._current)
   _current(copy._current)
 {
 {
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::Copy Assignment Operator
 //     Function: CollisionLevelState::Copy Assignment Operator
 //       Access: Public
 //       Access: Public
@@ -69,7 +74,9 @@ operator = (const CollisionLevelState<MaskType> &copy) {
   CollisionLevelStateBase::operator = (copy);
   CollisionLevelStateBase::operator = (copy);
   _current = copy._current;
   _current = copy._current;
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::clear
 //     Function: CollisionLevelState::clear
 //       Access: Public
 //       Access: Public
@@ -81,7 +88,9 @@ clear() {
   CollisionLevelStateBase::clear();
   CollisionLevelStateBase::clear();
   _current.clear();
   _current.clear();
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::prepare_collider
 //     Function: CollisionLevelState::prepare_collider
 //       Access: Public
 //       Access: Public
@@ -98,7 +107,9 @@ prepare_collider(const ColliderDef &def, const NodePath &root) {
   CollisionLevelStateBase::prepare_collider(def, root);
   CollisionLevelStateBase::prepare_collider(def, root);
   _current.set_bit(index);
   _current.set_bit(index);
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::any_in_bounds
 //     Function: CollisionLevelState::any_in_bounds
 //       Access: Public
 //       Access: Public
@@ -184,7 +195,9 @@ any_in_bounds() {
 #endif  // NDEBUG
 #endif  // NDEBUG
   return has_any_collider();
   return has_any_collider();
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelStateBase::apply_transform
 //     Function: CollisionLevelStateBase::apply_transform
 //       Access: Public
 //       Access: Public
@@ -226,7 +239,9 @@ apply_transform() {
     _local_bounds = new_bounds;
     _local_bounds = new_bounds;
   }    
   }    
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::has_max_colliders
 //     Function: CollisionLevelState::has_max_colliders
 //       Access: Public, Static
 //       Access: Public, Static
@@ -239,7 +254,9 @@ INLINE bool CollisionLevelState<MaskType>::
 has_max_colliders() {
 has_max_colliders() {
   return CurrentMask::has_max_num_bits();
   return CurrentMask::has_max_num_bits();
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::get_max_colliders
 //     Function: CollisionLevelState::get_max_colliders
 //       Access: Public, Static
 //       Access: Public, Static
@@ -251,7 +268,9 @@ INLINE int CollisionLevelState<MaskType>::
 get_max_colliders() {
 get_max_colliders() {
   return CurrentMask::get_max_num_bits();
   return CurrentMask::get_max_num_bits();
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::has_collider
 //     Function: CollisionLevelState::has_collider
 //       Access: Public
 //       Access: Public
@@ -264,7 +283,9 @@ has_collider(int n) const {
   nassertr(n >= 0 && n < (int)_colliders.size(), false);
   nassertr(n >= 0 && n < (int)_colliders.size(), false);
   return (_current.get_bit(n));
   return (_current.get_bit(n));
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::has_any_collider
 //     Function: CollisionLevelState::has_any_collider
 //       Access: Public
 //       Access: Public
@@ -275,7 +296,9 @@ INLINE bool CollisionLevelState<MaskType>::
 has_any_collider() const {
 has_any_collider() const {
   return !_current.is_zero();
   return !_current.is_zero();
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionLevelState::omit_collider
 //     Function: CollisionLevelState::omit_collider
 //       Access: Public
 //       Access: Public
@@ -289,3 +312,4 @@ omit_collider(int n) {
 
 
   _current.clear_bit(n);
   _current.clear_bit(n);
 }
 }
+#endif  // CPPPARSER

+ 4 - 0
panda/src/collide/collisionLevelState.h

@@ -40,6 +40,9 @@
 template<class MaskType>
 template<class MaskType>
 class CollisionLevelState : public CollisionLevelStateBase {
 class CollisionLevelState : public CollisionLevelStateBase {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   INLINE CollisionLevelState(const NodePath &node_path);
   INLINE CollisionLevelState(const NodePath &node_path);
   INLINE CollisionLevelState(const CollisionLevelState<MaskType> &parent, 
   INLINE CollisionLevelState(const CollisionLevelState<MaskType> &parent, 
                              PandaNode *child);
                              PandaNode *child);
@@ -70,6 +73,7 @@ private:
   CurrentMask _current;
   CurrentMask _current;
 
 
   friend class CollisionTraverser;
   friend class CollisionTraverser;
+#endif  // CPPPARSER
 };
 };
 
 
 #include "collisionLevelState.I"
 #include "collisionLevelState.I"

+ 1 - 1
panda/src/collide/collisionLevelStateBase.I

@@ -110,7 +110,7 @@ get_num_colliders() const {
 //       Access: Public
 //       Access: Public
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE CollisionSolid *CollisionLevelStateBase::
+INLINE const CollisionSolid *CollisionLevelStateBase::
 get_collider(int n) const {
 get_collider(int n) const {
   nassertr(n >= 0 && n < (int)_colliders.size(), NULL);
   nassertr(n >= 0 && n < (int)_colliders.size(), NULL);
 
 

+ 1 - 1
panda/src/collide/collisionLevelStateBase.cxx

@@ -57,7 +57,7 @@ void CollisionLevelStateBase::
 prepare_collider(const ColliderDef &def, const NodePath &root) {
 prepare_collider(const ColliderDef &def, const NodePath &root) {
   _colliders.push_back(def);
   _colliders.push_back(def);
 
 
-  CollisionSolid *collider = def._collider;
+  const CollisionSolid *collider = def._collider;
   CPT(BoundingVolume) bv = collider->get_bounds();
   CPT(BoundingVolume) bv = collider->get_bounds();
   if (!bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
   if (!bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
     _local_bounds.push_back((GeometricBoundingVolume *)NULL);
     _local_bounds.push_back((GeometricBoundingVolume *)NULL);

+ 2 - 2
panda/src/collide/collisionLevelStateBase.h

@@ -48,7 +48,7 @@ class CollisionLevelStateBase {
 public:
 public:
   class ColliderDef {
   class ColliderDef {
   public:
   public:
-    CollisionSolid *_collider;
+    CPT(CollisionSolid) _collider;
     CollisionNode *_node;
     CollisionNode *_node;
     NodePath _node_path;
     NodePath _node_path;
   };
   };
@@ -68,7 +68,7 @@ public:
 
 
   INLINE int get_num_colliders() const;
   INLINE int get_num_colliders() const;
 
 
-  INLINE CollisionSolid *get_collider(int n) const;
+  INLINE const CollisionSolid *get_collider(int n) const;
   INLINE CollisionNode *get_collider_node(int n) const;
   INLINE CollisionNode *get_collider_node(int n) const;
   INLINE NodePath get_collider_node_path(int n) const;
   INLINE NodePath get_collider_node_path(int n) const;
   INLINE const GeometricBoundingVolume *get_local_bound(int n) const;
   INLINE const GeometricBoundingVolume *get_local_bound(int n) const;

+ 13 - 2
panda/src/collide/collisionNode.I

@@ -99,10 +99,21 @@ get_num_solids() const {
 //       Access: Published
 //       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE CollisionSolid *CollisionNode::
+INLINE CPT(CollisionSolid) CollisionNode::
 get_solid(int n) const {
 get_solid(int n) const {
   nassertr(n >= 0 && n < get_num_solids(), NULL);
   nassertr(n >= 0 && n < get_num_solids(), NULL);
-  return _solids[n];
+  return _solids[n].get_read_pointer();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionNode::modify_solid
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PT(CollisionSolid) CollisionNode::
+modify_solid(int n) {
+  nassertr(n >= 0 && n < get_num_solids(), NULL);
+  return _solids[n].get_write_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 9 - 16
panda/src/collide/collisionNode.cxx

@@ -119,15 +119,7 @@ xform(const LMatrix4f &mat) {
 
 
   Solids::iterator si;
   Solids::iterator si;
   for (si = _solids.begin(); si != _solids.end(); ++si) {
   for (si = _solids.begin(); si != _solids.end(); ++si) {
-    CollisionSolid *solid = (*si);
-
-    // We may have to copy each of our solids as we transform them if
-    // someone else is sharing their pointers.
-    if (solid->get_ref_count() > 1) {
-      solid = solid->make_copy();
-      (*si) = solid;
-    }
-
+    PT(CollisionSolid) solid = (*si).get_write_pointer();
     solid->xform(mat);
     solid->xform(mat);
   }
   }
   mark_internal_bounds_stale();
   mark_internal_bounds_stale();
@@ -156,8 +148,8 @@ combine_with(PandaNode *other) {
     // name, because the name is often meaningful.
     // name, because the name is often meaningful.
     CollisionNode *cother = DCAST(CollisionNode, other);
     CollisionNode *cother = DCAST(CollisionNode, other);
     if (get_name() == cother->get_name()) {
     if (get_name() == cother->get_name()) {
-      const PT(CollisionSolid) *solids_begin = &cother->_solids[0];
-      const PT(CollisionSolid) *solids_end = solids_begin + cother->_solids.size();
+      const COWPT(CollisionSolid) *solids_begin = &cother->_solids[0];
+      const COWPT(CollisionSolid) *solids_end = solids_begin + cother->_solids.size();
       _solids.insert(_solids.end(), solids_begin, solids_end);
       _solids.insert(_solids.end(), solids_begin, solids_end);
       mark_internal_bounds_stale();
       mark_internal_bounds_stale();
       return this;
       return this;
@@ -215,9 +207,9 @@ bool CollisionNode::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
   // Append our collision vizzes to the drawing, even though they're
   // Append our collision vizzes to the drawing, even though they're
   // not actually part of the scene graph.
   // not actually part of the scene graph.
-  Solids::iterator si;
+  Solids::const_iterator si;
   for (si = _solids.begin(); si != _solids.end(); ++si) {
   for (si = _solids.begin(); si != _solids.end(); ++si) {
-    CollisionSolid *solid = (*si);
+    CPT(CollisionSolid) solid = (*si).get_read_pointer();
     PT(PandaNode) node = solid->get_viz(trav, data, false);
     PT(PandaNode) node = solid->get_viz(trav, data, false);
     if (node != (PandaNode *)NULL) {
     if (node != (PandaNode *)NULL) {
       CullTraverserData next_data(data, node);
       CullTraverserData next_data(data, node);
@@ -240,7 +232,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
       // ghosted.
       // ghosted.
       
       
       for (si = _solids.begin(); si != _solids.end(); ++si) {
       for (si = _solids.begin(); si != _solids.end(); ++si) {
-        CollisionSolid *solid = (*si);
+        CPT(CollisionSolid) solid = (*si).get_read_pointer();
         PT(PandaNode) node = solid->get_viz(trav, data, false);
         PT(PandaNode) node = solid->get_viz(trav, data, false);
         if (node != (PandaNode *)NULL) {
         if (node != (PandaNode *)NULL) {
           CullTraverserData next_data(data, node);
           CullTraverserData next_data(data, node);
@@ -323,7 +315,8 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
 
 
   Solids::const_iterator gi;
   Solids::const_iterator gi;
   for (gi = _solids.begin(); gi != _solids.end(); ++gi) {
   for (gi = _solids.begin(); gi != _solids.end(); ++gi) {
-    CPT(BoundingVolume) volume = (*gi)->get_bounds();
+    CPT(CollisionSolid) solid = (*gi).get_read_pointer();
+    CPT(BoundingVolume) volume = solid->get_bounds();
     cpt_volumes.push_back(volume);
     cpt_volumes.push_back(volume);
     child_volumes.push_back(volume);
     child_volumes.push_back(volume);
   }
   }
@@ -396,7 +389,7 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   int num_solids = _solids.size();
   int num_solids = _solids.size();
   dg.add_uint16(num_solids);
   dg.add_uint16(num_solids);
   for(int i = 0; i < num_solids; i++) {
   for(int i = 0; i < num_solids; i++) {
-    manager->write_pointer(dg, _solids[i]);
+    manager->write_pointer(dg, _solids[i].get_read_pointer());
   }
   }
 
 
   dg.add_uint32(_from_collide_mask.get_word());
   dg.add_uint32(_from_collide_mask.get_word());

+ 3 - 2
panda/src/collide/collisionNode.h

@@ -63,7 +63,8 @@ PUBLISHED:
 
 
   INLINE void clear_solids();
   INLINE void clear_solids();
   INLINE int get_num_solids() const;
   INLINE int get_num_solids() const;
-  INLINE CollisionSolid *get_solid(int n) const;
+  INLINE CPT(CollisionSolid) get_solid(int n) const;
+  INLINE PT(CollisionSolid) modify_solid(int n);
   INLINE void set_solid(int n, CollisionSolid *solid);
   INLINE void set_solid(int n, CollisionSolid *solid);
   INLINE void remove_solid(int n);
   INLINE void remove_solid(int n);
   INLINE int add_solid(CollisionSolid *solid);
   INLINE int add_solid(CollisionSolid *solid);
@@ -85,7 +86,7 @@ private:
   CollideMask _from_collide_mask;
   CollideMask _from_collide_mask;
   int _collider_sort;
   int _collider_sort;
 
 
-  typedef pvector< PT(CollisionSolid) > Solids;
+  typedef pvector< COWPT(CollisionSolid) > Solids;
   Solids _solids;
   Solids _solids;
   
   
 public:
 public:

+ 11 - 0
panda/src/collide/collisionSolid.cxx

@@ -58,6 +58,7 @@ CollisionSolid() : _lock("CollisionSolid") {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CollisionSolid::
 CollisionSolid::
 CollisionSolid(const CollisionSolid &copy) :
 CollisionSolid(const CollisionSolid &copy) :
+  CopyOnWriteObject(copy),
   _effective_normal(copy._effective_normal),
   _effective_normal(copy._effective_normal),
   _internal_bounds(copy._internal_bounds),
   _internal_bounds(copy._internal_bounds),
   _flags(copy._flags),
   _flags(copy._flags),
@@ -75,6 +76,16 @@ CollisionSolid::
 ~CollisionSolid() {
 ~CollisionSolid() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::make_cow_copy
+//       Access: Protected, Virtual
+//  Description: Required to implement CopyOnWriteObject.
+////////////////////////////////////////////////////////////////////
+PT(CopyOnWriteObject) CollisionSolid::
+make_cow_copy() {
+  return make_copy();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::get_bounds
 //     Function: CollisionSolid::get_bounds
 //       Access: Protected
 //       Access: Protected

+ 6 - 4
panda/src/collide/collisionSolid.h

@@ -22,7 +22,7 @@
 #include "pandabase.h"
 #include "pandabase.h"
 
 
 #include "config_collide.h"
 #include "config_collide.h"
-#include "typedWritableReferenceCount.h"
+#include "copyOnWriteObject.h"
 #include "luse.h"
 #include "luse.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "renderState.h"
 #include "renderState.h"
@@ -51,13 +51,15 @@ class CullTraverserData;
 //               function calls handle the subset of the N*N
 //               function calls handle the subset of the N*N
 //               intersection tests that we care about.
 //               intersection tests that we care about.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA CollisionSolid : public TypedWritableReferenceCount {
+class EXPCL_PANDA CollisionSolid : public CopyOnWriteObject {
 public:
 public:
   CollisionSolid();
   CollisionSolid();
   CollisionSolid(const CollisionSolid &copy);
   CollisionSolid(const CollisionSolid &copy);
   virtual ~CollisionSolid();
   virtual ~CollisionSolid();
 
 
   virtual CollisionSolid *make_copy()=0;
   virtual CollisionSolid *make_copy()=0;
+protected:
+  virtual PT(CopyOnWriteObject) make_cow_copy();
 
 
 PUBLISHED:
 PUBLISHED:
   virtual LPoint3f get_collision_origin() const=0;
   virtual LPoint3f get_collision_origin() const=0;
@@ -155,9 +157,9 @@ public:
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
-    TypedWritableReferenceCount::init_type();
+    CopyOnWriteObject::init_type();
     register_type(_type_handle, "CollisionSolid",
     register_type(_type_handle, "CollisionSolid",
-                  TypedWritableReferenceCount::get_class_type());
+                  CopyOnWriteObject::get_class_type());
   }
   }
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
     return get_class_type();
     return get_class_type();

+ 1 - 1
panda/src/collide/collisionSphere.cxx

@@ -205,7 +205,7 @@ test_intersection_from_ds_solid(const CollisionEntry &entry) const {
   float lens_radius = length(
   float lens_radius = length(
     LVector3f(ds_solid->get_lens_radius(), 0.0f, 0.0f) * wrt_mat);
     LVector3f(ds_solid->get_lens_radius(), 0.0f, 0.0f) * wrt_mat);
   LVector3f lens_vec = lens_center - into_center;
   LVector3f lens_vec = lens_center - into_center;
-  float lens_distance_squared = dot(lens_vec, lens_vec);
+  //  float lens_distance_squared = dot(lens_vec, lens_vec);
 
 
   LVector3f surface_normal; // into
   LVector3f surface_normal; // into
   //LPoint3f surface_point; // into
   //LPoint3f surface_point; // into

+ 5 - 5
panda/src/collide/collisionTraverser.cxx

@@ -290,7 +290,7 @@ traverse(const NodePath &root) {
   }
   }
 
 
   bool traversal_done = false;
   bool traversal_done = false;
-  if (_colliders.size() <= CollisionLevelStateSingle::get_max_colliders() ||
+  if ((int)_colliders.size() <= CollisionLevelStateSingle::get_max_colliders() ||
       !allow_collider_multiple) {
       !allow_collider_multiple) {
     // Use the single-word-at-a-time traverser, which might need to make
     // Use the single-word-at-a-time traverser, which might need to make
     // lots of passes.
     // lots of passes.
@@ -312,7 +312,7 @@ traverse(const NodePath &root) {
   }
   }
 
 
   if (!traversal_done &&
   if (!traversal_done &&
-      _colliders.size() <= CollisionLevelStateDouble::get_max_colliders()) {
+      (int)_colliders.size() <= CollisionLevelStateDouble::get_max_colliders()) {
     // Try the double-word-at-a-time traverser.
     // Try the double-word-at-a-time traverser.
     LevelStatesDouble level_states;
     LevelStatesDouble level_states;
     prepare_colliders_double(level_states, root);
     prepare_colliders_double(level_states, root);
@@ -565,7 +565,7 @@ prepare_colliders_single(CollisionTraverser::LevelStatesSingle &level_states,
       
       
       int num_solids = cnode->get_num_solids();
       int num_solids = cnode->get_num_solids();
       for (int s = 0; s < num_solids; ++s) {
       for (int s = 0; s < num_solids; ++s) {
-        CollisionSolid *collider = cnode->get_solid(s);
+        CPT(CollisionSolid) collider = cnode->get_solid(s);
         def._collider = collider;
         def._collider = collider;
         level_state.prepare_collider(def, root);
         level_state.prepare_collider(def, root);
 
 
@@ -780,7 +780,7 @@ prepare_colliders_double(CollisionTraverser::LevelStatesDouble &level_states,
       
       
       int num_solids = cnode->get_num_solids();
       int num_solids = cnode->get_num_solids();
       for (int s = 0; s < num_solids; ++s) {
       for (int s = 0; s < num_solids; ++s) {
-        CollisionSolid *collider = cnode->get_solid(s);
+        CPT(CollisionSolid) collider = cnode->get_solid(s);
         def._collider = collider;
         def._collider = collider;
         level_state.prepare_collider(def, root);
         level_state.prepare_collider(def, root);
 
 
@@ -995,7 +995,7 @@ prepare_colliders_quad(CollisionTraverser::LevelStatesQuad &level_states,
       
       
       int num_solids = cnode->get_num_solids();
       int num_solids = cnode->get_num_solids();
       for (int s = 0; s < num_solids; ++s) {
       for (int s = 0; s < num_solids; ++s) {
-        CollisionSolid *collider = cnode->get_solid(s);
+        CPT(CollisionSolid) collider = cnode->get_solid(s);
         def._collider = collider;
         def._collider = collider;
         level_state.prepare_collider(def, root);
         level_state.prepare_collider(def, root);
 
 

+ 1 - 1
panda/src/display/graphicsEngine.cxx

@@ -1892,7 +1892,7 @@ get_window_renderer(const string &name, int pipeline_stage) {
   thread->set_min_pipeline_stage(pipeline_stage);
   thread->set_min_pipeline_stage(pipeline_stage);
   _pipeline->set_min_stages(pipeline_stage + 1);
   _pipeline->set_min_stages(pipeline_stage + 1);
 
 
-  thread->start(TP_normal, true, true);
+  thread->start(TP_normal, true);
   _threads[name] = thread;
   _threads[name] = thread;
 
 
   nassertr(thread->get_pipeline_stage() < _pipeline->get_num_stages(), thread.p());
   nassertr(thread->get_pipeline_stage() < _pipeline->get_num_stages(), thread.p());

+ 4 - 4
panda/src/display/graphicsEngine.h

@@ -93,10 +93,10 @@ PUBLISHED:
   int get_num_windows() const;
   int get_num_windows() const;
   GraphicsOutput *get_window(int n) const;
   GraphicsOutput *get_window(int n) const;
 
 
-  void render_frame();
-  void open_windows();
-  void sync_frame();
-  void flip_frame();
+  BLOCKING void render_frame();
+  BLOCKING void open_windows();
+  BLOCKING void sync_frame();
+  BLOCKING void flip_frame();
 
 
   bool extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg);
   bool extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg);
 
 

+ 12 - 12
panda/src/downloader/httpChannel.h

@@ -168,16 +168,16 @@ PUBLISHED:
   INLINE void clear_extra_headers();
   INLINE void clear_extra_headers();
   INLINE void send_extra_header(const string &key, const string &value);
   INLINE void send_extra_header(const string &key, const string &value);
 
 
-  INLINE bool get_document(const DocumentSpec &url);
-  INLINE bool get_subdocument(const DocumentSpec &url, 
-                              size_t first_byte, size_t last_byte);
-  INLINE bool get_header(const DocumentSpec &url);
-  INLINE bool post_form(const DocumentSpec &url, const string &body);
-  INLINE bool put_document(const DocumentSpec &url, const string &body);
-  INLINE bool delete_document(const DocumentSpec &url);
-  INLINE bool get_trace(const DocumentSpec &url);
-  INLINE bool connect_to(const DocumentSpec &url);
-  INLINE bool get_options(const DocumentSpec &url);
+  BLOCKING INLINE bool get_document(const DocumentSpec &url);
+  BLOCKING INLINE bool get_subdocument(const DocumentSpec &url, 
+                                       size_t first_byte, size_t last_byte);
+  BLOCKING INLINE bool get_header(const DocumentSpec &url);
+  BLOCKING INLINE bool post_form(const DocumentSpec &url, const string &body);
+  BLOCKING INLINE bool put_document(const DocumentSpec &url, const string &body);
+  BLOCKING INLINE bool delete_document(const DocumentSpec &url);
+  BLOCKING INLINE bool get_trace(const DocumentSpec &url);
+  BLOCKING INLINE bool connect_to(const DocumentSpec &url);
+  BLOCKING INLINE bool get_options(const DocumentSpec &url);
 
 
   INLINE void begin_get_document(const DocumentSpec &url);
   INLINE void begin_get_document(const DocumentSpec &url);
   INLINE void begin_get_subdocument(const DocumentSpec &url, 
   INLINE void begin_get_subdocument(const DocumentSpec &url, 
@@ -188,8 +188,8 @@ PUBLISHED:
   INLINE void begin_connect_to(const DocumentSpec &url);
   INLINE void begin_connect_to(const DocumentSpec &url);
 
 
   ISocketStream *read_body();
   ISocketStream *read_body();
-  bool download_to_file(const Filename &filename, bool subdocument_resumes = true);
-  bool download_to_ram(Ramfile *ramfile, bool subdocument_resumes = true);
+  BLOCKING bool download_to_file(const Filename &filename, bool subdocument_resumes = true);
+  BLOCKING bool download_to_ram(Ramfile *ramfile, bool subdocument_resumes = true);
   SocketStream *get_connection();
   SocketStream *get_connection();
 
 
   INLINE size_t get_bytes_downloaded() const;
   INLINE size_t get_bytes_downloaded() const;

+ 2 - 2
panda/src/event/asyncTaskManager.cxx

@@ -88,7 +88,7 @@ add(AsyncTask *task) {
       _threads.reserve(_num_threads);
       _threads.reserve(_num_threads);
       for (int i = 0; i < _num_threads; ++i) {
       for (int i = 0; i < _num_threads; ++i) {
         PT(AsyncTaskManagerThread) thread = new AsyncTaskManagerThread(this);
         PT(AsyncTaskManagerThread) thread = new AsyncTaskManagerThread(this);
-        if (thread->start(TP_low, true, true)) {
+        if (thread->start(TP_low, true)) {
           _threads.push_back(thread);
           _threads.push_back(thread);
         }
         }
       }
       }
@@ -137,7 +137,7 @@ add_and_do(AsyncTask *task) {
       _threads.reserve(_num_threads);
       _threads.reserve(_num_threads);
       for (int i = 0; i < _num_threads; ++i) {
       for (int i = 0; i < _num_threads; ++i) {
         PT(AsyncTaskManagerThread) thread = new AsyncTaskManagerThread(this);
         PT(AsyncTaskManagerThread) thread = new AsyncTaskManagerThread(this);
-        if (thread->start(TP_low, true, true)) {
+        if (thread->start(TP_low, true)) {
           _threads.push_back(thread);
           _threads.push_back(thread);
         }
         }
       }
       }

+ 10 - 10
panda/src/express/multifile.h

@@ -43,10 +43,10 @@ private:
   void operator = (const Multifile &copy);
   void operator = (const Multifile &copy);
 
 
 PUBLISHED:
 PUBLISHED:
-  bool open_read(const Filename &multifile_name);
-  bool open_write(const Filename &multifile_name);
-  bool open_read_write(const Filename &multifile_name);
-  void close();
+  BLOCKING bool open_read(const Filename &multifile_name);
+  BLOCKING bool open_write(const Filename &multifile_name);
+  BLOCKING bool open_read_write(const Filename &multifile_name);
+  BLOCKING void close();
 
 
   INLINE const Filename &get_multifile_name() const;
   INLINE const Filename &get_multifile_name() const;
 
 
@@ -71,8 +71,8 @@ PUBLISHED:
                      int compression_level);
                      int compression_level);
   string update_subfile(const string &subfile_name, const Filename &filename,
   string update_subfile(const string &subfile_name, const Filename &filename,
                         int compression_level);
                         int compression_level);
-  bool flush();
-  bool repack();
+  BLOCKING bool flush();
+  BLOCKING bool repack();
 
 
   int get_num_subfiles() const;
   int get_num_subfiles() const;
   int find_subfile(const string &subfile_name) const;
   int find_subfile(const string &subfile_name) const;
@@ -90,10 +90,10 @@ PUBLISHED:
   streampos get_subfile_internal_start(int index) const;
   streampos get_subfile_internal_start(int index) const;
   size_t get_subfile_internal_length(int index) const;
   size_t get_subfile_internal_length(int index) const;
 
 
-  INLINE string read_subfile(int index);
-  istream *open_read_subfile(int index);
-  bool extract_subfile(int index, const Filename &filename);
-  bool compare_subfile(int index, const Filename &filename);
+  BLOCKING INLINE string read_subfile(int index);
+  BLOCKING istream *open_read_subfile(int index);
+  BLOCKING bool extract_subfile(int index, const Filename &filename);
+  BLOCKING bool compare_subfile(int index, const Filename &filename);
 
 
   void output(ostream &out) const;
   void output(ostream &out) const;
   void ls(ostream &out = cout) const;
   void ls(ostream &out = cout) const;

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

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePointerTo::Constructor
 //     Function: NodePointerTo::Constructor
 //       Access: Public
 //       Access: Public
@@ -26,7 +27,9 @@ template<class T>
 INLINE NodePointerTo<T>::
 INLINE NodePointerTo<T>::
 NodePointerTo(To *ptr) : NodePointerToBase<T>(ptr) {
 NodePointerTo(To *ptr) : NodePointerToBase<T>(ptr) {
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePointerTo::Copy Constructor
 //     Function: NodePointerTo::Copy Constructor
 //       Access: Public
 //       Access: Public
@@ -38,7 +41,9 @@ NodePointerTo(const NodePointerTo<T> &copy) :
   NodePointerToBase<T>((const NodePointerToBase<T> &)copy)
   NodePointerToBase<T>((const NodePointerToBase<T> &)copy)
 {
 {
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePointerTo::Destructor
 //     Function: NodePointerTo::Destructor
 //       Access: Public
 //       Access: Public
@@ -48,7 +53,9 @@ template<class T>
 INLINE NodePointerTo<T>::
 INLINE NodePointerTo<T>::
 ~NodePointerTo() {
 ~NodePointerTo() {
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePointerTo::Dereference operator
 //     Function: NodePointerTo::Dereference operator
 //       Access: Public
 //       Access: Public
@@ -59,7 +66,9 @@ INLINE TYPENAME NodePointerTo<T>::To &NodePointerTo<T>::
 operator *() const {
 operator *() const {
   return *((To *)(this->_void_ptr));
   return *((To *)(this->_void_ptr));
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePointerTo::Member access operator
 //     Function: NodePointerTo::Member access operator
 //       Access: Public
 //       Access: Public
@@ -70,7 +79,9 @@ INLINE TYPENAME NodePointerTo<T>::To *NodePointerTo<T>::
 operator -> () const {
 operator -> () const {
   return (To *)(this->_void_ptr);
   return (To *)(this->_void_ptr);
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePointerTo::Typecast operator
 //     Function: NodePointerTo::Typecast operator
 //       Access: Public
 //       Access: Public
@@ -86,7 +97,9 @@ INLINE NodePointerTo<T>::
 operator T *() const {
 operator T *() const {
   return (To *)(this->_void_ptr);
   return (To *)(this->_void_ptr);
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePointerTo::p
 //     Function: NodePointerTo::p
 //       Access: Public
 //       Access: Public
@@ -99,7 +112,9 @@ INLINE TYPENAME NodePointerTo<T>::To *NodePointerTo<T>::
 p() const {
 p() const {
   return (To *)(this->_void_ptr);
   return (To *)(this->_void_ptr);
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePointerTo::Assignment operator
 //     Function: NodePointerTo::Assignment operator
 //       Access: Public
 //       Access: Public
@@ -111,7 +126,9 @@ operator = (To *ptr) {
   reassign(ptr);
   reassign(ptr);
   return *this;
   return *this;
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePointerTo::Assignment operator
 //     Function: NodePointerTo::Assignment operator
 //       Access: Public
 //       Access: Public
@@ -123,7 +140,9 @@ operator = (const NodePointerTo<T> &copy) {
   reassign((const NodePointerToBase<T> &)copy);
   reassign((const NodePointerToBase<T> &)copy);
   return *this;
   return *this;
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::Constructor
 //     Function: NodeConstPointerTo::Constructor
 //       Access: Public
 //       Access: Public
@@ -135,7 +154,9 @@ NodeConstPointerTo(const TYPENAME NodeConstPointerTo<T>::To *ptr) :
   NodePointerToBase<T>((TYPENAME NodeConstPointerTo<T>::To *)ptr)
   NodePointerToBase<T>((TYPENAME NodeConstPointerTo<T>::To *)ptr)
 {
 {
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::Copy Constructor
 //     Function: NodeConstPointerTo::Copy Constructor
 //       Access: Public
 //       Access: Public
@@ -147,7 +168,9 @@ NodeConstPointerTo(const NodePointerTo<T> &copy) :
   NodePointerToBase<T>((const NodePointerToBase<T> &)copy)
   NodePointerToBase<T>((const NodePointerToBase<T> &)copy)
 {
 {
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::Copy Constructor
 //     Function: NodeConstPointerTo::Copy Constructor
 //       Access: Public
 //       Access: Public
@@ -159,7 +182,9 @@ NodeConstPointerTo(const NodeConstPointerTo<T> &copy) :
   NodePointerToBase<T>((const NodePointerToBase<T> &)copy)
   NodePointerToBase<T>((const NodePointerToBase<T> &)copy)
 {
 {
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::Destructor
 //     Function: NodeConstPointerTo::Destructor
 //       Access: Public
 //       Access: Public
@@ -169,7 +194,9 @@ template<class T>
 INLINE NodeConstPointerTo<T>::
 INLINE NodeConstPointerTo<T>::
 ~NodeConstPointerTo() {
 ~NodeConstPointerTo() {
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::Dereference operator
 //     Function: NodeConstPointerTo::Dereference operator
 //       Access: Public
 //       Access: Public
@@ -180,7 +207,9 @@ INLINE const TYPENAME NodeConstPointerTo<T>::To &NodeConstPointerTo<T>::
 operator *() const {
 operator *() const {
   return *((To *)(this->_void_ptr));
   return *((To *)(this->_void_ptr));
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::Member access operator
 //     Function: NodeConstPointerTo::Member access operator
 //       Access: Public
 //       Access: Public
@@ -191,7 +220,9 @@ INLINE const TYPENAME NodeConstPointerTo<T>::To *NodeConstPointerTo<T>::
 operator -> () const {
 operator -> () const {
   return (To *)(this->_void_ptr);
   return (To *)(this->_void_ptr);
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::Typecast operator
 //     Function: NodeConstPointerTo::Typecast operator
 //       Access: Public
 //       Access: Public
@@ -208,7 +239,9 @@ INLINE NodeConstPointerTo<T>::
 operator const T * () const {
 operator const T * () const {
   return (To *)(this->_void_ptr);
   return (To *)(this->_void_ptr);
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::p
 //     Function: NodeConstPointerTo::p
 //       Access: Public
 //       Access: Public
@@ -221,7 +254,9 @@ INLINE const TYPENAME NodeConstPointerTo<T>::To *NodeConstPointerTo<T>::
 p() const {
 p() const {
   return (To *)(this->_void_ptr);
   return (To *)(this->_void_ptr);
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::Assignment operator
 //     Function: NodeConstPointerTo::Assignment operator
 //       Access: Public
 //       Access: Public
@@ -233,7 +268,9 @@ operator = (const To *ptr) {
   reassign((To *)ptr);
   reassign((To *)ptr);
   return *this;
   return *this;
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::Assignment operator
 //     Function: NodeConstPointerTo::Assignment operator
 //       Access: Public
 //       Access: Public
@@ -245,7 +282,9 @@ operator = (const NodePointerTo<T> &copy) {
   reassign((const NodePointerToBase<T> &)copy);
   reassign((const NodePointerToBase<T> &)copy);
   return *this;
   return *this;
 }
 }
+#endif  // CPPPARSER
 
 
+#ifndef CPPPARSER
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeConstPointerTo::Assignment operator
 //     Function: NodeConstPointerTo::Assignment operator
 //       Access: Public
 //       Access: Public
@@ -257,3 +296,4 @@ operator = (const NodeConstPointerTo<T> &copy) {
   reassign((const NodePointerToBase<T> &)copy);
   reassign((const NodePointerToBase<T> &)copy);
   return *this;
   return *this;
 }
 }
+#endif  // CPPPARSER

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

@@ -32,6 +32,9 @@
 template <class T>
 template <class T>
 class NodePointerTo : public NodePointerToBase<T> {
 class NodePointerTo : public NodePointerToBase<T> {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   typedef TYPENAME NodePointerToBase<T>::To To;
   typedef TYPENAME NodePointerToBase<T>::To To;
   INLINE NodePointerTo(To *ptr = (To *)NULL);
   INLINE NodePointerTo(To *ptr = (To *)NULL);
   INLINE NodePointerTo(const NodePointerTo<T> &copy);
   INLINE NodePointerTo(const NodePointerTo<T> &copy);
@@ -47,6 +50,7 @@ public:
 
 
   INLINE NodePointerTo<T> &operator = (To *ptr);
   INLINE NodePointerTo<T> &operator = (To *ptr);
   INLINE NodePointerTo<T> &operator = (const NodePointerTo<T> &copy);
   INLINE NodePointerTo<T> &operator = (const NodePointerTo<T> &copy);
+#endif  // CPPPARSER
 };
 };
 
 
 
 
@@ -58,6 +62,9 @@ public:
 template <class T>
 template <class T>
 class NodeConstPointerTo : public NodePointerToBase<T> {
 class NodeConstPointerTo : public NodePointerToBase<T> {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   typedef TYPENAME NodePointerToBase<T>::To To;
   typedef TYPENAME NodePointerToBase<T>::To To;
   INLINE NodeConstPointerTo(const To *ptr = (const To *)NULL);
   INLINE NodeConstPointerTo(const To *ptr = (const To *)NULL);
   INLINE NodeConstPointerTo(const NodePointerTo<T> &copy);
   INLINE NodeConstPointerTo(const NodePointerTo<T> &copy);
@@ -73,6 +80,7 @@ public:
   INLINE NodeConstPointerTo<T> &operator = (const To *ptr);
   INLINE NodeConstPointerTo<T> &operator = (const To *ptr);
   INLINE NodeConstPointerTo<T> &operator = (const NodePointerTo<T> &copy);
   INLINE NodeConstPointerTo<T> &operator = (const NodePointerTo<T> &copy);
   INLINE NodeConstPointerTo<T> &operator = (const NodeConstPointerTo<T> &copy);
   INLINE NodeConstPointerTo<T> &operator = (const NodeConstPointerTo<T> &copy);
+#endif  // CPPPARSER
 };
 };
 
 
 #define NPT(type) NodePointerTo< type >
 #define NPT(type) NodePointerTo< type >

+ 3 - 0
panda/src/express/pointerToArray.I

@@ -16,6 +16,8 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+#ifndef CPPPARSER
+
 template<class Element>
 template<class Element>
 pvector<Element> PointerToArray<Element>::_empty_array;
 pvector<Element> PointerToArray<Element>::_empty_array;
 
 
@@ -1006,3 +1008,4 @@ clear() {
 #endif // DO_PSTATS
 #endif // DO_PSTATS
 }
 }
 
 
+#endif  // CPPPARSEr

+ 9 - 15
panda/src/express/pointerToArray.h

@@ -99,6 +99,9 @@
 template <class Element>
 template <class Element>
 class PointerToArray : public PointerToArrayBase<Element> {
 class PointerToArray : public PointerToArrayBase<Element> {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   typedef TYPENAME PointerToArrayBase<Element>::To To;
   typedef TYPENAME PointerToArrayBase<Element>::To To;
   typedef TYPENAME pvector<Element>::value_type value_type;
   typedef TYPENAME pvector<Element>::value_type value_type;
   typedef TYPENAME pvector<Element>::reference reference;
   typedef TYPENAME pvector<Element>::reference reference;
@@ -110,7 +113,7 @@ public:
   typedef TYPENAME pvector<Element>::difference_type difference_type;
   typedef TYPENAME pvector<Element>::difference_type difference_type;
   typedef TYPENAME pvector<Element>::size_type size_type;
   typedef TYPENAME pvector<Element>::size_type size_type;
 
 
-PUBLISHED:
+public:
   INLINE PointerToArray();
   INLINE PointerToArray();
   INLINE static PointerToArray<Element> empty_array(size_type n);
   INLINE static PointerToArray<Element> empty_array(size_type n);
   INLINE PointerToArray(size_type n, const Element &value);
   INLINE PointerToArray(size_type n, const Element &value);
@@ -132,11 +135,7 @@ public:
 
 
   // Equality and comparison operators are pointerwise for
   // Equality and comparison operators are pointerwise for
   // PointerToArrays, not elementwise as in vector.
   // PointerToArrays, not elementwise as in vector.
-
-PUBLISHED:
   INLINE size_type size() const;
   INLINE size_type size() const;
-
-public:
   INLINE size_type max_size() const;
   INLINE size_type max_size() const;
   INLINE bool empty() const;
   INLINE bool empty() const;
 
 
@@ -159,7 +158,6 @@ public:
   INLINE void erase(iterator position);
   INLINE void erase(iterator position);
   INLINE void erase(iterator first, iterator last);
   INLINE void erase(iterator first, iterator last);
 
 
-PUBLISHED:
 #if !defined(WIN32_VC)
 #if !defined(WIN32_VC)
   INLINE reference operator [](size_type n) const;
   INLINE reference operator [](size_type n) const;
   INLINE reference operator [](int n) const;
   INLINE reference operator [](int n) const;
@@ -171,8 +169,6 @@ PUBLISHED:
   INLINE void pop_back();
   INLINE void pop_back();
   INLINE void make_empty();
   INLINE void make_empty();
 
 
-public:
-
   INLINE operator Element *() const;
   INLINE operator Element *() const;
   INLINE Element *p() const;
   INLINE Element *p() const;
   INLINE pvector<Element> &v() const;
   INLINE pvector<Element> &v() const;
@@ -206,6 +202,7 @@ private:
   // .so's, since it's a static member of a template class, but we
   // .so's, since it's a static member of a template class, but we
   // don't really care.
   // don't really care.
   static pvector<Element> _empty_array;
   static pvector<Element> _empty_array;
+#endif  // CPPPARSER
 };
 };
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -216,6 +213,9 @@ private:
 template <class Element>
 template <class Element>
 class ConstPointerToArray : public PointerToArrayBase<Element> {
 class ConstPointerToArray : public PointerToArrayBase<Element> {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   typedef TYPENAME PointerToArrayBase<Element>::To To;
   typedef TYPENAME PointerToArrayBase<Element>::To To;
   typedef TYPENAME pvector<Element>::value_type value_type;
   typedef TYPENAME pvector<Element>::value_type value_type;
   typedef TYPENAME pvector<Element>::const_reference reference;
   typedef TYPENAME pvector<Element>::const_reference reference;
@@ -232,14 +232,12 @@ public:
   typedef TYPENAME pvector<Element>::difference_type difference_type;
   typedef TYPENAME pvector<Element>::difference_type difference_type;
   typedef TYPENAME pvector<Element>::size_type size_type;
   typedef TYPENAME pvector<Element>::size_type size_type;
 
 
-PUBLISHED:
   INLINE ConstPointerToArray();
   INLINE ConstPointerToArray();
   INLINE ConstPointerToArray(const PointerToArray<Element> &copy);
   INLINE ConstPointerToArray(const PointerToArray<Element> &copy);
   INLINE ConstPointerToArray(const ConstPointerToArray<Element> &copy);
   INLINE ConstPointerToArray(const ConstPointerToArray<Element> &copy);
 
 
   INLINE PStatCollectorForwardBase *get_col() const;
   INLINE PStatCollectorForwardBase *get_col() const;
 
 
-public:
   // Duplicating the interface of vector.
   // Duplicating the interface of vector.
 
 
   INLINE iterator begin() const;
   INLINE iterator begin() const;
@@ -250,10 +248,7 @@ public:
   // Equality and comparison operators are pointerwise for
   // Equality and comparison operators are pointerwise for
   // PointerToArrays, not elementwise as in vector.
   // PointerToArrays, not elementwise as in vector.
 
 
-PUBLISHED:
   INLINE size_type size() const;
   INLINE size_type size() const;
-
-public:
   INLINE size_type max_size() const;
   INLINE size_type max_size() const;
   INLINE bool empty() const;
   INLINE bool empty() const;
 
 
@@ -262,14 +257,12 @@ public:
   INLINE reference front() const;
   INLINE reference front() const;
   INLINE reference back() const;
   INLINE reference back() const;
 
 
-PUBLISHED:
 #ifndef WIN32_VC
 #ifndef WIN32_VC
   INLINE reference operator [](size_type n) const;
   INLINE reference operator [](size_type n) const;
   INLINE reference operator [](int n) const;
   INLINE reference operator [](int n) const;
 #endif
 #endif
   INLINE const Element &get_element(size_type n) const;
   INLINE const Element &get_element(size_type n) const;
 
 
-public:
   INLINE operator const Element *() const;
   INLINE operator const Element *() const;
   INLINE const Element *p() const;
   INLINE const Element *p() const;
   INLINE const pvector<Element> &v() const;
   INLINE const pvector<Element> &v() const;
@@ -296,6 +289,7 @@ private:
   // .so's, since it's a static member of a template class, but we
   // .so's, since it's a static member of a template class, but we
   // don't really care.
   // don't really care.
   static pvector<Element> _empty_array;
   static pvector<Element> _empty_array;
+#endif  // CPPPARSER
 };
 };
 
 
 
 

+ 10 - 10
panda/src/express/virtualFile.h

@@ -48,18 +48,18 @@ PUBLISHED:
   virtual bool is_directory() const;
   virtual bool is_directory() const;
   virtual bool is_regular_file() const;
   virtual bool is_regular_file() const;
 
 
-  PT(VirtualFileList) scan_directory() const;
+  BLOCKING PT(VirtualFileList) scan_directory() const;
 
 
   void output(ostream &out) const;
   void output(ostream &out) const;
-  void ls(ostream &out = cout) const;
-  void ls_all(ostream &out = cout) const;
-
-  INLINE string read_file(bool auto_unwrap) const;
-  virtual istream *open_read_file(bool auto_unwrap) const;
-  void close_read_file(istream *stream) const;
-  virtual off_t get_file_size(istream *stream) const;
-  virtual off_t get_file_size() const;
-  virtual time_t get_timestamp() const;
+  BLOCKING void ls(ostream &out = cout) const;
+  BLOCKING void ls_all(ostream &out = cout) const;
+
+  BLOCKING INLINE string read_file(bool auto_unwrap) const;
+  BLOCKING virtual istream *open_read_file(bool auto_unwrap) const;
+  BLOCKING void close_read_file(istream *stream) const;
+  BLOCKING virtual off_t get_file_size(istream *stream) const;
+  BLOCKING virtual off_t get_file_size() const;
+  BLOCKING virtual time_t get_timestamp() const;
 
 
 public:
 public:
   INLINE void set_original_filename(const Filename &filename);
   INLINE void set_original_filename(const Filename &filename);

+ 25 - 25
panda/src/express/virtualFileSystem.h

@@ -52,28 +52,28 @@ PUBLISHED:
     MF_read_only      = 0x0002,
     MF_read_only      = 0x0002,
   };
   };
 
 
-  bool mount(Multifile *multifile, const string &mount_point, int flags);
-  bool mount(const Filename &physical_filename, const string &mount_point, 
-             int flags, const string &password = "");
-  int unmount(Multifile *multifile);
-  int unmount(const Filename &physical_filename);
-  int unmount_point(const string &mount_point);
-  int unmount_all();
-
-  bool chdir(const string &new_directory);
-  const Filename &get_cwd() const;
-
-  PT(VirtualFile) get_file(const Filename &filename) const;
-  PT(VirtualFile) find_file(const Filename &filename, 
-                            const DSearchPath &searchpath) const;
-  bool resolve_filename(Filename &filename, const DSearchPath &searchpath,
-                        const string &default_extension = string()) const;
-  int find_all_files(const Filename &filename, const DSearchPath &searchpath,
-                     DSearchPath::Results &results) const;
-
-  INLINE bool exists(const Filename &filename) const;
-  INLINE bool is_directory(const Filename &filename) const;
-  INLINE bool is_regular_file(const Filename &filename) const;
+  BLOCKING bool mount(Multifile *multifile, const string &mount_point, int flags);
+  BLOCKING bool mount(const Filename &physical_filename, const string &mount_point, 
+                      int flags, const string &password = "");
+  BLOCKING int unmount(Multifile *multifile);
+  BLOCKING int unmount(const Filename &physical_filename);
+  BLOCKING int unmount_point(const string &mount_point);
+  BLOCKING int unmount_all();
+
+  BLOCKING bool chdir(const string &new_directory);
+  BLOCKING const Filename &get_cwd() const;
+
+  BLOCKING PT(VirtualFile) get_file(const Filename &filename) const;
+  BLOCKING PT(VirtualFile) find_file(const Filename &filename, 
+                                     const DSearchPath &searchpath) const;
+  BLOCKING bool resolve_filename(Filename &filename, const DSearchPath &searchpath,
+                                 const string &default_extension = string()) const;
+  BLOCKING int find_all_files(const Filename &filename, const DSearchPath &searchpath,
+                              DSearchPath::Results &results) const;
+
+  BLOCKING INLINE bool exists(const Filename &filename) const;
+  BLOCKING INLINE bool is_directory(const Filename &filename) const;
+  BLOCKING INLINE bool is_regular_file(const Filename &filename) const;
 
 
   INLINE void ls(const string &filename) const;
   INLINE void ls(const string &filename) const;
   INLINE void ls_all(const string &filename) const;
   INLINE void ls_all(const string &filename) const;
@@ -82,9 +82,9 @@ PUBLISHED:
 
 
   static VirtualFileSystem *get_global_ptr();
   static VirtualFileSystem *get_global_ptr();
 
 
-  INLINE string read_file(const Filename &filename, bool auto_unwrap) const;
-  INLINE istream *open_read_file(const Filename &filename, bool auto_unwrap) const;
-  void close_read_file(istream *stream) const;
+  BLOCKING INLINE string read_file(const Filename &filename, bool auto_unwrap) const;
+  BLOCKING INLINE istream *open_read_file(const Filename &filename, bool auto_unwrap) const;
+  BLOCKING void close_read_file(istream *stream) const;
 
 
 public:
 public:
   INLINE bool read_file(const Filename &filename, string &result, bool auto_unwrap) const;
   INLINE bool read_file(const Filename &filename, string &result, bool auto_unwrap) const;

+ 8 - 11
panda/src/gobj/geom.I

@@ -88,7 +88,7 @@ get_usage_hint() const {
 INLINE CPT(GeomVertexData) Geom::
 INLINE CPT(GeomVertexData) Geom::
 get_vertex_data(Thread *current_thread) const {
 get_vertex_data(Thread *current_thread) const {
   CDReader cdata(_cycler, current_thread);
   CDReader cdata(_cycler, current_thread);
-  return cdata->_data;
+  return cdata->_data.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -112,11 +112,11 @@ get_num_primitives() const {
 //               inspect the ith object; use modify_primitive() or
 //               inspect the ith object; use modify_primitive() or
 //               set_primitive() if you want to modify it.
 //               set_primitive() if you want to modify it.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const GeomPrimitive *Geom::
+INLINE CPT(GeomPrimitive) Geom::
 get_primitive(int i) const {
 get_primitive(int i) const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
   nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL);
   nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL);
-  return cdata->_primitives[i];
+  return cdata->_primitives[i].get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -131,7 +131,7 @@ get_primitive(int i) const {
 //               don't mind it blowing away other changes you might
 //               don't mind it blowing away other changes you might
 //               have recently made in an upstream thread.
 //               have recently made in an upstream thread.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE GeomPrimitive *Geom::
+INLINE PT(GeomPrimitive) Geom::
 modify_primitive(int i) {
 modify_primitive(int i) {
   Thread *current_thread = Thread::get_current_thread();
   Thread *current_thread = Thread::get_current_thread();
   CDWriter cdata(_cycler, true, current_thread);
   CDWriter cdata(_cycler, true, current_thread);
@@ -139,10 +139,7 @@ modify_primitive(int i) {
   cdata->_got_usage_hint = false;
   cdata->_got_usage_hint = false;
   cdata->_modified = Geom::get_next_modified();
   cdata->_modified = Geom::get_next_modified();
   clear_cache_stage(current_thread);
   clear_cache_stage(current_thread);
-  if (cdata->_primitives[i]->get_ref_count() > 1) {
-    cdata->_primitives[i] = cdata->_primitives[i]->make_copy();
-  }
-  return cdata->_primitives[i];
+  return cdata->_primitives[i].get_write_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -587,7 +584,7 @@ get_usage_hint() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(GeomVertexData) GeomPipelineReader::
 INLINE CPT(GeomVertexData) GeomPipelineReader::
 get_vertex_data() const {
 get_vertex_data() const {
-  return _cdata->_data;
+  return _cdata->_data.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -605,10 +602,10 @@ get_num_primitives() const {
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const GeomPrimitive *GeomPipelineReader::
+INLINE CPT(GeomPrimitive) GeomPipelineReader::
 get_primitive(int i) const {
 get_primitive(int i) const {
   nassertr(i >= 0 && i < (int)_cdata->_primitives.size(), NULL);
   nassertr(i >= 0 && i < (int)_cdata->_primitives.size(), NULL);
-  return _cdata->_primitives[i];
+  return _cdata->_primitives[i].get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 57 - 50
panda/src/gobj/geom.cxx

@@ -37,6 +37,16 @@ TypeHandle Geom::CacheEntry::_type_handle;
 TypeHandle Geom::CData::_type_handle;
 TypeHandle Geom::CData::_type_handle;
 TypeHandle GeomPipelineReader::_type_handle;
 TypeHandle GeomPipelineReader::_type_handle;
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Geom::make_cow_copy
+//       Access: Protected, Virtual
+//  Description: Required to implement CopyOnWriteObject.
+////////////////////////////////////////////////////////////////////
+PT(CopyOnWriteObject) Geom::
+make_cow_copy() {
+  return make_copy();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::Constructor
 //     Function: Geom::Constructor
 //       Access: Published
 //       Access: Published
@@ -59,7 +69,7 @@ Geom(const GeomVertexData *data) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 Geom::
 Geom::
 Geom(const Geom &copy) :
 Geom(const Geom &copy) :
-  TypedWritableReferenceCount(copy),
+  CopyOnWriteObject(copy),
   _cycler(copy._cycler)  
   _cycler(copy._cycler)  
 {
 {
 }
 }
@@ -74,7 +84,7 @@ Geom(const Geom &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Geom::
 void Geom::
 operator = (const Geom &copy) {
 operator = (const Geom &copy) {
-  TypedWritableReferenceCount::operator = (copy);
+  CopyOnWriteObject::operator = (copy);
 
 
   clear_cache();
   clear_cache();
 
 
@@ -100,7 +110,7 @@ Geom::
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::make_copy
 //     Function: Geom::make_copy
-//       Access: Public, Virtual
+//       Access: Protected, Virtual
 //  Description: Returns a newly-allocated Geom that is a shallow copy
 //  Description: Returns a newly-allocated Geom that is a shallow copy
 //               of this one.  It will be a different Geom pointer,
 //               of this one.  It will be a different Geom pointer,
 //               but its internal data may or may not be shared with
 //               but its internal data may or may not be shared with
@@ -130,10 +140,8 @@ set_usage_hint(Geom::UsageHint usage_hint) {
 
 
   Primitives::iterator pi;
   Primitives::iterator pi;
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
-    if ((*pi)->get_ref_count() > 1) {
-      (*pi) = (*pi)->make_copy();
-    }
-    (*pi)->set_usage_hint(usage_hint);
+    PT(GeomPrimitive) prim = (*pi).get_write_pointer();
+    prim->set_usage_hint(usage_hint);
   }
   }
 
 
   clear_cache_stage(current_thread);
   clear_cache_stage(current_thread);
@@ -158,12 +166,9 @@ modify_vertex_data() {
   // is greater than 1, assume some other Geom has the same pointer,
   // is greater than 1, assume some other Geom has the same pointer,
   // so make a copy of it first.
   // so make a copy of it first.
   CDWriter cdata(_cycler, true, current_thread);
   CDWriter cdata(_cycler, true, current_thread);
-  if (cdata->_data->get_ref_count() > 1) {
-    cdata->_data = new GeomVertexData(*cdata->_data);
-  }
   clear_cache_stage(current_thread);
   clear_cache_stage(current_thread);
   mark_internal_bounds_stale(cdata);
   mark_internal_bounds_stale(cdata);
-  return cdata->_data;
+  return cdata->_data.get_write_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -213,13 +218,11 @@ offset_vertices(const GeomVertexData *data, int offset) {
 #endif
 #endif
   Primitives::iterator pi;
   Primitives::iterator pi;
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
-    if ((*pi)->get_ref_count() > 1) {
-      (*pi) = (*pi)->make_copy();
-    }
-    (*pi)->offset_vertices(offset);
+    PT(GeomPrimitive) prim = (*pi).get_write_pointer();
+    prim->offset_vertices(offset);
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
-    if (!(*pi)->check_valid(data)) {
+    if (!prim->check_valid(data)) {
       all_is_valid = false;
       all_is_valid = false;
     }
     }
 #endif
 #endif
@@ -249,8 +252,8 @@ make_nonindexed(bool composite_only) {
   int num_changed = 0;
   int num_changed = 0;
 
 
   CDWriter cdata(_cycler, true, current_thread);
   CDWriter cdata(_cycler, true, current_thread);
-  CPT(GeomVertexData) orig_data = cdata->_data;
-  PT(GeomVertexData) new_data = new GeomVertexData(*cdata->_data);
+  CPT(GeomVertexData) orig_data = cdata->_data.get_read_pointer();
+  PT(GeomVertexData) new_data = new GeomVertexData(*orig_data);
   new_data->clear_rows();
   new_data->clear_rows();
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
@@ -260,8 +263,8 @@ make_nonindexed(bool composite_only) {
   Primitives new_prims;
   Primitives new_prims;
   new_prims.reserve(cdata->_primitives.size());
   new_prims.reserve(cdata->_primitives.size());
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
-    PT(GeomPrimitive) primitive = (*pi)->make_copy();
-    new_prims.push_back(primitive);
+    PT(GeomPrimitive) primitive = (*pi).get_read_pointer()->make_copy();
+    new_prims.push_back(primitive.p());
 
 
     // GeomPoints are considered "composite" for the purposes of
     // GeomPoints are considered "composite" for the purposes of
     // making nonindexed, since there's no particular advantage to
     // making nonindexed, since there's no particular advantage to
@@ -316,7 +319,7 @@ set_primitive(int i, const GeomPrimitive *primitive) {
   Thread *current_thread = Thread::get_current_thread();
   Thread *current_thread = Thread::get_current_thread();
   CDWriter cdata(_cycler, true, current_thread);
   CDWriter cdata(_cycler, true, current_thread);
   nassertv(i >= 0 && i < (int)cdata->_primitives.size());
   nassertv(i >= 0 && i < (int)cdata->_primitives.size());
-  nassertv(primitive->check_valid(cdata->_data));
+  nassertv(primitive->check_valid(cdata->_data.get_read_pointer()));
 
 
   // All primitives within a particular Geom must have the same
   // All primitives within a particular Geom must have the same
   // fundamental primitive type (triangles, points, or lines).
   // fundamental primitive type (triangles, points, or lines).
@@ -362,7 +365,7 @@ add_primitive(const GeomPrimitive *primitive) {
   Thread *current_thread = Thread::get_current_thread();
   Thread *current_thread = Thread::get_current_thread();
   CDWriter cdata(_cycler, true, current_thread);
   CDWriter cdata(_cycler, true, current_thread);
 
 
-  nassertv(primitive->check_valid(cdata->_data));
+  nassertv(primitive->check_valid(cdata->_data.get_read_pointer()));
 
 
   // All primitives within a particular Geom must have the same
   // All primitives within a particular Geom must have the same
   // fundamental primitive type (triangles, points, or lines).
   // fundamental primitive type (triangles, points, or lines).
@@ -462,11 +465,11 @@ decompose_in_place() {
 #endif
 #endif
   Primitives::iterator pi;
   Primitives::iterator pi;
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
-    CPT(GeomPrimitive) new_prim = (*pi)->decompose();
+    CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer()->decompose();
     (*pi) = (GeomPrimitive *)new_prim.p();
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
-    if (!(*pi)->check_valid(cdata->_data)) {
+    if (!new_prim->check_valid(cdata->_data.get_read_pointer())) {
       all_is_valid = false;
       all_is_valid = false;
     }
     }
 #endif
 #endif
@@ -500,11 +503,11 @@ rotate_in_place() {
 #endif
 #endif
   Primitives::iterator pi;
   Primitives::iterator pi;
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
-    CPT(GeomPrimitive) new_prim = (*pi)->rotate();
+    CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer()->rotate();
     (*pi) = (GeomPrimitive *)new_prim.p();
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
-    if (!(*pi)->check_valid(cdata->_data)) {
+    if (!new_prim->check_valid(cdata->_data.get_read_pointer())) {
       all_is_valid = false;
       all_is_valid = false;
     }
     }
 #endif
 #endif
@@ -548,7 +551,7 @@ unify_in_place(int max_indices) {
 
 
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
   for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
-    CPT(GeomPrimitive) primitive = (*pi);
+    CPT(GeomPrimitive) primitive = (*pi).get_read_pointer();
     if (new_prim == (GeomPrimitive *)NULL) {
     if (new_prim == (GeomPrimitive *)NULL) {
       // The first primitive type we come across is copied directly.
       // The first primitive type we come across is copied directly.
       new_prim = primitive->make_copy();
       new_prim = primitive->make_copy();
@@ -577,7 +580,7 @@ unify_in_place(int max_indices) {
   }
   }
 
 
   // Now we have just one primitive.
   // Now we have just one primitive.
-  nassertv(new_prim->check_valid(cdata->_data));
+  nassertv(new_prim->check_valid(cdata->_data.get_read_pointer()));
 
 
   // The new primitive, naturally, inherits the Geom's overall shade
   // The new primitive, naturally, inherits the Geom's overall shade
   // model.
   // model.
@@ -607,12 +610,12 @@ unify_in_place(int max_indices) {
         ++i;
         ++i;
       }
       }
 
 
-      cdata->_primitives.push_back(smaller);
+      cdata->_primitives.push_back(smaller.p());
     }
     }
 
 
   } else {
   } else {
     // The new_prim has few enough vertices; keep it.
     // The new_prim has few enough vertices; keep it.
-    cdata->_primitives.push_back(new_prim);
+    cdata->_primitives.push_back(new_prim.p());
   }
   }
 
 
   cdata->_modified = Geom::get_next_modified();
   cdata->_modified = Geom::get_next_modified();
@@ -685,7 +688,7 @@ get_num_bytes() const {
   for (pi = cdata->_primitives.begin(); 
   for (pi = cdata->_primitives.begin(); 
        pi != cdata->_primitives.end();
        pi != cdata->_primitives.end();
        ++pi) {
        ++pi) {
-    num_bytes += (*pi)->get_num_bytes();
+    num_bytes += (*pi).get_read_pointer()->get_num_bytes();
   }
   }
 
 
   return num_bytes;
   return num_bytes;
@@ -799,8 +802,9 @@ output(ostream &out) const {
   for (pi = cdata->_primitives.begin(); 
   for (pi = cdata->_primitives.begin(); 
        pi != cdata->_primitives.end();
        pi != cdata->_primitives.end();
        ++pi) {
        ++pi) {
-    num_faces += (*pi)->get_num_faces();
-    types.insert((*pi)->get_type());
+    CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
+    num_faces += prim->get_num_faces();
+    types.insert(prim->get_type());
   }
   }
 
 
   out << get_type() << " [";
   out << get_type() << " [";
@@ -825,7 +829,7 @@ write(ostream &out, int indent_level) const {
   for (pi = cdata->_primitives.begin(); 
   for (pi = cdata->_primitives.begin(); 
        pi != cdata->_primitives.end();
        pi != cdata->_primitives.end();
        ++pi) {
        ++pi) {
-    (*pi)->write(out, indent_level);
+    (*pi).get_read_pointer()->write(out, indent_level);
   }
   }
 }
 }
 
 
@@ -1067,8 +1071,9 @@ do_calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
   for (pi = cdata->_primitives.begin(); 
   for (pi = cdata->_primitives.begin(); 
        pi != cdata->_primitives.end();
        pi != cdata->_primitives.end();
        ++pi) {
        ++pi) {
-    (*pi)->calc_tight_bounds(min_point, max_point, found_any, vertex_data,
-                             got_mat, mat, current_thread);
+    CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
+    prim->calc_tight_bounds(min_point, max_point, found_any, vertex_data,
+                            got_mat, mat, current_thread);
   }
   }
 }
 }
 
 
@@ -1112,7 +1117,7 @@ check_will_be_valid(const GeomVertexData *vertex_data) const {
   for (pi = cdata->_primitives.begin(); 
   for (pi = cdata->_primitives.begin(); 
        pi != cdata->_primitives.end();
        pi != cdata->_primitives.end();
        ++pi) {
        ++pi) {
-    if (!(*pi)->check_valid(vertex_data)) {
+    if (!(*pi).get_read_pointer()->check_valid(vertex_data)) {
       return false;
       return false;
     }
     }
   }
   }
@@ -1132,7 +1137,8 @@ reset_usage_hint(Geom::CData *cdata) {
   for (pi = cdata->_primitives.begin(); 
   for (pi = cdata->_primitives.begin(); 
        pi != cdata->_primitives.end();
        pi != cdata->_primitives.end();
        ++pi) {
        ++pi) {
-    cdata->_usage_hint = min(cdata->_usage_hint, (*pi)->get_usage_hint());
+    cdata->_usage_hint = min(cdata->_usage_hint, 
+                             (*pi).get_read_pointer()->get_usage_hint());
   }
   }
   cdata->_got_usage_hint = true;
   cdata->_got_usage_hint = true;
 }
 }
@@ -1149,17 +1155,18 @@ reset_geom_rendering(Geom::CData *cdata) {
   for (pi = cdata->_primitives.begin(); 
   for (pi = cdata->_primitives.begin(); 
        pi != cdata->_primitives.end();
        pi != cdata->_primitives.end();
        ++pi) {
        ++pi) {
-    cdata->_geom_rendering |= (*pi)->get_geom_rendering();
+    cdata->_geom_rendering |= (*pi).get_read_pointer()->get_geom_rendering();
   }
   }
 
 
   if ((cdata->_geom_rendering & GR_point) != 0) {
   if ((cdata->_geom_rendering & GR_point) != 0) {
-    if (cdata->_data->has_column(InternalName::get_size())) {
+    CPT(GeomVertexData) data = cdata->_data.get_read_pointer();
+    if (data->has_column(InternalName::get_size())) {
       cdata->_geom_rendering |= GR_per_point_size;
       cdata->_geom_rendering |= GR_per_point_size;
     }
     }
-    if (cdata->_data->has_column(InternalName::get_aspect_ratio())) {
+    if (data->has_column(InternalName::get_aspect_ratio())) {
       cdata->_geom_rendering |= GR_point_aspect_ratio;
       cdata->_geom_rendering |= GR_point_aspect_ratio;
     }
     }
-    if (cdata->_data->has_column(InternalName::get_rotate())) {
+    if (data->has_column(InternalName::get_rotate())) {
       cdata->_geom_rendering |= GR_point_rotate;
       cdata->_geom_rendering |= GR_point_rotate;
     }
     }
   }
   }
@@ -1237,8 +1244,8 @@ finalize(BamReader *manager) {
   // Make sure our GeomVertexData is finalized first.  This may result
   // Make sure our GeomVertexData is finalized first.  This may result
   // in the data getting finalized multiple times, but it doesn't mind
   // in the data getting finalized multiple times, but it doesn't mind
   // that.
   // that.
-  if (cdata->_data != (GeomVertexData *)NULL) {
-    cdata->_data->finalize(manager);
+  if (!cdata->_data.is_null()) {
+    cdata->_data.get_write_pointer()->finalize(manager);
   }
   }
 
 
   reset_geom_rendering(cdata);
   reset_geom_rendering(cdata);
@@ -1323,12 +1330,12 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Geom::CData::
 void Geom::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
 write_datagram(BamWriter *manager, Datagram &dg) const {
-  manager->write_pointer(dg, _data);
+  manager->write_pointer(dg, _data.get_read_pointer());
 
 
   dg.add_uint16(_primitives.size());
   dg.add_uint16(_primitives.size());
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
   for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
   for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
-    manager->write_pointer(dg, *pi);
+    manager->write_pointer(dg, (*pi).get_read_pointer());
   }
   }
 
 
   dg.add_uint8(_primitive_type);
   dg.add_uint8(_primitive_type);
@@ -1402,12 +1409,12 @@ check_usage_hint() const {
     // already have modified the pointer on the object since we
     // already have modified the pointer on the object since we
     // queried it.
     // queried it.
     {
     {
-      Geom::CDWriter fresh_cdata(((Geom *)_object)->_cycler, 
+      Geom::CDWriter fresh_cdata(((Geom *)_object.p())->_cycler, 
                                  false, _current_thread);
                                  false, _current_thread);
       if (!fresh_cdata->_got_usage_hint) {
       if (!fresh_cdata->_got_usage_hint) {
         // The cache is still stale.  We have to do the work of
         // The cache is still stale.  We have to do the work of
         // freshening it.
         // freshening it.
-        ((Geom *)_object)->reset_usage_hint(fresh_cdata);
+        ((Geom *)_object.p())->reset_usage_hint(fresh_cdata);
         nassertv(fresh_cdata->_got_usage_hint);
         nassertv(fresh_cdata->_got_usage_hint);
       }
       }
 
 
@@ -1436,7 +1443,7 @@ check_valid(const GeomVertexDataPipelineReader *data_reader) const {
   for (pi = _cdata->_primitives.begin(); 
   for (pi = _cdata->_primitives.begin(); 
        pi != _cdata->_primitives.end();
        pi != _cdata->_primitives.end();
        ++pi) {
        ++pi) {
-    const GeomPrimitive *primitive = (*pi);
+    CPT(GeomPrimitive) primitive = (*pi).get_read_pointer();
     GeomPrimitivePipelineReader reader(primitive, _current_thread);
     GeomPrimitivePipelineReader reader(primitive, _current_thread);
     reader.check_minmax();
     reader.check_minmax();
     if (!reader.check_valid(data_reader)) {
     if (!reader.check_valid(data_reader)) {
@@ -1461,7 +1468,7 @@ draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
     for (pi = _cdata->_primitives.begin(); 
     for (pi = _cdata->_primitives.begin(); 
          pi != _cdata->_primitives.end();
          pi != _cdata->_primitives.end();
          ++pi) {
          ++pi) {
-      const GeomPrimitive *primitive = (*pi);
+      CPT(GeomPrimitive) primitive = (*pi).get_read_pointer();
       GeomPrimitivePipelineReader reader(primitive, _current_thread);
       GeomPrimitivePipelineReader reader(primitive, _current_thread);
       if (reader.get_num_vertices() != 0) {
       if (reader.get_num_vertices() != 0) {
         reader.check_minmax();
         reader.check_minmax();

+ 16 - 10
panda/src/gobj/geom.h

@@ -20,7 +20,8 @@
 #define GEOM_H
 #define GEOM_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "typedWritableReferenceCount.h"
+#include "copyOnWriteObject.h"
+#include "copyOnWritePointer.h"
 #include "cycleData.h"
 #include "cycleData.h"
 #include "cycleDataLockedReader.h"
 #include "cycleDataLockedReader.h"
 #include "cycleDataReader.h"
 #include "cycleDataReader.h"
@@ -58,11 +59,16 @@ class PreparedGraphicsObjects;
 //               and all of them must be rendered at the same time, in
 //               and all of them must be rendered at the same time, in
 //               the same graphics state.
 //               the same graphics state.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA Geom : public TypedWritableReferenceCount, public GeomEnums {
+class EXPCL_PANDA Geom : public CopyOnWriteObject, public GeomEnums {
+protected:
+  virtual PT(CopyOnWriteObject) make_cow_copy();
+
 PUBLISHED:
 PUBLISHED:
   Geom(const GeomVertexData *data);
   Geom(const GeomVertexData *data);
+
 protected:
 protected:
   Geom(const Geom &copy);
   Geom(const Geom &copy);
+
 PUBLISHED:
 PUBLISHED:
   void operator = (const Geom &copy);
   void operator = (const Geom &copy);
   virtual ~Geom();
   virtual ~Geom();
@@ -84,8 +90,8 @@ PUBLISHED:
   int make_nonindexed(bool composite_only);
   int make_nonindexed(bool composite_only);
 
 
   INLINE int get_num_primitives() const;
   INLINE int get_num_primitives() const;
-  INLINE const GeomPrimitive *get_primitive(int i) const;
-  INLINE GeomPrimitive *modify_primitive(int i);
+  INLINE CPT(GeomPrimitive) get_primitive(int i) const;
+  INLINE PT(GeomPrimitive) modify_primitive(int i);
   void set_primitive(int i, const GeomPrimitive *primitive);
   void set_primitive(int i, const GeomPrimitive *primitive);
   void add_primitive(const GeomPrimitive *primitive);
   void add_primitive(const GeomPrimitive *primitive);
   void remove_primitive(int i);
   void remove_primitive(int i);
@@ -165,7 +171,7 @@ private:
   void reset_geom_rendering(CData *cdata);
   void reset_geom_rendering(CData *cdata);
 
 
 private:
 private:
-  typedef pvector<PT(GeomPrimitive) > Primitives;
+  typedef pvector<COWPT(GeomPrimitive) > Primitives;
 
 
   // We have to use reference-counting pointers here instead of having
   // We have to use reference-counting pointers here instead of having
   // explicit cleanup in the GeomVertexFormat destructor, because the
   // explicit cleanup in the GeomVertexFormat destructor, because the
@@ -269,7 +275,7 @@ private:
       return Geom::get_class_type();
       return Geom::get_class_type();
     }
     }
 
 
-    PT(GeomVertexData) _data;
+    COWPT(GeomVertexData) _data;
     Primitives _primitives;
     Primitives _primitives;
     PrimitiveType _primitive_type;
     PrimitiveType _primitive_type;
     ShadeModel _shade_model;
     ShadeModel _shade_model;
@@ -327,9 +333,9 @@ public:
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
-    TypedWritableReferenceCount::init_type();
+    CopyOnWriteObject::init_type();
     register_type(_type_handle, "Geom",
     register_type(_type_handle, "Geom",
-                  TypedWritableReferenceCount::get_class_type());
+                  CopyOnWriteObject::get_class_type());
     CDataCache::init_type();
     CDataCache::init_type();
     CacheEntry::init_type();
     CacheEntry::init_type();
     CData::init_type();
     CData::init_type();
@@ -376,7 +382,7 @@ public:
   INLINE UsageHint get_usage_hint() const;
   INLINE UsageHint get_usage_hint() const;
   INLINE CPT(GeomVertexData) get_vertex_data() const;
   INLINE CPT(GeomVertexData) get_vertex_data() const;
   INLINE int get_num_primitives() const;
   INLINE int get_num_primitives() const;
-  INLINE const GeomPrimitive *get_primitive(int i) const;
+  INLINE CPT(GeomPrimitive) get_primitive(int i) const;
 
 
   INLINE UpdateSeq get_modified() const;
   INLINE UpdateSeq get_modified() const;
 
 
@@ -386,7 +392,7 @@ public:
             const GeomVertexDataPipelineReader *data_reader) const;
             const GeomVertexDataPipelineReader *data_reader) const;
 
 
 private:
 private:
-  const Geom *_object;
+  CPT(Geom) _object;
   Thread *_current_thread;
   Thread *_current_thread;
   const Geom::CData *_cdata;
   const Geom::CData *_cdata;
 
 

+ 14 - 16
panda/src/gobj/geomPrimitive.I

@@ -241,7 +241,7 @@ get_max_vertex() const {
 INLINE int GeomPrimitive::
 INLINE int GeomPrimitive::
 get_data_size_bytes() const {
 get_data_size_bytes() const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
-  return cdata->_vertices->get_data_size_bytes();
+  return cdata->_vertices.get_read_pointer()->get_data_size_bytes();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -284,10 +284,10 @@ check_valid(const GeomVertexData *vertex_data) const {
 //               attempt to modify the returned array; use
 //               attempt to modify the returned array; use
 //               modify_vertices() or set_vertices() for this.
 //               modify_vertices() or set_vertices() for this.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const GeomVertexArrayData *GeomPrimitive::
+INLINE CPT(GeomVertexArrayData) GeomPrimitive::
 get_vertices() const {
 get_vertices() const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
-  return cdata->_vertices;
+  return cdata->_vertices.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -344,7 +344,7 @@ get_ends() const {
 //               Note that simple primitive types, like triangles, do
 //               Note that simple primitive types, like triangles, do
 //               not have a mins array.
 //               not have a mins array.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const GeomVertexArrayData *GeomPrimitive::
+INLINE CPT(GeomVertexArrayData) GeomPrimitive::
 get_mins() const {
 get_mins() const {
   GeomPrimitivePipelineReader reader(this, Thread::get_current_thread());
   GeomPrimitivePipelineReader reader(this, Thread::get_current_thread());
   reader.check_minmax();
   reader.check_minmax();
@@ -362,7 +362,7 @@ get_mins() const {
 //               Note that simple primitive types, like triangles, do
 //               Note that simple primitive types, like triangles, do
 //               not have a maxs array.
 //               not have a maxs array.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const GeomVertexArrayData *GeomPrimitive::
+INLINE CPT(GeomVertexArrayData) GeomPrimitive::
 get_maxs() const {
 get_maxs() const {
   GeomPrimitivePipelineReader reader(this, Thread::get_current_thread());
   GeomPrimitivePipelineReader reader(this, Thread::get_current_thread());
   reader.check_minmax();
   reader.check_minmax();
@@ -485,10 +485,9 @@ GeomPrimitivePipelineReader(const GeomPrimitive *object,
 #ifdef DO_PIPELINING
 #ifdef DO_PIPELINING
   _cdata->ref();
   _cdata->ref();
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
-  if (_cdata->_vertices != (GeomVertexArrayData *)NULL) {
+  if (!_cdata->_vertices.is_null()) {
     _vertices_reader =
     _vertices_reader =
-      new GeomVertexArrayDataPipelineReader(_cdata->_vertices, _current_thread);
-    nassertv(_vertices_reader->get_object() == _cdata->_vertices);
+      new GeomVertexArrayDataPipelineReader(_cdata->_vertices.get_read_pointer(), _current_thread);
   }
   }
 }
 }
 
 
@@ -520,7 +519,6 @@ operator = (const GeomPrimitivePipelineReader &) {
 INLINE GeomPrimitivePipelineReader::
 INLINE GeomPrimitivePipelineReader::
 ~GeomPrimitivePipelineReader() {
 ~GeomPrimitivePipelineReader() {
   if (_vertices_reader != (GeomVertexArrayDataPipelineReader *)NULL) {
   if (_vertices_reader != (GeomVertexArrayDataPipelineReader *)NULL) {
-    nassertv(_vertices_reader->get_object() == _cdata->_vertices);
     delete _vertices_reader;
     delete _vertices_reader;
   }
   }
 #ifdef _DEBUG
 #ifdef _DEBUG
@@ -596,7 +594,7 @@ get_index_type() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool GeomPrimitivePipelineReader::
 INLINE bool GeomPrimitivePipelineReader::
 is_indexed() const {
 is_indexed() const {
-  return (_cdata->_vertices != (GeomVertexArrayData *)NULL);
+  return (!_cdata->_vertices.is_null());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -609,7 +607,7 @@ get_num_vertices() const {
   if (_cdata->_num_vertices != -1) {
   if (_cdata->_num_vertices != -1) {
     return _cdata->_num_vertices;
     return _cdata->_num_vertices;
   } else {
   } else {
-    nassertr(_cdata->_vertices != (GeomVertexArrayData *)NULL, 0);
+    nassertr(!_cdata->_vertices.is_null(), 0);
     return _vertices_reader->get_num_rows();
     return _vertices_reader->get_num_rows();
   }
   }
 }
 }
@@ -665,7 +663,7 @@ get_modified() const {
 INLINE int GeomPrimitivePipelineReader::
 INLINE int GeomPrimitivePipelineReader::
 get_index_stride() const {
 get_index_stride() const {
   nassertr(is_indexed(), 0);
   nassertr(is_indexed(), 0);
-  return _cdata->_vertices->get_array_format()->get_stride();
+  return _cdata->_vertices.get_read_pointer()->get_array_format()->get_stride();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -703,11 +701,11 @@ get_ends() const {
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const GeomVertexArrayData *GeomPrimitivePipelineReader::
+INLINE CPT(GeomVertexArrayData) GeomPrimitivePipelineReader::
 get_mins() const {
 get_mins() const {
   nassertr(is_indexed(), NULL);
   nassertr(is_indexed(), NULL);
   nassertr(_cdata->_got_minmax, NULL);
   nassertr(_cdata->_got_minmax, NULL);
-  return _cdata->_mins;
+  return _cdata->_mins.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -715,11 +713,11 @@ get_mins() const {
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const GeomVertexArrayData *GeomPrimitivePipelineReader::
+INLINE CPT(GeomVertexArrayData) GeomPrimitivePipelineReader::
 get_maxs() const {
 get_maxs() const {
   nassertr(is_indexed(), NULL);
   nassertr(is_indexed(), NULL);
   nassertr(_cdata->_got_minmax, NULL);
   nassertr(_cdata->_got_minmax, NULL);
-  return _cdata->_maxs;
+  return _cdata->_maxs.get_read_pointer();
 }
 }
 
 
 INLINE ostream &
 INLINE ostream &

+ 56 - 50
panda/src/gobj/geomPrimitive.cxx

@@ -49,6 +49,16 @@ GeomPrimitive::
 GeomPrimitive() {
 GeomPrimitive() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::make_cow_copy
+//       Access: Protected, Virtual
+//  Description: Required to implement CopyOnWriteObject.
+////////////////////////////////////////////////////////////////////
+PT(CopyOnWriteObject) GeomPrimitive::
+make_cow_copy() {
+  return make_copy().p();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::Constructor
 //     Function: GeomPrimitive::Constructor
 //       Access: Published
 //       Access: Published
@@ -67,7 +77,7 @@ GeomPrimitive(GeomPrimitive::UsageHint usage_hint) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GeomPrimitive::
 GeomPrimitive::
 GeomPrimitive(const GeomPrimitive &copy) :
 GeomPrimitive(const GeomPrimitive &copy) :
-  TypedWritableReferenceCount(copy),
+  CopyOnWriteObject(copy),
   _cycler(copy._cycler)
   _cycler(copy._cycler)
 {
 {
 }
 }
@@ -82,7 +92,7 @@ GeomPrimitive(const GeomPrimitive &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 operator = (const GeomPrimitive &copy) {
 operator = (const GeomPrimitive &copy) {
-  TypedWritableReferenceCount::operator = (copy);
+  CopyOnWriteObject::operator = (copy);
   _cycler = copy._cycler;
   _cycler = copy._cycler;
 }
 }
 
 
@@ -127,11 +137,7 @@ set_usage_hint(GeomPrimitive::UsageHint usage_hint) {
   CDWriter cdata(_cycler, true);
   CDWriter cdata(_cycler, true);
   cdata->_usage_hint = usage_hint;
   cdata->_usage_hint = usage_hint;
 
 
-  if (cdata->_vertices != (GeomVertexArrayData *)NULL) {
-    if (cdata->_vertices->get_ref_count() > 1) {
-      cdata->_vertices = new GeomVertexArrayData(*cdata->_vertices);
-    }
-    
+  if (!cdata->_vertices.is_null()) {
     cdata->_modified = Geom::get_next_modified();
     cdata->_modified = Geom::get_next_modified();
     cdata->_usage_hint = usage_hint;
     cdata->_usage_hint = usage_hint;
   }
   }
@@ -189,13 +195,13 @@ add_vertex(int vertex) {
       get_num_vertices() == get_primitive_end(num_primitives - 1)) {
       get_num_vertices() == get_primitive_end(num_primitives - 1)) {
     // If we are beginning a new primitive, give the derived class a
     // If we are beginning a new primitive, give the derived class a
     // chance to insert some degenerate vertices.
     // chance to insert some degenerate vertices.
-    if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+    if (cdata->_vertices.is_null()) {
       do_make_indexed(cdata);
       do_make_indexed(cdata);
     }
     }
-    append_unused_vertices(cdata->_vertices, vertex);
+    append_unused_vertices(cdata->_vertices.get_write_pointer(), vertex);
   }
   }
 
 
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+  if (cdata->_vertices.is_null()) {
     // The nonindexed case.  We can keep the primitive nonindexed only
     // The nonindexed case.  We can keep the primitive nonindexed only
     // if the vertex number happens to be the next available vertex.
     // if the vertex number happens to be the next available vertex.
     nassertv(cdata->_num_vertices != -1);
     nassertv(cdata->_num_vertices != -1);
@@ -217,8 +223,9 @@ add_vertex(int vertex) {
     do_make_indexed(cdata);
     do_make_indexed(cdata);
   }
   }
 
 
-  GeomVertexWriter index(cdata->_vertices, 0);
-  index.set_row(cdata->_vertices->get_num_rows());
+  PT(GeomVertexArrayData) array_obj = cdata->_vertices.get_write_pointer();
+  GeomVertexWriter index(array_obj, 0);
+  index.set_row(array_obj->get_num_rows());
 
 
   index.add_data1i(vertex);
   index.add_data1i(vertex);
 
 
@@ -252,13 +259,13 @@ add_consecutive_vertices(int start, int num_vertices) {
       get_num_vertices() == get_primitive_end(num_primitives - 1)) {
       get_num_vertices() == get_primitive_end(num_primitives - 1)) {
     // If we are beginning a new primitive, give the derived class a
     // If we are beginning a new primitive, give the derived class a
     // chance to insert some degenerate vertices.
     // chance to insert some degenerate vertices.
-    if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+    if (cdata->_vertices.is_null()) {
       do_make_indexed(cdata);
       do_make_indexed(cdata);
     }
     }
-    append_unused_vertices(cdata->_vertices, start);
+    append_unused_vertices(cdata->_vertices.get_write_pointer(), start);
   }
   }
 
 
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+  if (cdata->_vertices.is_null()) {
     // The nonindexed case.  We can keep the primitive nonindexed only
     // The nonindexed case.  We can keep the primitive nonindexed only
     // if the vertex number happens to be the next available vertex.
     // if the vertex number happens to be the next available vertex.
     nassertv(cdata->_num_vertices != -1);
     nassertv(cdata->_num_vertices != -1);
@@ -280,8 +287,9 @@ add_consecutive_vertices(int start, int num_vertices) {
     do_make_indexed(cdata);
     do_make_indexed(cdata);
   }
   }
 
 
-  GeomVertexWriter index(cdata->_vertices, 0);
-  index.set_row(cdata->_vertices->get_num_rows());
+  PT(GeomVertexArrayData) array_obj = cdata->_vertices.get_write_pointer();
+  GeomVertexWriter index(array_obj, 0);
+  index.set_row(array_obj->get_num_rows());
 
 
   for (int v = start; v <= end; ++v) {
   for (int v = start; v <= end; ++v) {
     index.add_data1i(v);
     index.add_data1i(v);
@@ -781,8 +789,8 @@ int GeomPrimitive::
 get_num_bytes() const {
 get_num_bytes() const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
   int num_bytes = cdata->_ends.size() * sizeof(int) + sizeof(GeomPrimitive);
   int num_bytes = cdata->_ends.size() * sizeof(int) + sizeof(GeomPrimitive);
-  if (cdata->_vertices != (GeomVertexArrayData *)NULL) {
-    num_bytes += cdata->_vertices->get_data_size_bytes();
+  if (!cdata->_vertices.is_null()) {
+    num_bytes += cdata->_vertices.get_read_pointer()->get_data_size_bytes();
   }
   }
 
 
   return num_bytes;
   return num_bytes;
@@ -859,10 +867,10 @@ write(ostream &out, int indent_level) const {
 //               don't mind it blowing away other changes you might
 //               don't mind it blowing away other changes you might
 //               have recently made in an upstream thread.
 //               have recently made in an upstream thread.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-GeomVertexArrayData *GeomPrimitive::
+PT(GeomVertexArrayData) GeomPrimitive::
 modify_vertices(int num_vertices) {
 modify_vertices(int num_vertices) {
   CDWriter cdata(_cycler, true);
   CDWriter cdata(_cycler, true);
-  GeomVertexArrayData *vertices = do_modify_vertices(cdata);
+  PT(GeomVertexArrayData) vertices = do_modify_vertices(cdata);
   cdata->_num_vertices = num_vertices;
   cdata->_num_vertices = num_vertices;
   return vertices;
   return vertices;
 }
 }
@@ -1223,7 +1231,7 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
 
 
   CDReader cdata(_cycler, current_thread);
   CDReader cdata(_cycler, current_thread);
 
 
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+  if (cdata->_vertices.is_null()) {
     // Nonindexed case.
     // Nonindexed case.
     nassertv(cdata->_num_vertices != -1);
     nassertv(cdata->_num_vertices != -1);
     if (got_mat) {
     if (got_mat) {
@@ -1266,7 +1274,7 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
 
 
   } else {
   } else {
     // Indexed case.
     // Indexed case.
-    GeomVertexReader index(cdata->_vertices, 0, current_thread);
+    GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread);
     int num_vertices = get_num_vertices();
     int num_vertices = get_num_vertices();
 
 
     if (got_mat) {
     if (got_mat) {
@@ -1375,7 +1383,7 @@ append_unused_vertices(GeomVertexArrayData *, int) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 recompute_minmax(GeomPrimitive::CData *cdata) {
 recompute_minmax(GeomPrimitive::CData *cdata) {
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+  if (cdata->_vertices.is_null()) {
     // In the nonindexed case, we don't need to do much (the
     // In the nonindexed case, we don't need to do much (the
     // minmax is trivial).
     // minmax is trivial).
     nassertv(cdata->_num_vertices != -1);
     nassertv(cdata->_num_vertices != -1);
@@ -1394,13 +1402,13 @@ recompute_minmax(GeomPrimitive::CData *cdata) {
   } else if (get_num_vertices_per_primitive() == 0) {
   } else if (get_num_vertices_per_primitive() == 0) {
     // This is a complex primitive type like a triangle strip; compute
     // This is a complex primitive type like a triangle strip; compute
     // the minmax of each primitive (as well as the overall minmax).
     // the minmax of each primitive (as well as the overall minmax).
-    GeomVertexReader index(cdata->_vertices, 0);
+    GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
 
 
     cdata->_mins = make_index_data();
     cdata->_mins = make_index_data();
     cdata->_maxs = make_index_data();
     cdata->_maxs = make_index_data();
 
 
-    GeomVertexWriter mins(cdata->_mins, 0);
-    GeomVertexWriter maxs(cdata->_maxs, 0);
+    GeomVertexWriter mins(cdata->_mins.get_write_pointer(), 0);
+    GeomVertexWriter maxs(cdata->_maxs.get_write_pointer(), 0);
 
 
     int pi = 0;
     int pi = 0;
 
 
@@ -1431,12 +1439,12 @@ recompute_minmax(GeomPrimitive::CData *cdata) {
     }
     }
     mins.add_data1i(min_prim);
     mins.add_data1i(min_prim);
     maxs.add_data1i(max_prim);
     maxs.add_data1i(max_prim);
-    nassertv(cdata->_mins->get_num_rows() == (int)cdata->_ends.size());
+    nassertv(mins.get_array_data()->get_num_rows() == (int)cdata->_ends.size());
 
 
   } else {
   } else {
     // This is a simple primitive type like a triangle; just compute
     // This is a simple primitive type like a triangle; just compute
     // the overall minmax.
     // the overall minmax.
-    GeomVertexReader index(cdata->_vertices, 0);
+    GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
 
 
     cdata->_mins.clear();
     cdata->_mins.clear();
     cdata->_maxs.clear();
     cdata->_maxs.clear();
@@ -1464,10 +1472,10 @@ recompute_minmax(GeomPrimitive::CData *cdata) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 do_make_indexed(CData *cdata) {
 do_make_indexed(CData *cdata) {
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+  if (cdata->_vertices.is_null()) {
     nassertv(cdata->_num_vertices != -1);
     nassertv(cdata->_num_vertices != -1);
     cdata->_vertices = make_index_data();
     cdata->_vertices = make_index_data();
-    GeomVertexWriter index(cdata->_vertices, 0);
+    GeomVertexWriter index(cdata->_vertices.get_write_pointer(), 0);
     for (int i = 0; i < cdata->_num_vertices; ++i) {
     for (int i = 0; i < cdata->_num_vertices; ++i) {
       index.add_data1i(i + cdata->_first_vertex);
       index.add_data1i(i + cdata->_first_vertex);
     }
     }
@@ -1516,14 +1524,15 @@ void GeomPrimitive::
 do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) {
 do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) {
   cdata->_index_type = index_type;
   cdata->_index_type = index_type;
 
 
-  if (cdata->_vertices != (GeomVertexArrayData *)NULL) {
+  if (!cdata->_vertices.is_null()) {
     CPT(GeomVertexArrayFormat) new_format = get_index_format();
     CPT(GeomVertexArrayFormat) new_format = get_index_format();
     
     
-    if (cdata->_vertices->get_array_format() != new_format) {
+    CPT(GeomVertexArrayData) array_obj = cdata->_vertices.get_read_pointer();
+    if (array_obj->get_array_format() != new_format) {
       PT(GeomVertexArrayData) new_vertices = make_index_data();
       PT(GeomVertexArrayData) new_vertices = make_index_data();
-      new_vertices->set_num_rows(cdata->_vertices->get_num_rows());
+      new_vertices->set_num_rows(array_obj->get_num_rows());
 
 
-      GeomVertexReader from(cdata->_vertices, 0);
+      GeomVertexReader from(array_obj, 0);
       GeomVertexWriter to(new_vertices, 0);
       GeomVertexWriter to(new_vertices, 0);
       
       
       while (!from.is_at_end()) {
       while (!from.is_at_end()) {
@@ -1540,19 +1549,17 @@ do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) {
 //       Access: Private
 //       Access: Private
 //  Description: The private implementation of modify_vertices().
 //  Description: The private implementation of modify_vertices().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-GeomVertexArrayData *GeomPrimitive::
+PT(GeomVertexArrayData) GeomPrimitive::
 do_modify_vertices(GeomPrimitive::CData *cdata) {
 do_modify_vertices(GeomPrimitive::CData *cdata) {
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+  if (cdata->_vertices.is_null()) {
     do_make_indexed(cdata);
     do_make_indexed(cdata);
   }
   }
 
 
-  if (cdata->_vertices->get_ref_count() > 1) {
-    cdata->_vertices = new GeomVertexArrayData(*cdata->_vertices);
-  }
+  PT(GeomVertexArrayData) vertices = cdata->_vertices.get_write_pointer();
 
 
   cdata->_modified = Geom::get_next_modified();
   cdata->_modified = Geom::get_next_modified();
   cdata->_got_minmax = false;
   cdata->_got_minmax = false;
-  return cdata->_vertices;
+  return vertices;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1622,7 +1629,7 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
   dg.add_uint8(_index_type);
   dg.add_uint8(_index_type);
   dg.add_uint8(_usage_hint);
   dg.add_uint8(_usage_hint);
 
 
-  manager->write_pointer(dg, _vertices);
+  manager->write_pointer(dg, _vertices.get_read_pointer());
   WRITE_PTA(manager, dg, IPD_int::write_datagram, _ends);
   WRITE_PTA(manager, dg, IPD_int::write_datagram, _ends);
 }
 }
 
 
@@ -1639,8 +1646,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 
 
   _vertices = DCAST(GeomVertexArrayData, p_list[pi++]);    
   _vertices = DCAST(GeomVertexArrayData, p_list[pi++]);    
 
 
-  if (manager->get_file_minor_ver() < 6 &&
-      _vertices != (GeomVertexArrayData *)NULL) {
+  if (manager->get_file_minor_ver() < 6 && !_vertices.is_null()) {
     // Older bam files might have a meaningless number in
     // Older bam files might have a meaningless number in
     // _num_vertices if the primitive is indexed.  Nowadays, this
     // _num_vertices if the primitive is indexed.  Nowadays, this
     // number is always considered meaningful unless it is -1.
     // number is always considered meaningful unless it is -1.
@@ -1685,12 +1691,12 @@ check_minmax() const {
     // already have modified the pointer on the object since we
     // already have modified the pointer on the object since we
     // queried it.
     // queried it.
     {
     {
-      GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object)->_cycler, 
+      GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object.p())->_cycler, 
                                           false, _current_thread);
                                           false, _current_thread);
       if (!fresh_cdata->_got_minmax) {
       if (!fresh_cdata->_got_minmax) {
         // The cache is still stale.  We have to do the work of
         // The cache is still stale.  We have to do the work of
         // freshening it.
         // freshening it.
-        ((GeomPrimitive *)_object)->recompute_minmax(fresh_cdata);
+        ((GeomPrimitive *)_object.p())->recompute_minmax(fresh_cdata);
         nassertv(fresh_cdata->_got_minmax);
         nassertv(fresh_cdata->_got_minmax);
       }
       }
 
 
@@ -1715,12 +1721,12 @@ check_minmax() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int GeomPrimitivePipelineReader::
 int GeomPrimitivePipelineReader::
 get_first_vertex() const {
 get_first_vertex() const {
-  if (_cdata->_vertices == (GeomVertexArrayData *)NULL) {
+  if (_cdata->_vertices.is_null()) {
     return _cdata->_first_vertex;
     return _cdata->_first_vertex;
   } else if (_vertices_reader->get_num_rows() == 0) {
   } else if (_vertices_reader->get_num_rows() == 0) {
     return 0;
     return 0;
   } else {
   } else {
-    GeomVertexReader index(_cdata->_vertices, 0);
+    GeomVertexReader index(_cdata->_vertices.get_read_pointer(), 0);
     return index.get_data1i();
     return index.get_data1i();
   }
   }
 }
 }
@@ -1732,11 +1738,11 @@ get_first_vertex() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int GeomPrimitivePipelineReader::
 int GeomPrimitivePipelineReader::
 get_vertex(int i) const {
 get_vertex(int i) const {
-  if (_cdata->_vertices != (GeomVertexArrayData *)NULL) {
+  if (!_cdata->_vertices.is_null()) {
     // The indexed case.
     // The indexed case.
     nassertr(i >= 0 && i < _vertices_reader->get_num_rows(), -1);
     nassertr(i >= 0 && i < _vertices_reader->get_num_rows(), -1);
 
 
-    GeomVertexReader index(_cdata->_vertices, 0);
+    GeomVertexReader index(_cdata->_vertices.get_read_pointer(), 0);
     index.set_row(i);
     index.set_row(i);
     return index.get_data1i();
     return index.get_data1i();
 
 

+ 16 - 15
panda/src/gobj/geomPrimitive.h

@@ -23,7 +23,7 @@
 #include "geomEnums.h"
 #include "geomEnums.h"
 #include "geomVertexArrayData.h"
 #include "geomVertexArrayData.h"
 #include "geomVertexData.h"
 #include "geomVertexData.h"
-#include "typedWritableReferenceCount.h"
+#include "copyOnWriteObject.h"
 #include "luse.h"
 #include "luse.h"
 #include "updateSeq.h"
 #include "updateSeq.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
@@ -64,9 +64,10 @@ class GeomPrimitivePipelineReader;
 //               renders a strip of (n - 2) connected triangles from
 //               renders a strip of (n - 2) connected triangles from
 //               each sequence of n vertices.
 //               each sequence of n vertices.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA GeomPrimitive : public TypedWritableReferenceCount, public GeomEnums {
+class EXPCL_PANDA GeomPrimitive : public CopyOnWriteObject, public GeomEnums {
 protected:
 protected:
   GeomPrimitive();
   GeomPrimitive();
+  virtual PT(CopyOnWriteObject) make_cow_copy();
 
 
 PUBLISHED:
 PUBLISHED:
   GeomPrimitive(UsageHint usage_hint);
   GeomPrimitive(UsageHint usage_hint);
@@ -151,8 +152,8 @@ public:
   // recommended that application-level code use the above interfaces
   // recommended that application-level code use the above interfaces
   // instead.
   // instead.
 
 
-  INLINE const GeomVertexArrayData *get_vertices() const;
-  GeomVertexArrayData *modify_vertices(int num_vertices = -1);
+  INLINE CPT(GeomVertexArrayData) get_vertices() const;
+  PT(GeomVertexArrayData) modify_vertices(int num_vertices = -1);
   void set_vertices(const GeomVertexArrayData *vertices, int num_vertices = -1);
   void set_vertices(const GeomVertexArrayData *vertices, int num_vertices = -1);
   void set_nonindexed_vertices(int first_vertex, int num_vertices);
   void set_nonindexed_vertices(int first_vertex, int num_vertices);
 
 
@@ -163,8 +164,8 @@ public:
   PTA_int modify_ends();
   PTA_int modify_ends();
   void set_ends(CPTA_int ends);
   void set_ends(CPTA_int ends);
 
 
-  INLINE const GeomVertexArrayData *get_mins() const;
-  INLINE const GeomVertexArrayData *get_maxs() const;
+  INLINE CPT(GeomVertexArrayData) get_mins() const;
+  INLINE CPT(GeomVertexArrayData) get_maxs() const;
 
 
   void set_minmax(int min_vertex, int max_vertex,
   void set_minmax(int min_vertex, int max_vertex,
                   GeomVertexArrayData *mins, GeomVertexArrayData *maxs);
                   GeomVertexArrayData *mins, GeomVertexArrayData *maxs);
@@ -214,7 +215,7 @@ private:
   void do_make_indexed(CData *cdata);
   void do_make_indexed(CData *cdata);
   void consider_elevate_index_type(CData *cdata, int vertex);
   void consider_elevate_index_type(CData *cdata, int vertex);
   void do_set_index_type(CData *cdata, NumericType index_type);
   void do_set_index_type(CData *cdata, NumericType index_type);
-  GeomVertexArrayData *do_modify_vertices(CData *cdata);
+  PT(GeomVertexArrayData) do_modify_vertices(CData *cdata);
 
 
 private:
 private:
   // A GeomPrimitive keeps a list (actually, a map) of all the
   // A GeomPrimitive keeps a list (actually, a map) of all the
@@ -245,10 +246,10 @@ private:
     int _num_vertices;
     int _num_vertices;
     NumericType _index_type;
     NumericType _index_type;
     UsageHint _usage_hint;
     UsageHint _usage_hint;
-    PT(GeomVertexArrayData) _vertices;
+    COWPT(GeomVertexArrayData) _vertices;
     PTA_int _ends;
     PTA_int _ends;
-    PT(GeomVertexArrayData) _mins;
-    PT(GeomVertexArrayData) _maxs;
+    COWPT(GeomVertexArrayData) _mins;
+    COWPT(GeomVertexArrayData) _maxs;
     UpdateSeq _modified;
     UpdateSeq _modified;
     
     
     bool _got_minmax;
     bool _got_minmax;
@@ -292,9 +293,9 @@ public:
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
-    TypedWritableReferenceCount::init_type();
+    CopyOnWriteObject::init_type();
     register_type(_type_handle, "GeomPrimitive",
     register_type(_type_handle, "GeomPrimitive",
-                  TypedWritableReferenceCount::get_class_type());
+                  CopyOnWriteObject::get_class_type());
     CData::init_type();
     CData::init_type();
   }
   }
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
@@ -348,11 +349,11 @@ public:
   INLINE const GeomVertexArrayDataPipelineReader *get_vertices_reader() const;
   INLINE const GeomVertexArrayDataPipelineReader *get_vertices_reader() const;
   INLINE CPTA_uchar get_data() const;
   INLINE CPTA_uchar get_data() const;
   INLINE CPTA_int get_ends() const;
   INLINE CPTA_int get_ends() const;
-  INLINE const GeomVertexArrayData *get_mins() const;
-  INLINE const GeomVertexArrayData *get_maxs() const;
+  INLINE CPT(GeomVertexArrayData) get_mins() const;
+  INLINE CPT(GeomVertexArrayData) get_maxs() const;
   
   
 private:
 private:
-  const GeomPrimitive *_object;
+  CPT(GeomPrimitive) _object;
   Thread *_current_thread;
   Thread *_current_thread;
   const GeomPrimitive::CData *_cdata;
   const GeomPrimitive::CData *_cdata;
 
 

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

@@ -42,6 +42,16 @@ GeomVertexArrayData() {
   _endian_reversed = false;
   _endian_reversed = false;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayData::make_cow_copy
+//       Access: Protected, Virtual
+//  Description: Required to implement CopyOnWriteObject.
+////////////////////////////////////////////////////////////////////
+PT(CopyOnWriteObject) GeomVertexArrayData::
+make_cow_copy() {
+  return new GeomVertexArrayData(*this);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexArrayData::Constructor
 //     Function: GeomVertexArrayData::Constructor
 //       Access: Published
 //       Access: Published
@@ -69,7 +79,7 @@ GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GeomVertexArrayData::
 GeomVertexArrayData::
 GeomVertexArrayData(const GeomVertexArrayData &copy) :
 GeomVertexArrayData(const GeomVertexArrayData &copy) :
-  TypedWritableReferenceCount(copy),
+  CopyOnWriteObject(copy),
   _array_format(copy._array_format),
   _array_format(copy._array_format),
   _cycler(copy._cycler)
   _cycler(copy._cycler)
 {
 {
@@ -87,7 +97,7 @@ GeomVertexArrayData(const GeomVertexArrayData &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexArrayData::
 void GeomVertexArrayData::
 operator = (const GeomVertexArrayData &copy) {
 operator = (const GeomVertexArrayData &copy) {
-  TypedWritableReferenceCount::operator = (copy);
+  CopyOnWriteObject::operator = (copy);
   _array_format = copy._array_format;
   _array_format = copy._array_format;
   _cycler = copy._cycler;
   _cycler = copy._cycler;
 
 
@@ -334,7 +344,7 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexArrayData::
 void GeomVertexArrayData::
 write_datagram(BamWriter *manager, Datagram &dg) {
 write_datagram(BamWriter *manager, Datagram &dg) {
-  TypedWritableReferenceCount::write_datagram(manager, dg);
+  CopyOnWriteObject::write_datagram(manager, dg);
 
 
   manager->write_pointer(dg, _array_format);
   manager->write_pointer(dg, _array_format);
   manager->write_cdata(dg, _cycler, this);
   manager->write_cdata(dg, _cycler, this);
@@ -402,7 +412,7 @@ read_raw_data(BamReader *manager, DatagramIterator &scan) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int GeomVertexArrayData::
 int GeomVertexArrayData::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
+  int pi = CopyOnWriteObject::complete_pointers(p_list, manager);
 
 
   _array_format = DCAST(GeomVertexArrayFormat, p_list[pi++]);
   _array_format = DCAST(GeomVertexArrayFormat, p_list[pi++]);
 
 
@@ -473,7 +483,7 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexArrayData::
 void GeomVertexArrayData::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
-  TypedWritableReferenceCount::fillin(scan, manager);
+  CopyOnWriteObject::fillin(scan, manager);
 
 
   manager->read_pointer(scan);
   manager->read_pointer(scan);
   manager->read_cdata(scan, _cycler, this);
   manager->read_cdata(scan, _cycler, this);

+ 8 - 5
panda/src/gobj/geomVertexArrayData.h

@@ -20,7 +20,7 @@
 #define GEOMVERTEXARRAYDATA_H
 #define GEOMVERTEXARRAYDATA_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "typedWritableReferenceCount.h"
+#include "copyOnWriteObject.h"
 #include "geomVertexArrayFormat.h"
 #include "geomVertexArrayFormat.h"
 #include "geomEnums.h"
 #include "geomEnums.h"
 #include "pta_uchar.h"
 #include "pta_uchar.h"
@@ -58,10 +58,13 @@ class GraphicsStateGuardianBase;
 //               GeomVertexReader/Writer/Rewriter for high-level tools
 //               GeomVertexReader/Writer/Rewriter for high-level tools
 //               to manipulate the actual vertex data.
 //               to manipulate the actual vertex data.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA GeomVertexArrayData : public TypedWritableReferenceCount, public GeomEnums {
+class EXPCL_PANDA GeomVertexArrayData : public CopyOnWriteObject, public GeomEnums {
 private:
 private:
   GeomVertexArrayData();
   GeomVertexArrayData();
 
 
+protected:
+  virtual PT(CopyOnWriteObject) make_cow_copy();
+
 PUBLISHED:
 PUBLISHED:
   GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
   GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
                       UsageHint usage_hint);
                       UsageHint usage_hint);
@@ -179,9 +182,9 @@ public:
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
-    TypedWritableReferenceCount::init_type();
+    CopyOnWriteObject::init_type();
     register_type(_type_handle, "GeomVertexArrayData",
     register_type(_type_handle, "GeomVertexArrayData",
-                  TypedWritableReferenceCount::get_class_type());
+                  CopyOnWriteObject::get_class_type());
     CData::init_type();
     CData::init_type();
   }
   }
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
@@ -225,7 +228,7 @@ public:
   INLINE UpdateSeq get_modified() const;
   INLINE UpdateSeq get_modified() const;
 
 
 protected:
 protected:
-  GeomVertexArrayData *_object;
+  PT(GeomVertexArrayData) _object;
   Thread *_current_thread;
   Thread *_current_thread;
   GeomVertexArrayData::CData *_cdata;
   GeomVertexArrayData::CData *_cdata;
 };
 };

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

@@ -167,11 +167,11 @@ get_num_arrays() const {
 //               indicated array, for application code to directly
 //               indicated array, for application code to directly
 //               examine (but not modify) the underlying vertex data.
 //               examine (but not modify) the underlying vertex data.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const GeomVertexArrayData *GeomVertexData::
+INLINE CPT(GeomVertexArrayData) GeomVertexData::
 get_array(int i) const {
 get_array(int i) const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
   nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL);
   nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL);
-  return cdata->_arrays[i];
+  return cdata->_arrays[i].get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -188,7 +188,7 @@ get_array(int i) const {
 //               don't mind it blowing away other changes you might
 //               don't mind it blowing away other changes you might
 //               have recently made in an upstream thread.
 //               have recently made in an upstream thread.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE GeomVertexArrayData *GeomVertexData::
+INLINE PT(GeomVertexArrayData) GeomVertexData::
 modify_array(int i) {
 modify_array(int i) {
   GeomVertexDataPipelineWriter writer(this, true, Thread::get_current_thread());
   GeomVertexDataPipelineWriter writer(this, true, Thread::get_current_thread());
   return writer.modify_array(i);
   return writer.modify_array(i);
@@ -260,10 +260,10 @@ clear_transform_table() {
 //               have a TransformBlendTable assigned (which implies
 //               have a TransformBlendTable assigned (which implies
 //               the vertices will not be animated by the CPU).
 //               the vertices will not be animated by the CPU).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const TransformBlendTable *GeomVertexData::
+INLINE CPT(TransformBlendTable) GeomVertexData::
 get_transform_blend_table() const {
 get_transform_blend_table() const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
-  return cdata->_transform_blend_table;
+  return cdata->_transform_blend_table.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -589,10 +589,10 @@ get_num_arrays() const {
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const GeomVertexArrayData *GeomVertexDataPipelineBase::
+INLINE CPT(GeomVertexArrayData) GeomVertexDataPipelineBase::
 get_array(int i) const {
 get_array(int i) const {
   nassertr(i >= 0 && i < (int)_cdata->_arrays.size(), NULL);
   nassertr(i >= 0 && i < (int)_cdata->_arrays.size(), NULL);
-  return _cdata->_arrays[i];
+  return _cdata->_arrays[i].get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -610,9 +610,9 @@ get_transform_table() const {
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const TransformBlendTable *GeomVertexDataPipelineBase::
+INLINE CPT(TransformBlendTable) GeomVertexDataPipelineBase::
 get_transform_blend_table() const {
 get_transform_blend_table() const {
-  return _cdata->_transform_blend_table;
+  return _cdata->_transform_blend_table.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 69 - 78
panda/src/gobj/geomVertexData.cxx

@@ -53,6 +53,16 @@ GeomVertexData() :
 {
 {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexData::make_cow_copy
+//       Access: Protected, Virtual
+//  Description: Required to implement CopyOnWriteObject.
+////////////////////////////////////////////////////////////////////
+PT(CopyOnWriteObject) GeomVertexData::
+make_cow_copy() {
+  return new GeomVertexData(*this);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::Constructor
 //     Function: GeomVertexData::Constructor
 //       Access: Published
 //       Access: Published
@@ -79,7 +89,7 @@ GeomVertexData(const string &name,
     for (int i = 0; i < num_arrays; i++) {
     for (int i = 0; i < num_arrays; i++) {
       PT(GeomVertexArrayData) array = new GeomVertexArrayData
       PT(GeomVertexArrayData) array = new GeomVertexArrayData
         (format->get_array(i), usage_hint);
         (format->get_array(i), usage_hint);
-      cdata->_arrays.push_back(array);
+      cdata->_arrays.push_back(array.p());
     }
     }
   }
   }
   CLOSE_ITERATE_ALL_STAGES(_cycler);
   CLOSE_ITERATE_ALL_STAGES(_cycler);
@@ -92,7 +102,7 @@ GeomVertexData(const string &name,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GeomVertexData::
 GeomVertexData::
 GeomVertexData(const GeomVertexData &copy) :
 GeomVertexData(const GeomVertexData &copy) :
-  TypedWritableReferenceCount(copy),
+  CopyOnWriteObject(copy),
   _name(copy._name),
   _name(copy._name),
   _cycler(copy._cycler),
   _cycler(copy._cycler),
   _char_pcollector(copy._char_pcollector),
   _char_pcollector(copy._char_pcollector),
@@ -119,7 +129,7 @@ GeomVertexData(const GeomVertexData &copy) :
 GeomVertexData::
 GeomVertexData::
 GeomVertexData(const GeomVertexData &copy, 
 GeomVertexData(const GeomVertexData &copy, 
                const GeomVertexFormat *format) :
                const GeomVertexFormat *format) :
-  TypedWritableReferenceCount(copy),
+  CopyOnWriteObject(copy),
   _name(copy._name),
   _name(copy._name),
   _cycler(copy._cycler),
   _cycler(copy._cycler),
   _char_pcollector(copy._char_pcollector),
   _char_pcollector(copy._char_pcollector),
@@ -139,7 +149,7 @@ GeomVertexData(const GeomVertexData &copy,
     for (int i = 0; i < num_arrays; i++) {
     for (int i = 0; i < num_arrays; i++) {
       PT(GeomVertexArrayData) array = new GeomVertexArrayData
       PT(GeomVertexArrayData) array = new GeomVertexArrayData
         (format->get_array(i), usage_hint);
         (format->get_array(i), usage_hint);
-      cdata->_arrays.push_back(array);
+      cdata->_arrays.push_back(array.p());
     }
     }
 
 
     // It's important that we *not* copy the animated_vertices pointer.
     // It's important that we *not* copy the animated_vertices pointer.
@@ -159,7 +169,7 @@ GeomVertexData(const GeomVertexData &copy,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::
 void GeomVertexData::
 operator = (const GeomVertexData &copy) {
 operator = (const GeomVertexData &copy) {
-  TypedWritableReferenceCount::operator = (copy);
+  CopyOnWriteObject::operator = (copy);
 
 
   clear_cache();
   clear_cache();
 
 
@@ -222,10 +232,8 @@ set_usage_hint(GeomVertexData::UsageHint usage_hint) {
   for (ai = cdata->_arrays.begin();
   for (ai = cdata->_arrays.begin();
        ai != cdata->_arrays.end();
        ai != cdata->_arrays.end();
        ++ai) {
        ++ai) {
-    if ((*ai)->get_ref_count() > 1) {
-      (*ai) = new GeomVertexArrayData(*(*ai));
-    }
-    (*ai)->set_usage_hint(usage_hint);
+    PT(GeomVertexArrayData) array_obj = (*ai).get_write_pointer();
+    array_obj->set_usage_hint(usage_hint);
   }
   }
   clear_cache_stage();
   clear_cache_stage();
   cdata->_modified = Geom::get_next_modified();
   cdata->_modified = Geom::get_next_modified();
@@ -270,7 +278,7 @@ set_format(const GeomVertexFormat *format) {
   for (int i = 0; i < num_arrays; i++) {
   for (int i = 0; i < num_arrays; i++) {
     PT(GeomVertexArrayData) array = new GeomVertexArrayData
     PT(GeomVertexArrayData) array = new GeomVertexArrayData
       (cdataw->_format->get_array(i), usage_hint);
       (cdataw->_format->get_array(i), usage_hint);
-    cdataw->_arrays.push_back(array);
+    cdataw->_arrays.push_back(array.p());
   }
   }
 
 
   // Now copy the original data back in.  This will automatically
   // Now copy the original data back in.  This will automatically
@@ -303,10 +311,8 @@ clear_rows() {
   for (ai = cdata->_arrays.begin();
   for (ai = cdata->_arrays.begin();
        ai != cdata->_arrays.end();
        ai != cdata->_arrays.end();
        ++ai) {
        ++ai) {
-    if ((*ai)->get_ref_count() > 1) {
-      (*ai) = new GeomVertexArrayData(*(*ai));
-    }
-    (*ai)->clear_rows();
+    PT(GeomVertexArrayData) array_obj = (*ai).get_write_pointer();
+    array_obj->clear_rows();
   }
   }
   clear_cache_stage();
   clear_cache_stage();
   cdata->_modified = Geom::get_next_modified();
   cdata->_modified = Geom::get_next_modified();
@@ -350,21 +356,15 @@ set_transform_table(const TransformTable *table) {
 //               don't mind it blowing away other changes you might
 //               don't mind it blowing away other changes you might
 //               have recently made in an upstream thread.
 //               have recently made in an upstream thread.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-TransformBlendTable *GeomVertexData::
+PT(TransformBlendTable) GeomVertexData::
 modify_transform_blend_table() {
 modify_transform_blend_table() {
-  // Perform copy-on-write: if the reference count on the table is
-  // greater than 1, assume some other GeomVertexData has the same
-  // pointer, so make a copy of it first.
   CDWriter cdata(_cycler, true);
   CDWriter cdata(_cycler, true);
 
 
-  if (cdata->_transform_blend_table->get_ref_count() > 1) {
-    cdata->_transform_blend_table = new TransformBlendTable(*cdata->_transform_blend_table);
-  }
   clear_cache_stage();
   clear_cache_stage();
   cdata->_modified = Geom::get_next_modified();
   cdata->_modified = Geom::get_next_modified();
   cdata->_animated_vertices_modified = UpdateSeq();
   cdata->_animated_vertices_modified = UpdateSeq();
 
 
-  return cdata->_transform_blend_table;
+  return cdata->_transform_blend_table.get_write_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -490,7 +490,8 @@ copy_from(const GeomVertexData *source, bool keep_data_objects,
 
 
   // Now go back through and copy any data that's left over.
   // Now go back through and copy any data that's left over.
   for (source_i = 0; source_i < num_arrays; ++source_i) {
   for (source_i = 0; source_i < num_arrays; ++source_i) {
-    CPTA_uchar array_data = source->get_array(source_i)->get_data();
+    CPT(GeomVertexArrayData) array_obj = source->get_array(source_i);
+    CPTA_uchar array_data = array_obj->get_data();
     const GeomVertexArrayFormat *source_array_format = source_format->get_array(source_i);
     const GeomVertexArrayFormat *source_array_format = source_format->get_array(source_i);
     int num_columns = source_array_format->get_num_columns();
     int num_columns = source_array_format->get_num_columns();
     for (int di = 0; di < num_columns; ++di) {
     for (int di = 0; di < num_columns; ++di) {
@@ -507,7 +508,8 @@ copy_from(const GeomVertexData *source, bool keep_data_objects,
 
 
         if (dest_column->is_bytewise_equivalent(*source_column)) {
         if (dest_column->is_bytewise_equivalent(*source_column)) {
           // We can do a quick bytewise copy.
           // We can do a quick bytewise copy.
-          PTA_uchar dest_array_data = modify_array(dest_i)->modify_data();
+          PT(GeomVertexArrayData) dest_array_obj = modify_array(dest_i);
+          PTA_uchar dest_array_data = dest_array_obj->modify_data();
 
 
           bytewise_copy(dest_array_data + dest_column->get_start(), 
           bytewise_copy(dest_array_data + dest_column->get_start(), 
                         dest_array_format->get_stride(),
                         dest_array_format->get_stride(),
@@ -517,7 +519,8 @@ copy_from(const GeomVertexData *source, bool keep_data_objects,
         } else if (dest_column->is_packed_argb() && 
         } else if (dest_column->is_packed_argb() && 
                    source_column->is_uint8_rgba()) {
                    source_column->is_uint8_rgba()) {
           // A common special case: OpenGL color to DirectX color.
           // A common special case: OpenGL color to DirectX color.
-          PTA_uchar dest_array_data = modify_array(dest_i)->modify_data();
+          PT(GeomVertexArrayData) dest_array_obj = modify_array(dest_i);
+          PTA_uchar dest_array_data = dest_array_obj->modify_data();
 
 
           uint8_rgba_to_packed_argb
           uint8_rgba_to_packed_argb
             (dest_array_data + dest_column->get_start(), 
             (dest_array_data + dest_column->get_start(), 
@@ -529,7 +532,8 @@ copy_from(const GeomVertexData *source, bool keep_data_objects,
                    source_column->is_packed_argb()) {
                    source_column->is_packed_argb()) {
           // Another common special case: DirectX color to OpenGL
           // Another common special case: DirectX color to OpenGL
           // color.
           // color.
-          PTA_uchar dest_array_data = modify_array(dest_i)->modify_data();
+          PT(GeomVertexArrayData) dest_array_obj = modify_array(dest_i);
+          PTA_uchar dest_array_data = dest_array_obj->modify_data();
 
 
           packed_argb_to_uint8_rgba
           packed_argb_to_uint8_rgba
             (dest_array_data + dest_column->get_start(), 
             (dest_array_data + dest_column->get_start(), 
@@ -649,8 +653,12 @@ copy_row_from(int dest_row, const GeomVertexData *source,
   int num_arrays = source_format->get_num_arrays();
   int num_arrays = source_format->get_num_arrays();
 
 
   for (int i = 0; i < num_arrays; ++i) {
   for (int i = 0; i < num_arrays; ++i) {
-    PTA_uchar dest_array_data = modify_array(i)->modify_data();
-    CPTA_uchar source_array_data = source->get_array(i)->get_data();
+    PT(GeomVertexArrayData) dest_array_obj = modify_array(i);
+    PTA_uchar dest_array_data = dest_array_obj->modify_data();
+
+    CPT(GeomVertexArrayData) source_array_obj = source->get_array(i);
+    CPTA_uchar source_array_data = source_array_obj->get_data();
+
     const GeomVertexArrayFormat *array_format = source_format->get_array(i);
     const GeomVertexArrayFormat *array_format = source_format->get_array(i);
     int stride = array_format->get_stride();
     int stride = array_format->get_stride();
 
 
@@ -914,13 +922,13 @@ animate_vertices(Thread *current_thread) const {
   }
   }
 
 
   UpdateSeq modified;
   UpdateSeq modified;
-  if (cdata->_transform_blend_table != (TransformBlendTable *)NULL) {
+  if (!cdata->_transform_blend_table.is_null()) {
     if (cdata->_slider_table != (SliderTable *)NULL) {
     if (cdata->_slider_table != (SliderTable *)NULL) {
       modified = 
       modified = 
-        max(cdata->_transform_blend_table->get_modified(current_thread),
+        max(cdata->_transform_blend_table.get_read_pointer()->get_modified(current_thread),
             cdata->_slider_table->get_modified(current_thread));
             cdata->_slider_table->get_modified(current_thread));
     } else {
     } else {
-      modified = cdata->_transform_blend_table->get_modified(current_thread);
+      modified = cdata->_transform_blend_table.get_read_pointer()->get_modified(current_thread);
     }
     }
 
 
   } else if (cdata->_slider_table != (SliderTable *)NULL) {
   } else if (cdata->_slider_table != (SliderTable *)NULL) {
@@ -1092,10 +1100,11 @@ write(ostream &out, int indent_level) const {
     indent(out, indent_level) << get_name() << "\n";
     indent(out, indent_level) << get_name() << "\n";
   }
   }
   get_format()->write_with_data(out, indent_level + 2, this);
   get_format()->write_with_data(out, indent_level + 2, this);
-  if (get_transform_blend_table() != (TransformBlendTable *)NULL) {
+  CPT(TransformBlendTable) table = get_transform_blend_table();
+  if (table != (TransformBlendTable *)NULL) {
     indent(out, indent_level)
     indent(out, indent_level)
       << "Transform blend table:\n";
       << "Transform blend table:\n";
-    get_transform_blend_table()->write(out, indent_level + 2);
+    table->write(out, indent_level + 2);
   }
   }
 }
 }
 
 
@@ -1321,7 +1330,7 @@ update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) {
   }
   }
 
 
   // Then apply the transforms.
   // Then apply the transforms.
-  CPT(TransformBlendTable) tb_table = cdata->_transform_blend_table;
+  CPT(TransformBlendTable) tb_table = cdata->_transform_blend_table.get_read_pointer();
   if (tb_table != (TransformBlendTable *)NULL) {
   if (tb_table != (TransformBlendTable *)NULL) {
     PStatTimer timer3(_skinning_pcollector);
     PStatTimer timer3(_skinning_pcollector);
 
 
@@ -1414,7 +1423,7 @@ register_with_read_factory() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::
 void GeomVertexData::
 write_datagram(BamWriter *manager, Datagram &dg) {
 write_datagram(BamWriter *manager, Datagram &dg) {
-  TypedWritableReferenceCount::write_datagram(manager, dg);
+  CopyOnWriteObject::write_datagram(manager, dg);
 
 
   dg.add_string(_name);
   dg.add_string(_name);
   manager->write_cdata(dg, _cycler);
   manager->write_cdata(dg, _cycler);
@@ -1450,7 +1459,7 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int GeomVertexData::
 int GeomVertexData::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
+  int pi = CopyOnWriteObject::complete_pointers(p_list, manager);
   return pi;
   return pi;
 }
 }
 
 
@@ -1486,10 +1495,11 @@ finalize(BamReader *manager) {
     cdata->_format = new_format;
     cdata->_format = new_format;
 
 
     CPT(GeomVertexArrayFormat) new_array_format = new_format->get_array(i);
     CPT(GeomVertexArrayFormat) new_array_format = new_format->get_array(i);
-    nassertv(cdata->_arrays[i]->_array_format->compare_to(*new_array_format) == 0);
+    PT(GeomVertexArrayData) array_obj = cdata->_arrays[i].get_unsafe_pointer();
+    nassertv(array_obj->_array_format->compare_to(*new_array_format) == 0);
 
 
-    manager->change_pointer(cdata->_arrays[i]->_array_format, new_array_format);
-    cdata->_arrays[i]->_array_format = new_array_format;
+    manager->change_pointer(array_obj->_array_format, new_array_format);
+    array_obj->_array_format = new_array_format;
   }
   }
 
 
   if (cdata->_transform_table != (TransformTable *)NULL) {
   if (cdata->_transform_table != (TransformTable *)NULL) {
@@ -1516,7 +1526,7 @@ finalize(BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::
 void GeomVertexData::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
-  TypedWritableReferenceCount::fillin(scan, manager);
+  CopyOnWriteObject::fillin(scan, manager);
 
 
   set_name(scan.get_string());
   set_name(scan.get_string());
   manager->read_cdata(scan, _cycler);
   manager->read_cdata(scan, _cycler);
@@ -1582,11 +1592,11 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
   dg.add_uint16(_arrays.size());
   dg.add_uint16(_arrays.size());
   Arrays::const_iterator ai;
   Arrays::const_iterator ai;
   for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
   for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
-    manager->write_pointer(dg, *ai);
+    manager->write_pointer(dg, (*ai).get_read_pointer());
   }
   }
 
 
   manager->write_pointer(dg, _transform_table);
   manager->write_pointer(dg, _transform_table);
-  manager->write_pointer(dg, _transform_blend_table);
+  manager->write_pointer(dg, _transform_blend_table.get_read_pointer());
   manager->write_pointer(dg, _slider_table);
   manager->write_pointer(dg, _slider_table);
 }
 }
 
 
@@ -1620,7 +1630,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
     // a SparseArray for each of them that reflects the complete
     // a SparseArray for each of them that reflects the complete
     // number of rows in the data.
     // number of rows in the data.
     SparseArray all_rows;
     SparseArray all_rows;
-    all_rows.set_range(0, _arrays[0]->get_num_rows());
+    all_rows.set_range(0, _arrays[0].get_read_pointer()->get_num_rows());
 
 
     if (_slider_table != (SliderTable *)NULL) {
     if (_slider_table != (SliderTable *)NULL) {
       int num_sliders = _slider_table->get_num_sliders();
       int num_sliders = _slider_table->get_num_sliders();
@@ -1628,8 +1638,8 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
         ((SliderTable *)_slider_table.p())->set_slider_rows(i, all_rows);
         ((SliderTable *)_slider_table.p())->set_slider_rows(i, all_rows);
       }
       }
     }
     }
-    if (_transform_blend_table != (TransformBlendTable *)NULL) {
-      _transform_blend_table->set_rows(all_rows);
+    if (!_transform_blend_table.is_null()) {
+      _transform_blend_table.get_unsafe_pointer()->set_rows(all_rows);
     }
     }
   }
   }
 
 
@@ -1671,7 +1681,7 @@ get_num_bytes() const {
 
 
   GeomVertexData::Arrays::const_iterator ai;
   GeomVertexData::Arrays::const_iterator ai;
   for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
   for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
-    num_bytes += (*ai)->get_data_size_bytes();
+    num_bytes += (*ai).get_read_pointer()->get_data_size_bytes();
   }
   }
 
 
   return num_bytes;
   return num_bytes;
@@ -1808,7 +1818,7 @@ make_array_readers() {
   _array_readers.reserve(_cdata->_arrays.size());
   _array_readers.reserve(_cdata->_arrays.size());
   GeomVertexData::Arrays::const_iterator ai;
   GeomVertexData::Arrays::const_iterator ai;
   for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
   for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
-    _array_readers.push_back(new GeomVertexArrayDataPipelineReader(*ai, _current_thread));
+    _array_readers.push_back(new GeomVertexArrayDataPipelineReader((*ai).get_read_pointer(), _current_thread));
   }
   }
 
 
   _got_array_readers = true;
   _got_array_readers = true;
@@ -1868,13 +1878,7 @@ set_num_rows(int n) {
 
 
   for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
   for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
     if (_array_writers[i]->get_num_rows() != n) {
     if (_array_writers[i]->get_num_rows() != n) {
-      // Copy-on-write.
-      if (_cdata->_arrays[i]->get_ref_count() > 1) {
-        delete _array_writers[i];
-        _cdata->_arrays[i] = new GeomVertexArrayData(*_cdata->_arrays[i]);
-        _array_writers[i] = new GeomVertexArrayDataPipelineWriter(_cdata->_arrays[i], _force_to_0, _current_thread);
-      }
-      if (_cdata->_arrays[i]->has_column(InternalName::get_color())) {
+      if (_array_writers[i]->get_object()->has_column(InternalName::get_color())) {
         color_array = i;
         color_array = i;
         orig_color_rows = _array_writers[i]->get_num_rows();
         orig_color_rows = _array_writers[i]->get_num_rows();
       }
       }
@@ -1944,12 +1948,6 @@ unclean_set_num_rows(int n) {
 
 
   for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
   for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
     if (_array_writers[i]->get_num_rows() != n) {
     if (_array_writers[i]->get_num_rows() != n) {
-      // Copy-on-write.
-      if (_cdata->_arrays[i]->get_ref_count() > 1) {
-        delete _array_writers[i];
-        _cdata->_arrays[i] = new GeomVertexArrayData(*_cdata->_arrays[i]);
-        _array_writers[i] = new GeomVertexArrayDataPipelineWriter(_cdata->_arrays[i], _force_to_0, _current_thread);
-      }
       _array_writers[i]->unclean_set_num_rows(n);
       _array_writers[i]->unclean_set_num_rows(n);
       any_changed = true;
       any_changed = true;
     }
     }
@@ -1969,26 +1967,22 @@ unclean_set_num_rows(int n) {
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-GeomVertexArrayData *GeomVertexDataPipelineWriter::
+PT(GeomVertexArrayData) GeomVertexDataPipelineWriter::
 modify_array(int i) {
 modify_array(int i) {
-  // 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.
   nassertr(i >= 0 && i < (int)_cdata->_arrays.size(), NULL);
   nassertr(i >= 0 && i < (int)_cdata->_arrays.size(), NULL);
 
 
-  if (_cdata->_arrays[i]->get_ref_count() > 1) {
-    _cdata->_arrays[i] = new GeomVertexArrayData(*_cdata->_arrays[i]);
+  PT(GeomVertexArrayData) new_data;
+  if (_got_array_writers) {
+    new_data = _array_writers[i]->get_object();
+  } else {
+    new_data = _cdata->_arrays[i].get_write_pointer();
   }
   }
+
   _object->clear_cache_stage();
   _object->clear_cache_stage();
   _cdata->_modified = Geom::get_next_modified();
   _cdata->_modified = Geom::get_next_modified();
   _cdata->_animated_vertices_modified = UpdateSeq();
   _cdata->_animated_vertices_modified = UpdateSeq();
 
 
-  if (_got_array_writers) {
-    delete _array_writers[i];
-    _array_writers[i] = new GeomVertexArrayDataPipelineWriter(_cdata->_arrays[i], _force_to_0, _current_thread);
-  }
-
-  return _cdata->_arrays[i];
+  return new_data;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2006,7 +2000,7 @@ set_array(int i, const GeomVertexArrayData *array) {
 
 
   if (_got_array_writers) {
   if (_got_array_writers) {
     delete _array_writers[i];
     delete _array_writers[i];
-    _array_writers[i] = new GeomVertexArrayDataPipelineWriter(_cdata->_arrays[i], _force_to_0, _current_thread);
+    _array_writers[i] = new GeomVertexArrayDataPipelineWriter(_cdata->_arrays[i].get_write_pointer(), _force_to_0, _current_thread);
   }
   }
 }
 }
 
 
@@ -2022,10 +2016,8 @@ make_array_writers() {
   _array_writers.reserve(_cdata->_arrays.size());
   _array_writers.reserve(_cdata->_arrays.size());
   GeomVertexData::Arrays::iterator ai;
   GeomVertexData::Arrays::iterator ai;
   for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
   for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
-    if ((*ai)->get_ref_count() > 1) {
-      (*ai) = new GeomVertexArrayData(*(*ai));
-    }
-    _array_writers.push_back(new GeomVertexArrayDataPipelineWriter(*ai, _force_to_0, _current_thread));
+    PT(GeomVertexArrayData) array_obj = (*ai).get_write_pointer();
+    _array_writers.push_back(new GeomVertexArrayDataPipelineWriter(array_obj, _force_to_0, _current_thread));
   }
   }
 
 
   _object->clear_cache_stage();
   _object->clear_cache_stage();
@@ -2033,7 +2025,6 @@ make_array_writers() {
   _cdata->_animated_vertices_modified = UpdateSeq();
   _cdata->_animated_vertices_modified = UpdateSeq();
 
 
   _got_array_writers = true;
   _got_array_writers = true;
-  nassertv(get_array_writer(0)->get_data_size_bytes() == get_array(0)->get_data_size_bytes());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 18 - 14
panda/src/gobj/geomVertexData.h

@@ -20,7 +20,8 @@
 #define GEOMVERTEXDATA_H
 #define GEOMVERTEXDATA_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "typedWritableReferenceCount.h"
+#include "copyOnWriteObject.h"
+#include "copyOnWritePointer.h"
 #include "geomVertexFormat.h"
 #include "geomVertexFormat.h"
 #include "geomVertexColumn.h"
 #include "geomVertexColumn.h"
 #include "geomVertexArrayData.h"
 #include "geomVertexArrayData.h"
@@ -75,9 +76,12 @@ class GeomVertexColumn;
 //               GeomVertexWriter, and GeomVertexRewriter objects to
 //               GeomVertexWriter, and GeomVertexRewriter objects to
 //               read and write vertex data at a high level.
 //               read and write vertex data at a high level.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA GeomVertexData : public TypedWritableReferenceCount, public GeomEnums {
+class EXPCL_PANDA GeomVertexData : public CopyOnWriteObject, public GeomEnums {
 private:
 private:
   GeomVertexData();
   GeomVertexData();
+protected:
+  virtual PT(CopyOnWriteObject) make_cow_copy();
+  
 PUBLISHED:
 PUBLISHED:
   GeomVertexData(const string &name,
   GeomVertexData(const string &name,
                  const GeomVertexFormat *format, 
                  const GeomVertexFormat *format, 
@@ -106,16 +110,16 @@ PUBLISHED:
   void clear_rows();
   void clear_rows();
 
 
   INLINE int get_num_arrays() const;
   INLINE int get_num_arrays() const;
-  INLINE const GeomVertexArrayData *get_array(int i) const;
-  INLINE GeomVertexArrayData *modify_array(int i);
+  INLINE CPT(GeomVertexArrayData) get_array(int i) const;
+  INLINE PT(GeomVertexArrayData) modify_array(int i);
   INLINE void set_array(int i, const GeomVertexArrayData *array);
   INLINE void set_array(int i, const GeomVertexArrayData *array);
 
 
   INLINE const TransformTable *get_transform_table() const;
   INLINE const TransformTable *get_transform_table() const;
   void set_transform_table(const TransformTable *table);
   void set_transform_table(const TransformTable *table);
   INLINE void clear_transform_table();
   INLINE void clear_transform_table();
 
 
-  INLINE const TransformBlendTable *get_transform_blend_table() const;
-  TransformBlendTable *modify_transform_blend_table();
+  INLINE CPT(TransformBlendTable) get_transform_blend_table() const;
+  PT(TransformBlendTable) modify_transform_blend_table();
   void set_transform_blend_table(const TransformBlendTable *table);
   void set_transform_blend_table(const TransformBlendTable *table);
   INLINE void clear_transform_blend_table();
   INLINE void clear_transform_blend_table();
 
 
@@ -184,7 +188,7 @@ private:
 private:
 private:
   string _name;
   string _name;
 
 
-  typedef pvector< PT(GeomVertexArrayData) > Arrays;
+  typedef pvector< COWPT(GeomVertexArrayData) > Arrays;
 
 
   // The pipelined data with each CacheEntry.
   // The pipelined data with each CacheEntry.
   class CDataCache : public CycleData {
   class CDataCache : public CycleData {
@@ -273,7 +277,7 @@ private:
     CPT(GeomVertexFormat) _format;
     CPT(GeomVertexFormat) _format;
     Arrays _arrays;
     Arrays _arrays;
     CPT(TransformTable) _transform_table;
     CPT(TransformTable) _transform_table;
-    PT(TransformBlendTable) _transform_blend_table;
+    COWPT(TransformBlendTable) _transform_blend_table;
     CPT(SliderTable) _slider_table;
     CPT(SliderTable) _slider_table;
     PT(GeomVertexData) _animated_vertices;
     PT(GeomVertexData) _animated_vertices;
     UpdateSeq _animated_vertices_modified;
     UpdateSeq _animated_vertices_modified;
@@ -329,9 +333,9 @@ public:
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
-    TypedWritableReferenceCount::init_type();
+    CopyOnWriteObject::init_type();
     register_type(_type_handle, "GeomVertexData",
     register_type(_type_handle, "GeomVertexData",
-                  TypedWritableReferenceCount::get_class_type());
+                  CopyOnWriteObject::get_class_type());
     CDataCache::init_type();
     CDataCache::init_type();
     CacheEntry::init_type();
     CacheEntry::init_type();
     CData::init_type();
     CData::init_type();
@@ -373,15 +377,15 @@ public:
 
 
   INLINE UsageHint get_usage_hint() const;
   INLINE UsageHint get_usage_hint() const;
   INLINE int get_num_arrays() const;
   INLINE int get_num_arrays() const;
-  INLINE const GeomVertexArrayData *get_array(int i) const;
+  INLINE CPT(GeomVertexArrayData) get_array(int i) const;
   INLINE const TransformTable *get_transform_table() const;
   INLINE const TransformTable *get_transform_table() const;
-  INLINE const TransformBlendTable *get_transform_blend_table() const;
+  INLINE CPT(TransformBlendTable) get_transform_blend_table() const;
   INLINE const SliderTable *get_slider_table() const;
   INLINE const SliderTable *get_slider_table() const;
   int get_num_bytes() const;
   int get_num_bytes() const;
   INLINE UpdateSeq get_modified() const;
   INLINE UpdateSeq get_modified() const;
 
 
 protected:
 protected:
-  GeomVertexData *_object;
+  PT(GeomVertexData) _object;
   Thread *_current_thread;
   Thread *_current_thread;
   GeomVertexData::CData *_cdata;
   GeomVertexData::CData *_cdata;
 };
 };
@@ -471,7 +475,7 @@ public:
   INLINE void check_array_writers() const;
   INLINE void check_array_writers() const;
   INLINE GeomVertexArrayDataPipelineWriter *get_array_writer(int i) const;
   INLINE GeomVertexArrayDataPipelineWriter *get_array_writer(int i) const;
 
 
-  GeomVertexArrayData *modify_array(int i);
+  PT(GeomVertexArrayData) modify_array(int i);
   void set_array(int i, const GeomVertexArrayData *array);
   void set_array(int i, const GeomVertexArrayData *array);
 
 
   int get_num_rows() const;
   int get_num_rows() const;

+ 10 - 0
panda/src/gobj/transformBlendTable.cxx

@@ -23,6 +23,16 @@
 
 
 TypeHandle TransformBlendTable::_type_handle;
 TypeHandle TransformBlendTable::_type_handle;
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TransformBlendTable::make_cow_copy
+//       Access: Protected, Virtual
+//  Description: Required to implement CopyOnWriteObject.
+////////////////////////////////////////////////////////////////////
+PT(CopyOnWriteObject) TransformBlendTable::
+make_cow_copy() {
+  return new TransformBlendTable(*this);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformBlendTable::Constructor
 //     Function: TransformBlendTable::Constructor
 //       Access: Published
 //       Access: Published

+ 7 - 4
panda/src/gobj/transformBlendTable.h

@@ -22,7 +22,7 @@
 #include "pandabase.h"
 #include "pandabase.h"
 #include "transformBlend.h"
 #include "transformBlend.h"
 #include "vertexTransform.h"
 #include "vertexTransform.h"
-#include "typedWritableReferenceCount.h"
+#include "copyOnWriteObject.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "pvector.h"
 #include "pvector.h"
 #include "pmap.h"
 #include "pmap.h"
@@ -51,7 +51,10 @@ class FactoryParams;
 //               TransformTable for one set up to compute its
 //               TransformTable for one set up to compute its
 //               dynamic vertices on the graphics card.
 //               dynamic vertices on the graphics card.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA TransformBlendTable : public TypedWritableReferenceCount {
+class EXPCL_PANDA TransformBlendTable : public CopyOnWriteObject {
+protected:
+  virtual PT(CopyOnWriteObject) make_cow_copy();
+
 PUBLISHED:
 PUBLISHED:
   TransformBlendTable();
   TransformBlendTable();
   TransformBlendTable(const TransformBlendTable &copy);
   TransformBlendTable(const TransformBlendTable &copy);
@@ -141,9 +144,9 @@ public:
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
-    TypedWritableReferenceCount::init_type();
+    CopyOnWriteObject::init_type();
     register_type(_type_handle, "TransformBlendTable",
     register_type(_type_handle, "TransformBlendTable",
-                  TypedWritableReferenceCount::get_class_type());
+                  CopyOnWriteObject::get_class_type());
   }
   }
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
     return get_class_type();
     return get_class_type();

+ 1 - 1
panda/src/net/connectionReader.cxx

@@ -129,7 +129,7 @@ ConnectionReader(ConnectionManager *manager, int num_threads) :
     _threads.push_back(thread);
     _threads.push_back(thread);
   }
   }
   for (i = 0; i < num_threads; i++) {
   for (i = 0; i < num_threads; i++) {
-    _threads[i]->start(TP_normal, true, true);
+    _threads[i]->start(TP_normal, true);
   }
   }
 
 
   _manager->add_reader(this);
   _manager->add_reader(this);

+ 1 - 1
panda/src/net/connectionWriter.cxx

@@ -85,7 +85,7 @@ ConnectionWriter(ConnectionManager *manager, int num_threads) :
     _threads.push_back(thread);
     _threads.push_back(thread);
   }
   }
   for (i = 0; i < num_threads; i++) {
   for (i = 0; i < num_threads; i++) {
-    _threads[i]->start(TP_normal, true, true);
+    _threads[i]->start(TP_normal, true);
   }
   }
 
 
   _manager->add_writer(this);
   _manager->add_writer(this);

+ 26 - 47
panda/src/pgraph/geomNode.I

@@ -47,25 +47,12 @@ set_preserved(bool value){
 //               shared between multiple different GeomNodes, but see
 //               shared between multiple different GeomNodes, but see
 //               get_unique_geom().
 //               get_unique_geom().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const Geom *GeomNode::
+INLINE CPT(Geom) GeomNode::
 get_geom(int n) const {
 get_geom(int n) const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
-  const GeomList &geoms = *(cdata->get_geoms());
-  nassertr(n >= 0 && n < (int)geoms.size(), NULL);
-  return geoms[n]._geom;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomNode::get_unique_geom
-//       Access: Public
-//  Description: This method is deprecated; you should call
-//               modify_geom() instead.
-////////////////////////////////////////////////////////////////////
-INLINE Geom *GeomNode::
-get_unique_geom(int n) {
-  pgraph_cat.warning() 
-    << "Deprecated method GeomNode::get_unique_geom() called.  Use modify_geom() instead.\n";
-  return modify_geom(n);
+  CPT(GeomList) geoms = cdata->get_geoms();
+  nassertr(n >= 0 && n < (int)geoms->size(), NULL);
+  return (*geoms)[n]._geom.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -86,17 +73,12 @@ get_unique_geom(int n) {
 //               that were made independently in pipeline stage 0.
 //               that were made independently in pipeline stage 0.
 //               Use with caution.
 //               Use with caution.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE Geom *GeomNode::
+INLINE PT(Geom) GeomNode::
 modify_geom(int n) {
 modify_geom(int n) {
   CDWriter cdata(_cycler, true);
   CDWriter cdata(_cycler, true);
-  GeomList &geoms = *(cdata->modify_geoms());
-  nassertr(n >= 0 && n < (int)geoms.size(), NULL);
-  Geom *geom = geoms[n]._geom;
-  if (geom->get_ref_count() > 1) {
-    geom = geoms[n]._geom = geom->make_copy();
-  }
-
-  return geom;
+  PT(GeomList) geoms = cdata->modify_geoms();
+  nassertr(n >= 0 && n < (int)geoms->size(), NULL);
+  return (*geoms)[n]._geom.get_write_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -112,9 +94,9 @@ modify_geom(int n) {
 INLINE const RenderState *GeomNode::
 INLINE const RenderState *GeomNode::
 get_geom_state(int n) const {
 get_geom_state(int n) const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
-  const GeomList &geoms = *(cdata->get_geoms());
-  nassertr(n >= 0 && n < (int)geoms.size(), NULL);
-  return geoms[n]._state;
+  CPT(GeomList) geoms = cdata->get_geoms();
+  nassertr(n >= 0 && n < (int)geoms->size(), NULL);
+  return (*geoms)[n]._state;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -137,9 +119,9 @@ get_geom_state(int n) const {
 INLINE void GeomNode::
 INLINE void GeomNode::
 set_geom_state(int n, const RenderState *state) {
 set_geom_state(int n, const RenderState *state) {
   CDWriter cdata(_cycler, true);
   CDWriter cdata(_cycler, true);
-  GeomList &geoms = *(cdata->modify_geoms());
-  nassertv(n >= 0 && n < (int)geoms.size());
-  geoms[n]._state = state;
+  PT(GeomList) geoms = cdata->modify_geoms();
+  nassertv(n >= 0 && n < (int)geoms->size());
+  (*geoms)[n]._state = state;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -150,10 +132,10 @@ set_geom_state(int n, const RenderState *state) {
 INLINE void GeomNode::
 INLINE void GeomNode::
 remove_geom(int n) {
 remove_geom(int n) {
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
-  GeomList &geoms = *(cdata->modify_geoms());
-  nassertv(n >= 0 && n < (int)geoms.size());
+  PT(GeomList) geoms = cdata->modify_geoms();
+  nassertv(n >= 0 && n < (int)geoms->size());
 
 
-  geoms.erase(geoms.begin() + n);
+  geoms->erase(geoms->begin() + n);
   mark_internal_bounds_stale();
   mark_internal_bounds_stale();
 }
 }
 
 
@@ -260,9 +242,9 @@ CData() :
 //       Access: Public
 //       Access: Public
 //  Description: Returns a read-only pointer to the _geoms list.
 //  Description: Returns a read-only pointer to the _geoms list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const GeomNode::GeomList *GeomNode::CData::
+INLINE CPT(GeomNode::GeomList) GeomNode::CData::
 get_geoms() const {
 get_geoms() const {
-  return _geoms;
+  return _geoms.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -271,12 +253,9 @@ get_geoms() const {
 //  Description: Returns a modifiable, unique pointer to the _geoms
 //  Description: Returns a modifiable, unique pointer to the _geoms
 //               list.
 //               list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE GeomNode::GeomList *GeomNode::CData::
+INLINE PT(GeomNode::GeomList) GeomNode::CData::
 modify_geoms() {
 modify_geoms() {
-  if (_geoms->get_ref_count() > 1) {
-    _geoms = new GeomList(*_geoms);
-  }
-  return _geoms;
+  return _geoms.get_write_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -337,7 +316,7 @@ operator = (const GeomNode::Geoms &copy) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomNode::Geoms::
 INLINE int GeomNode::Geoms::
 get_num_geoms() const {
 get_num_geoms() const {
-  nassertr(_geoms != (GeomList *)NULL, 0);
+  nassertr(!_geoms.is_null(), 0);
   return _geoms->size();
   return _geoms->size();
 }
 }
 
 
@@ -348,11 +327,11 @@ get_num_geoms() const {
 //               not be modified, since the same object might be
 //               not be modified, since the same object might be
 //               shared between multiple different GeomNodes.
 //               shared between multiple different GeomNodes.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const Geom *GeomNode::Geoms::
+INLINE CPT(Geom) GeomNode::Geoms::
 get_geom(int n) const {
 get_geom(int n) const {
-  nassertr(_geoms != (GeomList *)NULL, NULL);
+  nassertr(!_geoms.is_null(), NULL);
   nassertr(n >= 0 && n < (int)_geoms->size(), NULL);
   nassertr(n >= 0 && n < (int)_geoms->size(), NULL);
-  return (*_geoms)[n]._geom;
+  return (*_geoms)[n]._geom.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -367,7 +346,7 @@ get_geom(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const RenderState *GeomNode::Geoms::
 INLINE const RenderState *GeomNode::Geoms::
 get_geom_state(int n) const {
 get_geom_state(int n) const {
-  nassertr(_geoms != (GeomList *)NULL, NULL);
+  nassertr(!_geoms.is_null(), NULL);
   nassertr(n >= 0 && n < (int)_geoms->size(), NULL);
   nassertr(n >= 0 && n < (int)_geoms->size(), NULL);
   return (*_geoms)[n]._state;
   return (*_geoms)[n]._state;
 }
 }

+ 52 - 42
panda/src/pgraph/geomNode.cxx

@@ -115,10 +115,10 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
     CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
     CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
     GeomList::iterator gi;
     GeomList::iterator gi;
-    GeomList &geoms = *(cdata->modify_geoms());
-    for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
+    PT(GeomList) geoms = cdata->modify_geoms();
+    for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
       GeomEntry &entry = (*gi);
       GeomEntry &entry = (*gi);
-      PT(Geom) new_geom = entry._geom->make_copy();
+      PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
       
       
       AccumulatedAttribs geom_attribs = attribs;
       AccumulatedAttribs geom_attribs = attribs;
       entry._state = geom_attribs.collect(entry._state, attrib_types);
       entry._state = geom_attribs.collect(entry._state, attrib_types);
@@ -319,9 +319,9 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
 
 
   CDReader cdata(_cycler, current_thread);
   CDReader cdata(_cycler, current_thread);
   GeomList::const_iterator gi;
   GeomList::const_iterator gi;
-  const GeomList &geoms = *(cdata->get_geoms());
-  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
-    const Geom *geom = (*gi)._geom;
+  CPT(GeomList) geoms = cdata->get_geoms();
+  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
+    CPT(Geom) geom = (*gi)._geom.get_read_pointer();
     geom->calc_tight_bounds(min_point, max_point, found_any,
     geom->calc_tight_bounds(min_point, max_point, found_any,
 			    geom->get_vertex_data(current_thread)->animate_vertices(current_thread),
 			    geom->get_vertex_data(current_thread)->animate_vertices(current_thread),
 			    !next_transform->is_identity(), mat,
 			    !next_transform->is_identity(), mat,
@@ -401,12 +401,12 @@ add_geoms_from(const GeomNode *other) {
     CDStageReader cdata_other(other->_cycler, pipeline_stage, current_thread);
     CDStageReader cdata_other(other->_cycler, pipeline_stage, current_thread);
 
 
     GeomList::const_iterator gi;
     GeomList::const_iterator gi;
-    const GeomList &other_geoms = *(cdata_other->get_geoms());
-    GeomList &this_geoms = *(cdata->modify_geoms());
-    for (gi = other_geoms.begin(); gi != other_geoms.end(); ++gi) {
+    CPT(GeomList) other_geoms = cdata_other->get_geoms();
+    PT(GeomList) this_geoms = cdata->modify_geoms();
+    for (gi = other_geoms->begin(); gi != other_geoms->end(); ++gi) {
       const GeomEntry &entry = (*gi);
       const GeomEntry &entry = (*gi);
-      nassertv(entry._geom->check_valid());
-      this_geoms.push_back(entry);
+      nassertv(entry._geom.get_read_pointer()->check_valid());
+      this_geoms->push_back(entry);
     }
     }
   }
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
@@ -433,9 +433,9 @@ set_geom(int n, Geom *geom) {
   nassertv(geom->check_valid());
   nassertv(geom->check_valid());
 
 
   CDWriter cdata(_cycler, true);
   CDWriter cdata(_cycler, true);
-  GeomList &geoms = *(cdata->modify_geoms());
-  nassertv(n >= 0 && n < (int)geoms.size());
-  geoms[n]._geom = geom;
+  PT(GeomList) geoms = cdata->modify_geoms();
+  nassertv(n >= 0 && n < (int)geoms->size());
+  (*geoms)[n]._geom = geom;
 
 
   mark_internal_bounds_stale();
   mark_internal_bounds_stale();
 }
 }
@@ -496,17 +496,19 @@ unify(int max_indices) {
     // operation, but usually there are only a handful of Geoms to
     // operation, but usually there are only a handful of Geoms to
     // consider, so that's not a big deal.
     // consider, so that's not a big deal.
     GeomList::const_iterator gi;
     GeomList::const_iterator gi;
-    const GeomList &geoms = *(cdata->get_geoms());
-    for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
-      const GeomEntry &entry = (*gi);
+    CPT(GeomList) old_geoms = cdata->get_geoms();
+    for (gi = old_geoms->begin(); gi != old_geoms->end(); ++gi) {
+      const GeomEntry &old_entry = (*gi);
       
       
       bool unified = false;
       bool unified = false;
       GeomList::iterator gj;
       GeomList::iterator gj;
       for (gj = new_geoms->begin(); gj != new_geoms->end() && !unified; ++gj) {
       for (gj = new_geoms->begin(); gj != new_geoms->end() && !unified; ++gj) {
         GeomEntry &new_entry = (*gj);
         GeomEntry &new_entry = (*gj);
-        if (entry._state == new_entry._state) {
+        if (old_entry._state == new_entry._state) {
           // Both states match, so try to combine the primitives.
           // Both states match, so try to combine the primitives.
-          if (new_entry._geom->copy_primitives_from(entry._geom)) {
+          CPT(Geom) old_geom = old_entry._geom.get_read_pointer();
+          PT(Geom) new_geom = new_entry._geom.get_write_pointer();
+          if (new_geom->copy_primitives_from(old_geom)) {
             // Successfully combined!
             // Successfully combined!
             unified = true;
             unified = true;
             any_changed = true;
             any_changed = true;
@@ -517,7 +519,7 @@ unify(int max_indices) {
       if (!unified) {
       if (!unified) {
         // Couldn't unify this Geom with anything, so just add it to the
         // Couldn't unify this Geom with anything, so just add it to the
         // output list.
         // output list.
-        new_geoms->push_back(entry);
+        new_geoms->push_back(old_entry);
       }
       }
     }
     }
     
     
@@ -525,9 +527,12 @@ unify(int max_indices) {
     cdata->set_geoms(new_geoms);
     cdata->set_geoms(new_geoms);
 
 
     // Finally, go back through and unify the resulting geom(s).
     // Finally, go back through and unify the resulting geom(s).
-    for (gi = new_geoms->begin(); gi != new_geoms->end(); ++gi) {
-      const GeomEntry &entry = (*gi);
-      entry._geom->unify_in_place(max_indices);
+    GeomList::iterator wgi;
+    for (wgi = new_geoms->begin(); wgi != new_geoms->end(); ++wgi) {
+      GeomEntry &entry = (*wgi);
+      nassertv(entry._geom.test_ref_count_integrity());
+      PT(Geom) geom = entry._geom.get_write_pointer();
+      geom->unify_in_place(max_indices);
     }
     }
   }
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
@@ -548,11 +553,11 @@ write_geoms(ostream &out, int indent_level) const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
   write(out, indent_level);
   write(out, indent_level);
   GeomList::const_iterator gi;
   GeomList::const_iterator gi;
-  const GeomList &geoms = *(cdata->get_geoms());
-  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
+  CPT(GeomList) geoms = cdata->get_geoms();
+  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);
     const GeomEntry &entry = (*gi);
     indent(out, indent_level + 2) 
     indent(out, indent_level + 2) 
-      << *entry._geom << " " << *entry._state << "\n";
+      << *entry._geom.get_read_pointer() << " " << *entry._state << "\n";
   }
   }
 }
 }
 
 
@@ -567,12 +572,13 @@ write_verbose(ostream &out, int indent_level) const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
   write(out, indent_level);
   write(out, indent_level);
   GeomList::const_iterator gi;
   GeomList::const_iterator gi;
-  const GeomList &geoms = *(cdata->get_geoms());
-  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
+  CPT(GeomList) geoms = cdata->get_geoms();
+  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);
     const GeomEntry &entry = (*gi);
+    CPT(Geom) geom = entry._geom.get_read_pointer();
     indent(out, indent_level + 2) 
     indent(out, indent_level + 2) 
-      << *entry._geom << " " << *entry._state << "\n";
-    entry._geom->write(out, indent_level + 4);
+      << *geom << " " << *entry._state << "\n";
+    geom->write(out, indent_level + 4);
   }
   }
 }
 }
 
 
@@ -593,8 +599,8 @@ output(ostream &out) const {
   pset<TypeHandle> attrib_types;
   pset<TypeHandle> attrib_types;
 
 
   GeomList::const_iterator gi;
   GeomList::const_iterator gi;
-  const GeomList &geoms = *(cdata->get_geoms());
-  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
+  CPT(GeomList) geoms = cdata->get_geoms();
+  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);
     const GeomEntry &entry = (*gi);
     int num_attribs = entry._state->get_num_attribs();
     int num_attribs = entry._state->get_num_attribs();
     for (int i = 0; i < num_attribs; i++) {
     for (int i = 0; i < num_attribs; i++) {
@@ -604,7 +610,7 @@ output(ostream &out) const {
   }
   }
 
 
   PandaNode::output(out);
   PandaNode::output(out);
-  out << " (" << geoms.size() << " geoms";
+  out << " (" << geoms->size() << " geoms";
 
 
   if (!attrib_types.empty()) {
   if (!attrib_types.empty()) {
     out << ":";
     out << ":";
@@ -654,10 +660,10 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
   CDStageReader cdata(_cycler, pipeline_stage, current_thread);
   CDStageReader cdata(_cycler, pipeline_stage, current_thread);
 
 
   GeomList::const_iterator gi;
   GeomList::const_iterator gi;
-  const GeomList &geoms = *(cdata->get_geoms());
-  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
+  CPT(GeomList) geoms = cdata->get_geoms();
+  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);
     const GeomEntry &entry = (*gi);
-    child_volumes.push_back(entry._geom->get_bounds());
+    child_volumes.push_back(entry._geom.get_read_pointer()->get_bounds());
   }
   }
 
 
   const BoundingVolume **child_begin = &child_volumes[0];
   const BoundingVolume **child_begin = &child_volumes[0];
@@ -752,14 +758,15 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomNode::CData::
 void GeomNode::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
 write_datagram(BamWriter *manager, Datagram &dg) const {
-  int num_geoms = _geoms->size();
+  CPT(GeomList) geoms = _geoms.get_read_pointer();
+  int num_geoms = geoms->size();
   nassertv(num_geoms == (int)(PN_uint16)num_geoms);
   nassertv(num_geoms == (int)(PN_uint16)num_geoms);
   dg.add_uint16(num_geoms);
   dg.add_uint16(num_geoms);
   
   
   GeomList::const_iterator gi;
   GeomList::const_iterator gi;
-  for (gi = _geoms->begin(); gi != _geoms->end(); ++gi) {
+  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);
     const GeomEntry &entry = (*gi);
-    manager->write_pointer(dg, entry._geom);
+    manager->write_pointer(dg, entry._geom.get_read_pointer());
     manager->write_pointer(dg, entry._state);
     manager->write_pointer(dg, entry._state);
   }
   }
 }
 }
@@ -777,7 +784,8 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 
 
   // Get the geom and state pointers.
   // Get the geom and state pointers.
   GeomList::iterator gi;
   GeomList::iterator gi;
-  for (gi = _geoms->begin(); gi != _geoms->end(); ++gi) {
+  PT(GeomList) geoms = _geoms.get_write_pointer();
+  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     GeomEntry &entry = (*gi);
     GeomEntry &entry = (*gi);
     entry._geom = DCAST(Geom, p_list[pi++]);
     entry._geom = DCAST(Geom, p_list[pi++]);
     entry._state = DCAST(RenderState, p_list[pi++]);
     entry._state = DCAST(RenderState, p_list[pi++]);
@@ -797,10 +805,12 @@ void GeomNode::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   int num_geoms = scan.get_uint16();
   int num_geoms = scan.get_uint16();
   // Read the list of geoms and states.  Push back a NULL for each one.
   // Read the list of geoms and states.  Push back a NULL for each one.
-  _geoms->reserve(num_geoms);
+  PT(GeomList) geoms = new GeomList;
+  geoms->reserve(num_geoms);
   for (int i = 0; i < num_geoms; i++) {
   for (int i = 0; i < num_geoms; i++) {
     manager->read_pointer(scan);
     manager->read_pointer(scan);
     manager->read_pointer(scan);
     manager->read_pointer(scan);
-    _geoms->push_back(GeomEntry(NULL, NULL));
+    geoms->push_back(GeomEntry(NULL, NULL));
   }
   }
+  _geoms = geoms;
 }
 }

+ 10 - 9
panda/src/pgraph/geomNode.h

@@ -27,6 +27,7 @@
 #include "pipelineCycler.h"
 #include "pipelineCycler.h"
 #include "cycleData.h"
 #include "cycleData.h"
 #include "pvector.h"
 #include "pvector.h"
+#include "copyOnWritePointer.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : GeomNode
 //       Class : GeomNode
@@ -64,9 +65,8 @@ PUBLISHED:
 
 
   INLINE void set_preserved(bool value);
   INLINE void set_preserved(bool value);
   INLINE int get_num_geoms() const;
   INLINE int get_num_geoms() const;
-  INLINE const Geom *get_geom(int n) const;
-  INLINE Geom *get_unique_geom(int n);
-  INLINE Geom *modify_geom(int n);
+  INLINE CPT(Geom) get_geom(int n) const;
+  INLINE PT(Geom) modify_geom(int n);
   INLINE const RenderState *get_geom_state(int n) const;
   INLINE const RenderState *get_geom_state(int n) const;
   INLINE void set_geom_state(int n, const RenderState *state);
   INLINE void set_geom_state(int n, const RenderState *state);
 
 
@@ -98,14 +98,14 @@ public:
   class GeomEntry {
   class GeomEntry {
   public:
   public:
     INLINE GeomEntry(Geom *geom, const RenderState *state);
     INLINE GeomEntry(Geom *geom, const RenderState *state);
-    PT(Geom) _geom;
+    COWPT(Geom) _geom;
     CPT(RenderState) _state;
     CPT(RenderState) _state;
   };
   };
 
 
 private:
 private:
 
 
   bool _preserved;
   bool _preserved;
-  typedef RefCountObj< pvector<GeomEntry> > GeomList;
+  typedef CopyOnWriteObj< pvector<GeomEntry> > GeomList;
   typedef pmap<const InternalName *, int> NameCount;
   typedef pmap<const InternalName *, int> NameCount;
 
 
   INLINE void count_name(NameCount &name_count, const InternalName *name);
   INLINE void count_name(NameCount &name_count, const InternalName *name);
@@ -124,12 +124,12 @@ private:
       return GeomNode::get_class_type();
       return GeomNode::get_class_type();
     }
     }
 
 
-    INLINE const GeomList *get_geoms() const;
-    INLINE GeomList *modify_geoms();
+    INLINE CPT(GeomList) get_geoms() const;
+    INLINE PT(GeomList) modify_geoms();
     INLINE void set_geoms(GeomList *geoms);
     INLINE void set_geoms(GeomList *geoms);
 
 
   private:
   private:
-    PT(GeomList) _geoms;
+    COWPT(GeomList) _geoms;
   };
   };
 
 
   PipelineCycler<CData> _cycler;
   PipelineCycler<CData> _cycler;
@@ -151,7 +151,7 @@ public:
     INLINE void operator = (const Geoms &copy);
     INLINE void operator = (const Geoms &copy);
 
 
     INLINE int get_num_geoms() const;
     INLINE int get_num_geoms() const;
-    INLINE const Geom *get_geom(int n) const;
+    INLINE CPT(Geom) get_geom(int n) const;
     INLINE const RenderState *get_geom_state(int n) const;
     INLINE const RenderState *get_geom_state(int n) const;
 
 
   private:
   private:
@@ -173,6 +173,7 @@ public:
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
+    GeomList::init_type();
     PandaNode::init_type();
     PandaNode::init_type();
     register_type(_type_handle, "GeomNode",
     register_type(_type_handle, "GeomNode",
                   PandaNode::get_class_type());
                   PandaNode::get_class_type());

+ 5 - 5
panda/src/pgraph/geomTransformer.cxx

@@ -136,7 +136,7 @@ transform_vertices(GeomNode *node, const LMatrix4f &mat) {
     GeomNode::GeomList &geoms = *(cdata->modify_geoms());
     GeomNode::GeomList &geoms = *(cdata->modify_geoms());
     for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
       GeomNode::GeomEntry &entry = (*gi);
       GeomNode::GeomEntry &entry = (*gi);
-      PT(Geom) new_geom = entry._geom->make_copy();
+      PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
       if (transform_vertices(new_geom, mat)) {
       if (transform_vertices(new_geom, mat)) {
         entry._geom = new_geom;
         entry._geom = new_geom;
         any_changed = true;
         any_changed = true;
@@ -230,7 +230,7 @@ transform_texcoords(GeomNode *node, const InternalName *from_name,
   GeomNode::GeomList &geoms = *(cdata->modify_geoms());
   GeomNode::GeomList &geoms = *(cdata->modify_geoms());
   for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
   for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     GeomNode::GeomEntry &entry = (*gi);
     GeomNode::GeomEntry &entry = (*gi);
-    PT(Geom) new_geom = entry._geom->make_copy();
+    PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
     if (transform_texcoords(new_geom, from_name, to_name, mat)) {
     if (transform_texcoords(new_geom, from_name, to_name, mat)) {
       entry._geom = new_geom;
       entry._geom = new_geom;
       any_changed = true;
       any_changed = true;
@@ -290,7 +290,7 @@ set_color(GeomNode *node, const Colorf &color) {
   GeomNode::GeomList &geoms = *(cdata->modify_geoms());
   GeomNode::GeomList &geoms = *(cdata->modify_geoms());
   for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
   for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     GeomNode::GeomEntry &entry = (*gi);
     GeomNode::GeomEntry &entry = (*gi);
-    PT(Geom) new_geom = entry._geom->make_copy();
+    PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
     if (set_color(new_geom, color)) {
     if (set_color(new_geom, color)) {
       entry._geom = new_geom;
       entry._geom = new_geom;
       any_changed = true;
       any_changed = true;
@@ -353,7 +353,7 @@ transform_colors(GeomNode *node, const LVecBase4f &scale) {
   GeomNode::GeomList &geoms = *(cdata->modify_geoms());
   GeomNode::GeomList &geoms = *(cdata->modify_geoms());
   for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
   for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     GeomNode::GeomEntry &entry = (*gi);
     GeomNode::GeomEntry &entry = (*gi);
-    PT(Geom) new_geom = entry._geom->make_copy();
+    PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
     if (transform_colors(new_geom, scale)) {
     if (transform_colors(new_geom, scale)) {
       entry._geom = new_geom;
       entry._geom = new_geom;
       any_changed = true;
       any_changed = true;
@@ -686,7 +686,7 @@ collect_vertex_data(GeomNode *node, int collect_bits) {
   GeomNode::GeomList &geoms = *(cdata->modify_geoms());
   GeomNode::GeomList &geoms = *(cdata->modify_geoms());
   for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
   for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     GeomNode::GeomEntry &entry = (*gi);
     GeomNode::GeomEntry &entry = (*gi);
-    PT(Geom) new_geom = entry._geom->make_copy();
+    PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
     entry._geom = new_geom;
     entry._geom = new_geom;
 
 
     if ((collect_bits & SceneGraphReducer::CVD_avoid_dynamic) != 0 &&
     if ((collect_bits & SceneGraphReducer::CVD_avoid_dynamic) != 0 &&

+ 2 - 2
panda/src/pgraph/loader.h

@@ -79,8 +79,8 @@ PUBLISHED:
 
 
   Loader(const string &name = "loader", int num_threads = -1);
   Loader(const string &name = "loader", int num_threads = -1);
 
 
-  INLINE PT(PandaNode) load_sync(const Filename &filename, 
-                                 const LoaderOptions &options = LoaderOptions()) const;
+  BLOCKING INLINE PT(PandaNode) load_sync(const Filename &filename, 
+                                          const LoaderOptions &options = LoaderOptions()) const;
 
 
   INLINE void load_async(AsyncTask *request);
   INLINE void load_async(AsyncTask *request);
 
 

+ 11 - 1
panda/src/pgraph/nodePath.cxx

@@ -5522,6 +5522,16 @@ verify_complete(Thread *current_thread) const {
     return true;
     return true;
   }
   }
 
 
+#ifdef HAVE_THREADS
+  if (Thread::is_threading_supported()) {
+    // In a threaded environment, we can't reliably test this, since a
+    // sub-thread may be mucking with the NodePath's ancestry as we
+    // try to validate it.  NodePaths are inherently not thread-safe,
+    // but generally that's not an issue.
+    return true;
+  }
+#endif  // HAVE_THREADS
+
   const NodePathComponent *comp = _head;
   const NodePathComponent *comp = _head;
   nassertr(comp != (const NodePathComponent *)NULL, false);
   nassertr(comp != (const NodePathComponent *)NULL, false);
 
 
@@ -5530,7 +5540,7 @@ verify_complete(Thread *current_thread) const {
   PandaNode *node = comp->get_node();
   PandaNode *node = comp->get_node();
   nassertr(node != (const PandaNode *)NULL, false);
   nassertr(node != (const PandaNode *)NULL, false);
   int length = comp->get_length(pipeline_stage, current_thread);
   int length = comp->get_length(pipeline_stage, current_thread);
-
+  
   comp = comp->get_next(pipeline_stage, current_thread);
   comp = comp->get_next(pipeline_stage, current_thread);
   length--;
   length--;
   while (comp != (const NodePathComponent *)NULL) {
   while (comp != (const NodePathComponent *)NULL) {

+ 1 - 1
panda/src/pgraph/nodePath.h

@@ -833,7 +833,7 @@ PUBLISHED:
   INLINE void set_name(const string &name);
   INLINE void set_name(const string &name);
   INLINE string get_name() const;
   INLINE string get_name() const;
 
 
-  bool write_bam_file(const string &filename) const;
+  BLOCKING bool write_bam_file(const string &filename) const;
 
 
 private:
 private:
   static NodePathComponent *
   static NodePathComponent *

+ 50 - 59
panda/src/pgraph/pandaNode.I

@@ -44,9 +44,9 @@ get_num_parents(Thread *current_thread) const {
 INLINE PandaNode *PandaNode::
 INLINE PandaNode *PandaNode::
 get_parent(int n, Thread *current_thread) const {
 get_parent(int n, Thread *current_thread) const {
   CDReader cdata(_cycler, current_thread);
   CDReader cdata(_cycler, current_thread);
-  const Up &up = *cdata->get_up();
-  nassertr(n >= 0 && n < (int)up.size(), NULL);
-  return up[n].get_parent();
+  CPT(Up) up = cdata->get_up();
+  nassertr(n >= 0 && n < (int)up->size(), NULL);
+  return (*up)[n].get_parent();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -86,9 +86,9 @@ get_num_children(Thread *current_thread) const {
 INLINE PandaNode *PandaNode::
 INLINE PandaNode *PandaNode::
 get_child(int n, Thread *current_thread) const {
 get_child(int n, Thread *current_thread) const {
   CDReader cdata(_cycler, current_thread);
   CDReader cdata(_cycler, current_thread);
-  const Down &down = *cdata->get_down();
-  nassertr(n >= 0 && n < (int)down.size(), NULL);
-  return down[n].get_child();
+  CPT(Down) down = cdata->get_down();
+  nassertr(n >= 0 && n < (int)down->size(), NULL);
+  return (*down)[n].get_child();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -101,9 +101,9 @@ get_child(int n, Thread *current_thread) const {
 INLINE int PandaNode::
 INLINE int PandaNode::
 get_child_sort(int n, Thread *current_thread) const {
 get_child_sort(int n, Thread *current_thread) const {
   CDReader cdata(_cycler, current_thread);
   CDReader cdata(_cycler, current_thread);
-  const Down &down = *cdata->get_down();
-  nassertr(n >= 0 && n < (int)down.size(), -1);
-  return down[n].get_sort();
+  CPT(Down) down = cdata->get_down();
+  nassertr(n >= 0 && n < (int)down->size(), -1);
+  return (*down)[n].get_sort();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -115,7 +115,7 @@ get_child_sort(int n, Thread *current_thread) const {
 INLINE int PandaNode::
 INLINE int PandaNode::
 find_child(PandaNode *node, Thread *current_thread) const {
 find_child(PandaNode *node, Thread *current_thread) const {
   CDReader cdata(_cycler, current_thread);
   CDReader cdata(_cycler, current_thread);
-  return do_find_child(node, cdata);
+  return do_find_child(node, cdata->get_down());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -195,9 +195,9 @@ get_num_stashed(Thread *current_thread) const {
 INLINE PandaNode *PandaNode::
 INLINE PandaNode *PandaNode::
 get_stashed(int n, Thread *current_thread) const {
 get_stashed(int n, Thread *current_thread) const {
   CDReader cdata(_cycler, current_thread);
   CDReader cdata(_cycler, current_thread);
-  const Down &stashed = *cdata->get_stashed();
-  nassertr(n >= 0 && n < (int)stashed.size(), NULL);
-  return stashed[n].get_child();
+  CPT(Down) stashed = cdata->get_stashed();
+  nassertr(n >= 0 && n < (int)stashed->size(), NULL);
+  return (*stashed)[n].get_child();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -210,9 +210,9 @@ get_stashed(int n, Thread *current_thread) const {
 INLINE int PandaNode::
 INLINE int PandaNode::
 get_stashed_sort(int n, Thread *current_thread) const {
 get_stashed_sort(int n, Thread *current_thread) const {
   CDReader cdata(_cycler, current_thread);
   CDReader cdata(_cycler, current_thread);
-  const Down &stashed = *cdata->get_stashed();
-  nassertr(n >= 0 && n < (int)stashed.size(), -1);
-  return stashed[n].get_sort();
+  CPT(Down) stashed = cdata->get_stashed();
+  nassertr(n >= 0 && n < (int)stashed->size(), -1);
+  return (*stashed)[n].get_sort();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -224,7 +224,7 @@ get_stashed_sort(int n, Thread *current_thread) const {
 INLINE int PandaNode::
 INLINE int PandaNode::
 find_stashed(PandaNode *node, Thread *current_thread) const {
 find_stashed(PandaNode *node, Thread *current_thread) const {
   CDReader cdata(_cycler, current_thread);
   CDReader cdata(_cycler, current_thread);
-  return do_find_stashed(node, cdata);
+  return do_find_child(node, cdata->get_stashed());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -754,12 +754,12 @@ get_parents(Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNode::
 INLINE int PandaNode::
 do_find_parent(PandaNode *node, const CData *cdata) const {
 do_find_parent(PandaNode *node, const CData *cdata) const {
-  const Up &up = *cdata->get_up();
-  Up::const_iterator ui = up.find(UpConnection(node));
-  if (ui == up.end()) {
+  CPT(Up) up = cdata->get_up();
+  Up::const_iterator ui = up->find(UpConnection(node));
+  if (ui == up->end()) {
     return -1;
     return -1;
   }
   }
-  return ui - up.begin();
+  return ui - up->begin();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -930,9 +930,9 @@ set_fancy_bit(int bits, bool value) {
 //       Access: Public
 //       Access: Public
 //  Description: Returns a read-only pointer to the _down list.
 //  Description: Returns a read-only pointer to the _down list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const PandaNode::Down *PandaNode::CData::
+INLINE CPT(PandaNode::Down) PandaNode::CData::
 get_down() const {
 get_down() const {
-  return _down;
+  return _down.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -941,12 +941,9 @@ get_down() const {
 //  Description: Returns a modifiable, unique pointer to the _down
 //  Description: Returns a modifiable, unique pointer to the _down
 //               list.
 //               list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE PandaNode::Down *PandaNode::CData::
+INLINE PT(PandaNode::Down) PandaNode::CData::
 modify_down() {
 modify_down() {
-  if (_down->get_ref_count() > 1) {
-    _down = new Down(*_down);
-  }
-  return _down;
+  return _down.get_write_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -954,9 +951,9 @@ modify_down() {
 //       Access: Public
 //       Access: Public
 //  Description: Returns a read-only pointer to the _stashed list.
 //  Description: Returns a read-only pointer to the _stashed list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const PandaNode::Down *PandaNode::CData::
+INLINE CPT(PandaNode::Down) PandaNode::CData::
 get_stashed() const {
 get_stashed() const {
-  return _stashed;
+  return _stashed.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -965,12 +962,9 @@ get_stashed() const {
 //  Description: Returns a modifiable, unique pointer to the _stashed
 //  Description: Returns a modifiable, unique pointer to the _stashed
 //               list.
 //               list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE PandaNode::Down *PandaNode::CData::
+INLINE PT(PandaNode::Down) PandaNode::CData::
 modify_stashed() {
 modify_stashed() {
-  if (_stashed->get_ref_count() > 1) {
-    _stashed = new Down(*_stashed);
-  }
-  return _stashed;
+  return _stashed.get_write_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -978,9 +972,9 @@ modify_stashed() {
 //       Access: Public
 //       Access: Public
 //  Description: Returns a read-only pointer to the _up list.
 //  Description: Returns a read-only pointer to the _up list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const PandaNode::Up *PandaNode::CData::
+INLINE CPT(PandaNode::Up) PandaNode::CData::
 get_up() const {
 get_up() const {
-  return _up;
+  return _up.get_read_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -989,12 +983,9 @@ get_up() const {
 //  Description: Returns a modifiable, unique pointer to the _up
 //  Description: Returns a modifiable, unique pointer to the _up
 //               list.
 //               list.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE PandaNode::Up *PandaNode::CData::
+INLINE PT(PandaNode::Up) PandaNode::CData::
 modify_up() {
 modify_up() {
-  if (_up->get_ref_count() > 1) {
-    _up = new Up(*_up);
-  }
-  return _up;
+  return _up.get_write_pointer();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1426,9 +1417,9 @@ get_num_parents() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *PandaNodePipelineReader::
 INLINE PandaNode *PandaNodePipelineReader::
 get_parent(int n) const {
 get_parent(int n) const {
-  const PandaNode::Up &up = *_cdata->get_up();
-  nassertr(n >= 0 && n < (int)up.size(), NULL);
-  return up[n].get_parent();
+  CPT(PandaNode::Up) up = _cdata->get_up();
+  nassertr(n >= 0 && n < (int)up->size(), NULL);
+  return (*up)[n].get_parent();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1465,9 +1456,9 @@ get_num_children() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *PandaNodePipelineReader::
 INLINE PandaNode *PandaNodePipelineReader::
 get_child(int n) const {
 get_child(int n) const {
-  const PandaNode::Down &down = *_cdata->get_down();
-  nassertr(n >= 0 && n < (int)down.size(), NULL);
-  return down[n].get_child();
+  CPT(PandaNode::Down) down = _cdata->get_down();
+  nassertr(n >= 0 && n < (int)down->size(), NULL);
+  return (*down)[n].get_child();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1479,9 +1470,9 @@ get_child(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNodePipelineReader::
 INLINE int PandaNodePipelineReader::
 get_child_sort(int n) const {
 get_child_sort(int n) const {
-  const PandaNode::Down &down = *_cdata->get_down();
-  nassertr(n >= 0 && n < (int)down.size(), -1);
-  return down[n].get_sort();
+  CPT(PandaNode::Down) down = _cdata->get_down();
+  nassertr(n >= 0 && n < (int)down->size(), -1);
+  return (*down)[n].get_sort();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1492,7 +1483,7 @@ get_child_sort(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNodePipelineReader::
 INLINE int PandaNodePipelineReader::
 find_child(PandaNode *node) const {
 find_child(PandaNode *node) const {
-  return _object->do_find_child(node, _cdata);
+  return _object->do_find_child(node, _cdata->get_down());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1518,9 +1509,9 @@ get_num_stashed() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *PandaNodePipelineReader::
 INLINE PandaNode *PandaNodePipelineReader::
 get_stashed(int n) const {
 get_stashed(int n) const {
-  const PandaNode::Down &stashed = *_cdata->get_stashed();
-  nassertr(n >= 0 && n < (int)stashed.size(), NULL);
-  return stashed[n].get_child();
+  CPT(PandaNode::Down) stashed = _cdata->get_stashed();
+  nassertr(n >= 0 && n < (int)stashed->size(), NULL);
+  return (*stashed)[n].get_child();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1532,9 +1523,9 @@ get_stashed(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNodePipelineReader::
 INLINE int PandaNodePipelineReader::
 get_stashed_sort(int n) const {
 get_stashed_sort(int n) const {
-  const PandaNode::Down &stashed = *_cdata->get_stashed();
-  nassertr(n >= 0 && n < (int)stashed.size(), -1);
-  return stashed[n].get_sort();
+  CPT(PandaNode::Down) stashed = _cdata->get_stashed();
+  nassertr(n >= 0 && n < (int)stashed->size(), -1);
+  return (*stashed)[n].get_sort();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1545,7 +1536,7 @@ get_stashed_sort(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PandaNodePipelineReader::
 INLINE int PandaNodePipelineReader::
 find_stashed(PandaNode *node) const {
 find_stashed(PandaNode *node) const {
-  return _object->do_find_stashed(node, _cdata);
+  return _object->do_find_child(node, _cdata->get_stashed());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 56 - 78
panda/src/pgraph/pandaNode.cxx

@@ -649,16 +649,16 @@ remove_child(int child_index, Thread *current_thread) {
   nassertv(pipeline_stage == 0);
   nassertv(pipeline_stage == 0);
 
 
   CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
   CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
-  Down &down = *cdata->modify_down();
-  nassertv(child_index >= 0 && child_index < (int)down.size());
+  PT(Down) down = cdata->modify_down();
+  nassertv(child_index >= 0 && child_index < (int)down->size());
   
   
-  PT(PandaNode) child_node = down[child_index].get_child();
+  PT(PandaNode) child_node = (*down)[child_index].get_child();
   CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, 
   CDStageWriter cdata_child(child_node->_cycler, pipeline_stage, 
                                  current_thread);
                                  current_thread);
-  Up &up = *cdata_child->modify_up();
+  PT(Up) up = cdata_child->modify_up();
 
 
-  down.erase(down.begin() + child_index);
-  int num_erased = up.erase(UpConnection(this));
+  down->erase(down->begin() + child_index);
+  int num_erased = up->erase(UpConnection(this));
   nassertv(num_erased == 1);
   nassertv(num_erased == 1);
 
 
   sever_connection(this, child_node, pipeline_stage, current_thread);
   sever_connection(this, child_node, pipeline_stage, current_thread);
@@ -931,9 +931,9 @@ remove_all_children(Thread *current_thread) {
   // We have to do this for each upstream pipeline stage.
   // We have to do this for each upstream pipeline stage.
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
     CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
     CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
-    Down &down = *cdata->modify_down();
+    PT(Down) down = cdata->modify_down();
     Down::iterator di;
     Down::iterator di;
-    for (di = down.begin(); di != down.end(); ++di) {
+    for (di = down->begin(); di != down->end(); ++di) {
       PT(PandaNode) child_node = (*di).get_child();
       PT(PandaNode) child_node = (*di).get_child();
       CDStageWriter cdata_child(child_node->_cycler, pipeline_stage,
       CDStageWriter cdata_child(child_node->_cycler, pipeline_stage,
                                      current_thread);
                                      current_thread);
@@ -942,7 +942,7 @@ remove_all_children(Thread *current_thread) {
       sever_connection(this, child_node, pipeline_stage, current_thread);
       sever_connection(this, child_node, pipeline_stage, current_thread);
       child_node->parents_changed();
       child_node->parents_changed();
     }
     }
-    down.clear();
+    down->clear();
     
     
     Down &stashed = *cdata->modify_stashed();
     Down &stashed = *cdata->modify_stashed();
     for (di = stashed.begin(); di != stashed.end(); ++di) {
     for (di = stashed.begin(); di != stashed.end(); ++di) {
@@ -2482,9 +2482,9 @@ void PandaNode::
 r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
 r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
                 Thread *current_thread) {
                 Thread *current_thread) {
   CDReader from_cdata(from->_cycler, current_thread);
   CDReader from_cdata(from->_cycler, current_thread);
-  const Down &from_down = *from_cdata->get_down();
+  CPT(Down) from_down = from_cdata->get_down();
   Down::const_iterator di;
   Down::const_iterator di;
-  for (di = from_down.begin(); di != from_down.end(); ++di) {
+  for (di = from_down->begin(); di != from_down->end(); ++di) {
     int sort = (*di).get_sort();
     int sort = (*di).get_sort();
     PandaNode *source_child = (*di).get_child();
     PandaNode *source_child = (*di).get_child();
     PT(PandaNode) dest_child;
     PT(PandaNode) dest_child;
@@ -2524,51 +2524,6 @@ set_cull_callback() {
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::do_find_child
-//       Access: Private
-//  Description: The private implementation of find_child().
-////////////////////////////////////////////////////////////////////
-int PandaNode::
-do_find_child(PandaNode *node, const CData *cdata) const {
-  nassertr(node != (PandaNode *)NULL, -1);
-
-  // We have to search for the child by brute force, since we don't
-  // know what sort index it was added as.
-  const Down &down = *cdata->get_down();
-  Down::const_iterator di;
-  for (di = down.begin(); di != down.end(); ++di) {
-    if ((*di).get_child() == node) {
-      return di - down.begin();
-    }
-  }
-
-  return -1;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::do_find_stashed
-//       Access: Private
-//  Description: The private implementation of find_stashed().
-////////////////////////////////////////////////////////////////////
-int PandaNode::
-do_find_stashed(PandaNode *node, const CData *cdata) const {
-  nassertr(node != (PandaNode *)NULL, -1);
-
-  // We have to search for the child by brute force, since we don't
-  // know what sort index it was added as.
-  const Down &stashed = *cdata->get_stashed();
-  Down::const_iterator di;
-  for (di = stashed.begin(); di != stashed.end(); ++di) {
-    if ((*di).get_child() == node) {
-      return di - stashed.begin();
-    }
-  }
-
-  return -1;
-}
-
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::stage_remove_child
 //     Function: PandaNode::stage_remove_child
 //       Access: Private
 //       Access: Private
@@ -2590,21 +2545,21 @@ stage_remove_child(PandaNode *child_node, int pipeline_stage,
     return false;
     return false;
   }
   }
 
 
-  int child_index = do_find_child(child_node, cdata);
+  PT(Down) down = cdata->modify_down();
+  int child_index = do_find_child(child_node, down);
   if (child_index >= 0) {
   if (child_index >= 0) {
     // The child exists; remove it.
     // The child exists; remove it.
-    Down &down = *cdata->modify_down();
-    down.erase(down.begin() + child_index);
+    down->erase(down->begin() + child_index);
     int num_erased = cdata_child->modify_up()->erase(UpConnection(this));
     int num_erased = cdata_child->modify_up()->erase(UpConnection(this));
     nassertr(num_erased == 1, false);
     nassertr(num_erased == 1, false);
     return true;
     return true;
   }
   }
 
 
-  int stashed_index = do_find_stashed(child_node, cdata);
+  PT(Down) stashed = cdata->modify_stashed();
+  int stashed_index = do_find_child(child_node, stashed);
   if (stashed_index >= 0) {
   if (stashed_index >= 0) {
     // The child has been stashed; remove it.
     // The child has been stashed; remove it.
-    Down &stashed = *cdata->modify_down();
-    stashed.erase(stashed.begin() + stashed_index);
+    stashed->erase(stashed->begin() + stashed_index);
     int num_erased = cdata_child->modify_up()->erase(UpConnection(this));
     int num_erased = cdata_child->modify_up()->erase(UpConnection(this));
     nassertr(num_erased == 1, false);
     nassertr(num_erased == 1, false);
     return true;
     return true;
@@ -2652,20 +2607,22 @@ stage_replace_child(PandaNode *orig_child, PandaNode *new_child,
       sever_connection(this, new_child, pipeline_stage, current_thread);
       sever_connection(this, new_child, pipeline_stage, current_thread);
     }
     }
     
     
-    int child_index = do_find_child(orig_child, cdata);
+    PT(Down) down = cdata->modify_down();
+    int child_index = do_find_child(orig_child, down);
     if (child_index >= 0) {
     if (child_index >= 0) {
       // The child exists; replace it.
       // The child exists; replace it.
-      DownConnection &down = (*cdata->modify_down())[child_index];
-      nassertr(down.get_child() == orig_child, false);
-      down.set_child(new_child);
+      DownConnection &dc = (*down)[child_index];
+      nassertr(dc.get_child() == orig_child, false);
+      dc.set_child(new_child);
       
       
     } else {
     } else {
-      int stashed_index = do_find_stashed(orig_child, cdata);
+      PT(Down) stashed = cdata->modify_stashed();
+      int stashed_index = do_find_child(orig_child, stashed);
       if (stashed_index >= 0) {
       if (stashed_index >= 0) {
 	// The child has been stashed; remove it.
 	// The child has been stashed; remove it.
-	DownConnection &down = (*cdata->modify_stashed())[stashed_index];
-	nassertr(down.get_child() == orig_child, false);
-	down.set_child(new_child);
+	DownConnection &dc = (*stashed)[stashed_index];
+	nassertr(dc.get_child() == orig_child, false);
+	dc.set_child(new_child);
 	
 	
       } else {
       } else {
 	// Never heard of this child.  This shouldn't be possible, because
 	// Never heard of this child.  This shouldn't be possible, because
@@ -2827,19 +2784,19 @@ detach_one_stage(NodePathComponent *child, int pipeline_stage,
     // has been stashed.
     // has been stashed.
     Down::iterator di;
     Down::iterator di;
     bool found = false;
     bool found = false;
-    Down &down = *cdata_parent->modify_down();
-    for (di = down.begin(); di != down.end(); ++di) {
+    PT(Down) down = cdata_parent->modify_down();
+    for (di = down->begin(); di != down->end(); ++di) {
       if ((*di).get_child() == child_node) {
       if ((*di).get_child() == child_node) {
-	down.erase(di);
+	down->erase(di);
 	found = true;
 	found = true;
         break;
         break;
       }
       }
     }
     }
     if (!found) {
     if (!found) {
-      Down &stashed = *cdata_parent->modify_stashed();
-      for (di = stashed.begin(); di != stashed.end(); ++di) {
+      PT(Down) stashed = cdata_parent->modify_stashed();
+      for (di = stashed->begin(); di != stashed->end(); ++di) {
         if ((*di).get_child() == child_node) {
         if ((*di).get_child() == child_node) {
-          stashed.erase(di);
+          stashed->erase(di);
           found = true;
           found = true;
           break;
           break;
         }
         }
@@ -3121,8 +3078,8 @@ r_get_generic_component(bool accept_ambiguity, bool &ambiguity_detected,
         << " parents; choosing arbitrary path to root.\n";
         << " parents; choosing arbitrary path to root.\n";
     }
     }
     ambiguity_detected = true;
     ambiguity_detected = true;
-    const Up &up = *cdata->get_up();
-    parent_node = up[0].get_parent();
+    CPT(Up) up = cdata->get_up();
+    parent_node = (*up)[0].get_parent();
   }
   }
 
 
   // Now that the lock is released, it's safe to recurse.
   // Now that the lock is released, it's safe to recurse.
@@ -3286,6 +3243,27 @@ r_list_descendants(ostream &out, int indent_level) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::do_find_child
+//       Access: Private
+//  Description: The private implementation of find_child().
+////////////////////////////////////////////////////////////////////
+int PandaNode::
+do_find_child(PandaNode *node, const PandaNode::Down *down) const {
+  nassertr(node != (PandaNode *)NULL, -1);
+
+  // We have to search for the child by brute force, since we don't
+  // know what sort index it was added as.
+  Down::const_iterator di;
+  for (di = down->begin(); di != down->end(); ++di) {
+    if ((*di).get_child() == node) {
+      return di - down->begin();
+    }
+  }
+
+  return -1;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::update_bounds
 //     Function: PandaNode::update_bounds
 //       Access: Private
 //       Access: Private

+ 15 - 15
panda/src/pgraph/pandaNode.h

@@ -46,6 +46,8 @@
 #include "deletedChain.h"
 #include "deletedChain.h"
 #include "pandaNodeChain.h"
 #include "pandaNodeChain.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
+#include "copyOnWriteObject.h"
+#include "copyOnWritePointer.h"
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
 
 
@@ -300,8 +302,6 @@ private:
   class CData;
   class CData;
 
 
   INLINE int do_find_parent(PandaNode *node, const CData *cdata) const;
   INLINE int do_find_parent(PandaNode *node, const CData *cdata) const;
-  int do_find_child(PandaNode *node, const CData *cdata) const;
-  int do_find_stashed(PandaNode *node, const CData *cdata) const;
   bool stage_remove_child(PandaNode *child_node, int pipeline_stage,
   bool stage_remove_child(PandaNode *child_node, int pipeline_stage,
                           Thread *current_thread);
                           Thread *current_thread);
   bool stage_replace_child(PandaNode *orig_child, PandaNode *new_child,
   bool stage_replace_child(PandaNode *orig_child, PandaNode *new_child,
@@ -365,7 +365,7 @@ public:
   };
   };
 
 
 private:
 private:
-  typedef RefCountObj< ov_multiset<DownConnection> > Down;
+  typedef CopyOnWriteObj< ov_multiset<DownConnection> > Down;
 
 
   class EXPCL_PANDA UpConnection {
   class EXPCL_PANDA UpConnection {
   public:
   public:
@@ -378,7 +378,7 @@ private:
     // children do not circularly reference each other.
     // children do not circularly reference each other.
     PandaNode *_parent;
     PandaNode *_parent;
   };
   };
-  typedef RefCountObj< ov_set<UpConnection> > Up;
+  typedef CopyOnWriteObj< ov_set<UpConnection> > Up;
 
 
   // We also maintain a set of NodePathComponents in the node.  This
   // We also maintain a set of NodePathComponents in the node.  This
   // represents the set of instances of this node that we have
   // represents the set of instances of this node that we have
@@ -389,8 +389,7 @@ private:
 
 
   // We don't cycle the set of Paths, since these are across all
   // We don't cycle the set of Paths, since these are across all
   // threads.  A NodePathComponent, once created, is always associated
   // threads.  A NodePathComponent, once created, is always associated
-  // with the same node.  We do, however, protect the Paths under a
-  // mutex.
+  // with the same node.  We do, however, protect the Paths under a mutex.
   Paths _paths;
   Paths _paths;
   Mutex _paths_lock;
   Mutex _paths_lock;
 
 
@@ -521,19 +520,19 @@ private:
     void fillin_down_list(Down &down_list,
     void fillin_down_list(Down &down_list,
                           DatagramIterator &scan, BamReader *manager);
                           DatagramIterator &scan, BamReader *manager);
 
 
-    INLINE const Down *get_down() const;
-    INLINE Down *modify_down();
-    INLINE const Down *get_stashed() const;
-    INLINE Down *modify_stashed();
-    INLINE const Up *get_up() const;
-    INLINE Up *modify_up();
+    INLINE CPT(Down) get_down() const;
+    INLINE PT(Down) modify_down();
+    INLINE CPT(Down) get_stashed() const;
+    INLINE PT(Down) modify_stashed();
+    INLINE CPT(Up) get_up() const;
+    INLINE PT(Up) modify_up();
 
 
   private:
   private:
     // We store the child lists by reference, so we can copy them
     // We store the child lists by reference, so we can copy them
     // quickly.  We perform copy-on-write when necessary.
     // quickly.  We perform copy-on-write when necessary.
-    PT(Down) _down;
-    PT(Down) _stashed;
-    PT(Up) _up;
+    COWPT(Down) _down;
+    COWPT(Down) _stashed;
+    COWPT(Up) _up;
     
     
   public:
   public:
     static TypeHandle get_class_type() {
     static TypeHandle get_class_type() {
@@ -554,6 +553,7 @@ private:
   typedef CycleDataStageReader<CData> CDStageReader;
   typedef CycleDataStageReader<CData> CDStageReader;
   typedef CycleDataStageWriter<CData> CDStageWriter;
   typedef CycleDataStageWriter<CData> CDStageWriter;
 
 
+  int do_find_child(PandaNode *node, const Down *down) const;
   CDStageWriter update_bounds(int pipeline_stage, CDLockedStageReader &cdata);
   CDStageWriter update_bounds(int pipeline_stage, CDLockedStageReader &cdata);
 
 
   static DrawMask _overall_bit;
   static DrawMask _overall_bit;

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

@@ -46,6 +46,7 @@
     pipelineCyclerTrivialImpl.h pipelineCyclerTrivialImpl.I \
     pipelineCyclerTrivialImpl.h pipelineCyclerTrivialImpl.I \
     pipelineCyclerTrueImpl.h pipelineCyclerTrueImpl.I \
     pipelineCyclerTrueImpl.h pipelineCyclerTrueImpl.I \
     pmutex.h pmutex.I \
     pmutex.h pmutex.I \
+    pythonThread.h \
     reMutex.I reMutex.h \
     reMutex.I reMutex.h \
     reMutexDirect.h reMutexDirect.I \
     reMutexDirect.h reMutexDirect.I \
     reMutexHolder.I reMutexHolder.h \
     reMutexHolder.I reMutexHolder.h \
@@ -88,6 +89,7 @@
     pipelineCyclerTrivialImpl.cxx \
     pipelineCyclerTrivialImpl.cxx \
     pipelineCyclerTrueImpl.cxx \
     pipelineCyclerTrueImpl.cxx \
     pmutex.cxx \
     pmutex.cxx \
+    pythonThread.cxx \
     reMutex.cxx \
     reMutex.cxx \
     reMutexDirect.cxx \
     reMutexDirect.cxx \
     reMutexHolder.cxx \
     reMutexHolder.cxx \
@@ -132,6 +134,7 @@
     pipelineCyclerTrivialImpl.h pipelineCyclerTrivialImpl.I \
     pipelineCyclerTrivialImpl.h pipelineCyclerTrivialImpl.I \
     pipelineCyclerTrueImpl.h pipelineCyclerTrueImpl.I \
     pipelineCyclerTrueImpl.h pipelineCyclerTrueImpl.I \
     pmutex.h pmutex.I \
     pmutex.h pmutex.I \
+    pythonThread.h \
     reMutex.I reMutex.h \
     reMutex.I reMutex.h \
     reMutexDirect.h reMutexDirect.I \
     reMutexDirect.h reMutexDirect.I \
     reMutexHolder.I reMutexHolder.h \
     reMutexHolder.I reMutexHolder.h \

+ 3 - 3
panda/src/pipeline/conditionVar.I

@@ -19,7 +19,7 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVar::Constructor
 //     Function: ConditionVar::Constructor
-//       Access: Public
+//       Access: Published
 //  Description: You must pass in a Mutex to the condition variable
 //  Description: You must pass in a Mutex to the condition variable
 //               constructor.  This mutex may be shared by other
 //               constructor.  This mutex may be shared by other
 //               condition variables, if desired.  It is the caller's
 //               condition variables, if desired.  It is the caller's
@@ -39,7 +39,7 @@ ConditionVar(Mutex &mutex) :
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVar::Destructor
 //     Function: ConditionVar::Destructor
-//       Access: Public
+//       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE ConditionVar::
 INLINE ConditionVar::
@@ -86,7 +86,7 @@ signal_all() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVar::get_mutex
 //     Function: ConditionVar::get_mutex
-//       Access: Public
+//       Access: Published
 //  Description: Returns the mutex associated with this condition
 //  Description: Returns the mutex associated with this condition
 //               variable.
 //               variable.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/pipeline/conditionVar.h

@@ -51,7 +51,7 @@ class EXPCL_PANDA ConditionVar : public ConditionVarDebug
 class EXPCL_PANDA ConditionVar : public ConditionVarDirect
 class EXPCL_PANDA ConditionVar : public ConditionVarDirect
 #endif  // DEBUG_THREADS
 #endif  // DEBUG_THREADS
 {
 {
-public:
+PUBLISHED:
   INLINE ConditionVar(Mutex &mutex);
   INLINE ConditionVar(Mutex &mutex);
   INLINE ~ConditionVar();
   INLINE ~ConditionVar();
 private:
 private:
@@ -68,7 +68,7 @@ private:
   // method.
   // method.
   INLINE void signal_all();
   INLINE void signal_all();
 
 
-public:
+PUBLISHED:
   INLINE Mutex &get_mutex() const;
   INLINE Mutex &get_mutex() const;
 };
 };
 
 

+ 1 - 1
panda/src/pipeline/conditionVarDebug.I

@@ -42,7 +42,7 @@ operator = (const ConditionVarDebug &copy) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarDebug::get_mutex
 //     Function: ConditionVarDebug::get_mutex
-//       Access: Public
+//       Access: Published
 //  Description: Returns the mutex associated with this condition
 //  Description: Returns the mutex associated with this condition
 //               variable.
 //               variable.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 3 - 3
panda/src/pipeline/conditionVarDebug.cxx

@@ -51,7 +51,7 @@ ConditionVarDebug::
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarDebug::wait
 //     Function: ConditionVarDebug::wait
-//       Access: Public
+//       Access: Published
 //  Description: Waits on the condition.  The caller must already be
 //  Description: Waits on the condition.  The caller must already be
 //               holding the lock associated with the condition
 //               holding the lock associated with the condition
 //               variable before calling this function.
 //               variable before calling this function.
@@ -106,7 +106,7 @@ wait() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarDebug::signal
 //     Function: ConditionVarDebug::signal
-//       Access: Public
+//       Access: Published
 //  Description: Informs one of the other threads who are currently
 //  Description: Informs one of the other threads who are currently
 //               blocked on wait() that the relevant condition has
 //               blocked on wait() that the relevant condition has
 //               changed.  If multiple threads are currently waiting,
 //               changed.  If multiple threads are currently waiting,
@@ -144,7 +144,7 @@ signal() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarDebug::output
 //     Function: ConditionVarDebug::output
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: This method is declared virtual in ConditionVarDebug,
 //  Description: This method is declared virtual in ConditionVarDebug,
 //               but non-virtual in ConditionVarDirect.
 //               but non-virtual in ConditionVarDirect.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/pipeline/conditionVarDebug.h

@@ -45,7 +45,7 @@ private:
   INLINE ConditionVarDebug(const ConditionVarDebug &copy);
   INLINE ConditionVarDebug(const ConditionVarDebug &copy);
   INLINE void operator = (const ConditionVarDebug &copy);
   INLINE void operator = (const ConditionVarDebug &copy);
 
 
-public:
+PUBLISHED:
   INLINE MutexDebug &get_mutex() const;
   INLINE MutexDebug &get_mutex() const;
 
 
   void wait();
   void wait();

+ 2 - 2
panda/src/pipeline/conditionVarDirect.h

@@ -45,10 +45,10 @@ private:
   INLINE ConditionVarDirect(const ConditionVarDirect &copy);
   INLINE ConditionVarDirect(const ConditionVarDirect &copy);
   INLINE void operator = (const ConditionVarDirect &copy);
   INLINE void operator = (const ConditionVarDirect &copy);
 
 
-public:
+PUBLISHED:
   INLINE MutexDirect &get_mutex() const;
   INLINE MutexDirect &get_mutex() const;
 
 
-  INLINE void wait();
+  BLOCKING INLINE void wait();
   INLINE void signal();
   INLINE void signal();
   void output(ostream &out) const;
   void output(ostream &out) const;
 
 

+ 3 - 3
panda/src/pipeline/conditionVarFull.I

@@ -19,7 +19,7 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFull::Constructor
 //     Function: ConditionVarFull::Constructor
-//       Access: Public
+//       Access: Published
 //  Description: You must pass in a Mutex to the condition variable
 //  Description: You must pass in a Mutex to the condition variable
 //               constructor.  This mutex may be shared by other
 //               constructor.  This mutex may be shared by other
 //               condition variables, if desired.  It is the caller's
 //               condition variables, if desired.  It is the caller's
@@ -39,7 +39,7 @@ ConditionVarFull(Mutex &mutex) :
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFull::Destructor
 //     Function: ConditionVarFull::Destructor
-//       Access: Public
+//       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE ConditionVarFull::
 INLINE ConditionVarFull::
@@ -74,7 +74,7 @@ operator = (const ConditionVarFull &copy) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFull::get_mutex
 //     Function: ConditionVarFull::get_mutex
-//       Access: Public
+//       Access: Published
 //  Description: Returns the mutex associated with this condition
 //  Description: Returns the mutex associated with this condition
 //               variable.
 //               variable.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/pipeline/conditionVarFull.h

@@ -57,14 +57,14 @@ class EXPCL_PANDA ConditionVarFull : public ConditionVarFullDebug
 class EXPCL_PANDA ConditionVarFull : public ConditionVarFullDirect
 class EXPCL_PANDA ConditionVarFull : public ConditionVarFullDirect
 #endif  // DEBUG_THREADS
 #endif  // DEBUG_THREADS
 {
 {
-public:
+PUBLISHED:
   INLINE ConditionVarFull(Mutex &mutex);
   INLINE ConditionVarFull(Mutex &mutex);
   INLINE ~ConditionVarFull();
   INLINE ~ConditionVarFull();
 private:
 private:
   INLINE ConditionVarFull(const ConditionVarFull &copy);
   INLINE ConditionVarFull(const ConditionVarFull &copy);
   INLINE void operator = (const ConditionVarFull &copy);
   INLINE void operator = (const ConditionVarFull &copy);
 
 
-public:
+PUBLISHED:
   INLINE Mutex &get_mutex() const;
   INLINE Mutex &get_mutex() const;
 };
 };
 
 

+ 1 - 1
panda/src/pipeline/conditionVarFullDebug.I

@@ -42,7 +42,7 @@ operator = (const ConditionVarFullDebug &copy) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFullDebug::get_mutex
 //     Function: ConditionVarFullDebug::get_mutex
-//       Access: Public
+//       Access: Published
 //  Description: Returns the mutex associated with this condition
 //  Description: Returns the mutex associated with this condition
 //               variable.
 //               variable.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 4 - 4
panda/src/pipeline/conditionVarFullDebug.cxx

@@ -51,7 +51,7 @@ ConditionVarFullDebug::
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFullDebug::wait
 //     Function: ConditionVarFullDebug::wait
-//       Access: Public
+//       Access: Published
 //  Description: Waits on the condition.  The caller must already be
 //  Description: Waits on the condition.  The caller must already be
 //               holding the lock associated with the condition
 //               holding the lock associated with the condition
 //               variable before calling this function.
 //               variable before calling this function.
@@ -106,7 +106,7 @@ wait() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFullDebug::signal
 //     Function: ConditionVarFullDebug::signal
-//       Access: Public
+//       Access: Published
 //  Description: Informs one of the other threads who are currently
 //  Description: Informs one of the other threads who are currently
 //               blocked on wait() that the relevant condition has
 //               blocked on wait() that the relevant condition has
 //               changed.  If multiple threads are currently waiting,
 //               changed.  If multiple threads are currently waiting,
@@ -144,7 +144,7 @@ signal() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFullDebug::signal
 //     Function: ConditionVarFullDebug::signal
-//       Access: Public
+//       Access: Published
 //  Description: Informs all of the other threads who are currently
 //  Description: Informs all of the other threads who are currently
 //               blocked on wait() that the relevant condition has
 //               blocked on wait() that the relevant condition has
 //               changed.
 //               changed.
@@ -179,7 +179,7 @@ signal_all() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFullDebug::output
 //     Function: ConditionVarFullDebug::output
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description: This method is declared virtual in ConditionVarFullDebug,
 //  Description: This method is declared virtual in ConditionVarFullDebug,
 //               but non-virtual in ConditionVarFullDirect.
 //               but non-virtual in ConditionVarFullDirect.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/pipeline/conditionVarFullDebug.h

@@ -45,7 +45,7 @@ private:
   INLINE ConditionVarFullDebug(const ConditionVarFullDebug &copy);
   INLINE ConditionVarFullDebug(const ConditionVarFullDebug &copy);
   INLINE void operator = (const ConditionVarFullDebug &copy);
   INLINE void operator = (const ConditionVarFullDebug &copy);
 
 
-public:
+PUBLISHED:
   INLINE MutexDebug &get_mutex() const;
   INLINE MutexDebug &get_mutex() const;
 
 
   void wait();
   void wait();

+ 4 - 4
panda/src/pipeline/conditionVarFullDirect.I

@@ -68,7 +68,7 @@ operator = (const ConditionVarFullDirect &copy) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFullDirect::get_mutex
 //     Function: ConditionVarFullDirect::get_mutex
-//       Access: Public
+//       Access: Published
 //  Description: Returns the mutex associated with this condition
 //  Description: Returns the mutex associated with this condition
 //               variable.
 //               variable.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -79,7 +79,7 @@ get_mutex() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFullDirect::wait
 //     Function: ConditionVarFullDirect::wait
-//       Access: Public
+//       Access: Published
 //  Description: Waits on the condition.  The caller must already be
 //  Description: Waits on the condition.  The caller must already be
 //               holding the lock associated with the condition
 //               holding the lock associated with the condition
 //               variable before calling this function.
 //               variable before calling this function.
@@ -110,7 +110,7 @@ wait() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFullDirect::signal
 //     Function: ConditionVarFullDirect::signal
-//       Access: Public
+//       Access: Published
 //  Description: Informs one of the other threads who are currently
 //  Description: Informs one of the other threads who are currently
 //               blocked on wait() that the relevant condition has
 //               blocked on wait() that the relevant condition has
 //               changed.  If multiple threads are currently waiting,
 //               changed.  If multiple threads are currently waiting,
@@ -133,7 +133,7 @@ signal() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ConditionVarFullDirect::signal_all
 //     Function: ConditionVarFullDirect::signal_all
-//       Access: Public
+//       Access: Published
 //  Description: Informs all of the other threads who are currently
 //  Description: Informs all of the other threads who are currently
 //               blocked on wait() that the relevant condition has
 //               blocked on wait() that the relevant condition has
 //               changed.
 //               changed.

+ 1 - 1
panda/src/pipeline/conditionVarFullDirect.h

@@ -45,7 +45,7 @@ private:
   INLINE ConditionVarFullDirect(const ConditionVarFullDirect &copy);
   INLINE ConditionVarFullDirect(const ConditionVarFullDirect &copy);
   INLINE void operator = (const ConditionVarFullDirect &copy);
   INLINE void operator = (const ConditionVarFullDirect &copy);
 
 
-public:
+PUBLISHED:
   INLINE MutexDirect &get_mutex() const;
   INLINE MutexDirect &get_mutex() const;
 
 
   INLINE void wait();
   INLINE void wait();

+ 5 - 13
panda/src/pipeline/config_pipeline.cxx

@@ -20,6 +20,7 @@
 #include "mainThread.h"
 #include "mainThread.h"
 #include "externalThread.h"
 #include "externalThread.h"
 #include "thread.h"
 #include "thread.h"
+#include "pythonThread.h"
 #include "pandaSystem.h"
 #include "pandaSystem.h"
 
 
 #include "dconfig.h"
 #include "dconfig.h"
@@ -45,18 +46,6 @@ ConfigVariableInt thread_stack_size
           "created for each newly-created thread.  Not all thread "
           "created for each newly-created thread.  Not all thread "
           "implementations respect this value."));
           "implementations respect this value."));
 
 
-ConfigVariableBool threads_always_global
-("threads-always-global", false,
- PRC_DESC("Set this true to override the application's 'global' setting "
-          "for each created thread, and create every thread with global "
-          "true.  Also see threads-never-global."));
-
-ConfigVariableBool threads_never_global
-("threads-never-global", false,
- PRC_DESC("Set this true to override the application's 'global' setting "
-          "for each created thread, and create every thread with global "
-          "false.  Also see threads-always-global."));
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libpipeline
 //     Function: init_libpipeline
 //  Description: Initializes the library.  This must be called at
 //  Description: Initializes the library.  This must be called at
@@ -76,11 +65,14 @@ init_libpipeline() {
   MainThread::init_type();
   MainThread::init_type();
   ExternalThread::init_type();
   ExternalThread::init_type();
   Thread::init_type();
   Thread::init_type();
+#ifdef HAVE_PYTHON
+  PythonThread::init_type();
+#endif  // HAVE_PYTHON
 
 
 #ifdef HAVE_THREADS
 #ifdef HAVE_THREADS
  {
  {
   PandaSystem *ps = PandaSystem::get_global_ptr();
   PandaSystem *ps = PandaSystem::get_global_ptr();
   ps->add_system("threads");
   ps->add_system("threads");
   }
   }
-#endif
+#endif  // HAVE_THREADS
 }
 }

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

@@ -31,8 +31,6 @@ NotifyCategoryDecl(thread, EXPCL_PANDA, EXPTP_PANDA);
 
 
 extern ConfigVariableBool support_threads;
 extern ConfigVariableBool support_threads;
 extern ConfigVariableInt thread_stack_size;
 extern ConfigVariableInt thread_stack_size;
-extern ConfigVariableBool threads_always_global;
-extern ConfigVariableBool threads_never_global;
 
 
 extern EXPCL_PANDA void init_libpipeline();
 extern EXPCL_PANDA void init_libpipeline();
 
 

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

@@ -16,6 +16,8 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+#ifndef CPPPARSER
+
 #ifdef DO_PIPELINING
 #ifdef DO_PIPELINING
 // This is the implementation for full support of pipelining (as well
 // This is the implementation for full support of pipelining (as well
 // as the sanity-check only implementation).
 // as the sanity-check only implementation).
@@ -240,3 +242,4 @@ get_current_thread() const {
 }
 }
 
 
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER

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

@@ -50,6 +50,9 @@
 template<class CycleDataType>
 template<class CycleDataType>
 class CycleDataLockedReader {
 class CycleDataLockedReader {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   INLINE CycleDataLockedReader(const PipelineCycler<CycleDataType> &cycler,
   INLINE CycleDataLockedReader(const PipelineCycler<CycleDataType> &cycler,
                                Thread *current_thread = Thread::get_current_thread());
                                Thread *current_thread = Thread::get_current_thread());
   INLINE CycleDataLockedReader(const CycleDataLockedReader<CycleDataType> &copy);
   INLINE CycleDataLockedReader(const CycleDataLockedReader<CycleDataType> &copy);
@@ -74,6 +77,7 @@ private:
   // This is all we need for the trivial, do-nothing implementation.
   // This is all we need for the trivial, do-nothing implementation.
   const CycleDataType *_pointer;
   const CycleDataType *_pointer;
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER
 };
 };
 
 
 #include "cycleDataLockedReader.I"
 #include "cycleDataLockedReader.I"

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

@@ -16,6 +16,7 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+#ifndef CPPPARSER
 
 
 #ifdef DO_PIPELINING
 #ifdef DO_PIPELINING
 // This is the implementation for full support of pipelining (as well
 // This is the implementation for full support of pipelining (as well
@@ -245,3 +246,4 @@ get_current_thread() const {
 }
 }
 
 
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER

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

@@ -33,6 +33,9 @@
 template<class CycleDataType>
 template<class CycleDataType>
 class CycleDataLockedStageReader {
 class CycleDataLockedStageReader {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   INLINE CycleDataLockedStageReader(const PipelineCycler<CycleDataType> &cycler, 
   INLINE CycleDataLockedStageReader(const PipelineCycler<CycleDataType> &cycler, 
                                     int stage, Thread *current_thread = Thread::get_current_thread());
                                     int stage, Thread *current_thread = Thread::get_current_thread());
   INLINE CycleDataLockedStageReader(const CycleDataLockedStageReader<CycleDataType> &copy);
   INLINE CycleDataLockedStageReader(const CycleDataLockedStageReader<CycleDataType> &copy);
@@ -57,6 +60,7 @@ private:
   // This is all we need for the trivial, do-nothing implementation.
   // This is all we need for the trivial, do-nothing implementation.
   const CycleDataType *_pointer;
   const CycleDataType *_pointer;
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER
 };
 };
 
 
 #include "cycleDataLockedStageReader.I"
 #include "cycleDataLockedStageReader.I"

+ 3 - 0
panda/src/pipeline/cycleDataReader.I

@@ -16,6 +16,8 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+#ifndef CPPPARSER
+
 #ifdef DO_PIPELINING
 #ifdef DO_PIPELINING
 // This is the implementation for full support of pipelining (as well
 // This is the implementation for full support of pipelining (as well
 // as the sanity-check only implementation).
 // as the sanity-check only implementation).
@@ -193,3 +195,4 @@ get_current_thread() const {
 }
 }
 
 
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER

+ 4 - 0
panda/src/pipeline/cycleDataReader.h

@@ -44,6 +44,9 @@
 template<class CycleDataType>
 template<class CycleDataType>
 class CycleDataReader {
 class CycleDataReader {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   INLINE CycleDataReader(const PipelineCycler<CycleDataType> &cycler,
   INLINE CycleDataReader(const PipelineCycler<CycleDataType> &cycler,
                          Thread *current_thread = Thread::get_current_thread());
                          Thread *current_thread = Thread::get_current_thread());
   INLINE CycleDataReader(const CycleDataReader<CycleDataType> &copy);
   INLINE CycleDataReader(const CycleDataReader<CycleDataType> &copy);
@@ -67,6 +70,7 @@ private:
   // This is all we need for the trivial, do-nothing implementation.
   // This is all we need for the trivial, do-nothing implementation.
   const CycleDataType *_pointer;
   const CycleDataType *_pointer;
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER
 };
 };
 
 
 #include "cycleDataReader.I"
 #include "cycleDataReader.I"

+ 2 - 0
panda/src/pipeline/cycleDataStageReader.I

@@ -16,6 +16,7 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+#ifndef CPPPARSER
 
 
 #ifdef DO_PIPELINING
 #ifdef DO_PIPELINING
 // This is the implementation for full support of pipelining (as well
 // This is the implementation for full support of pipelining (as well
@@ -198,3 +199,4 @@ get_current_thread() const {
 }
 }
 
 
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER

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

@@ -33,6 +33,9 @@
 template<class CycleDataType>
 template<class CycleDataType>
 class CycleDataStageReader {
 class CycleDataStageReader {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   INLINE CycleDataStageReader(const PipelineCycler<CycleDataType> &cycler, 
   INLINE CycleDataStageReader(const PipelineCycler<CycleDataType> &cycler, 
                               int stage, Thread *current_thread = Thread::get_current_thread());
                               int stage, Thread *current_thread = Thread::get_current_thread());
   INLINE CycleDataStageReader(const CycleDataStageReader<CycleDataType> &copy);
   INLINE CycleDataStageReader(const CycleDataStageReader<CycleDataType> &copy);
@@ -56,6 +59,7 @@ private:
   // This is all we need for the trivial, do-nothing implementation.
   // This is all we need for the trivial, do-nothing implementation.
   const CycleDataType *_pointer;
   const CycleDataType *_pointer;
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER
 };
 };
 
 
 #include "cycleDataStageReader.I"
 #include "cycleDataStageReader.I"

+ 3 - 0
panda/src/pipeline/cycleDataStageWriter.I

@@ -16,6 +16,8 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+#ifndef CPPPARSER
+
 #ifdef DO_PIPELINING
 #ifdef DO_PIPELINING
 // This is the implementation for full support of pipelining (as well
 // This is the implementation for full support of pipelining (as well
 // as the sanity-check only implementation).
 // as the sanity-check only implementation).
@@ -331,3 +333,4 @@ get_current_thread() const {
 }
 }
 
 
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER

+ 4 - 0
panda/src/pipeline/cycleDataStageWriter.h

@@ -37,6 +37,9 @@
 template<class CycleDataType>
 template<class CycleDataType>
 class CycleDataStageWriter {
 class CycleDataStageWriter {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
   INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
                               Thread *current_thread = Thread::get_current_thread());
                               Thread *current_thread = Thread::get_current_thread());
   INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
   INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
@@ -71,6 +74,7 @@ private:
   // This is all we need for the trivial, do-nothing implementation.
   // This is all we need for the trivial, do-nothing implementation.
   CycleDataType *_pointer;
   CycleDataType *_pointer;
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER
 };
 };
 
 
 #include "cycleDataStageWriter.I"
 #include "cycleDataStageWriter.I"

+ 3 - 0
panda/src/pipeline/cycleDataWriter.I

@@ -16,6 +16,8 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+#ifndef CPPPARSER
+
 #ifdef DO_PIPELINING
 #ifdef DO_PIPELINING
 // This is the implementation for full support of pipelining (as well
 // This is the implementation for full support of pipelining (as well
 // as the sanity-check only implementation).
 // as the sanity-check only implementation).
@@ -333,3 +335,4 @@ get_current_thread() const {
 }
 }
 
 
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER

+ 4 - 0
panda/src/pipeline/cycleDataWriter.h

@@ -41,6 +41,9 @@
 template<class CycleDataType>
 template<class CycleDataType>
 class CycleDataWriter {
 class CycleDataWriter {
 public:
 public:
+  // By hiding this template from interrogate, we improve compile-time
+  // speed and memory utilization.
+#ifndef CPPPARSER
   INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
   INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
                          Thread *current_thread = Thread::get_current_thread());
                          Thread *current_thread = Thread::get_current_thread());
   INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, bool force_to_0,
   INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, bool force_to_0,
@@ -70,6 +73,7 @@ private:
   // This is all we need for the trivial, do-nothing implementation.
   // This is all we need for the trivial, do-nothing implementation.
   CycleDataType *_pointer;
   CycleDataType *_pointer;
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING
+#endif  // CPPPARSER
 };
 };
 
 
 #include "cycleDataWriter.I"
 #include "cycleDataWriter.I"

+ 5 - 5
panda/src/pipeline/mutexDebug.I

@@ -39,7 +39,7 @@ operator = (const MutexDebug &copy) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexDebug::lock
 //     Function: MutexDebug::lock
-//       Access: Public
+//       Access: Published
 //  Description: Grabs the mutex if it is available.  If it is not
 //  Description: Grabs the mutex if it is available.  If it is not
 //               available, blocks until it becomes available, then
 //               available, blocks until it becomes available, then
 //               grabs it.  In either case, the function does not
 //               grabs it.  In either case, the function does not
@@ -62,7 +62,7 @@ lock() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexDebug::lock
 //     Function: MutexDebug::lock
-//       Access: Public
+//       Access: Published
 //  Description: This variant on lock() accepts the current thread as
 //  Description: This variant on lock() accepts the current thread as
 //               a parameter, if it is already known, as an
 //               a parameter, if it is already known, as an
 //               optimization.
 //               optimization.
@@ -79,7 +79,7 @@ lock(Thread *current_thread) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexDebug::elevate_lock
 //     Function: MutexDebug::elevate_lock
-//       Access: Public
+//       Access: Published
 //  Description: This method increments the lock count, assuming the
 //  Description: This method increments the lock count, assuming the
 //               calling thread already holds the lock.  After this
 //               calling thread already holds the lock.  After this
 //               call, release() will need to be called one additional
 //               call, release() will need to be called one additional
@@ -107,7 +107,7 @@ elevate_lock() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexDebug::release
 //     Function: MutexDebug::release
-//       Access: Public
+//       Access: Published
 //  Description: Releases the mutex.  It is an error to call this if
 //  Description: Releases the mutex.  It is an error to call this if
 //               the mutex was not already locked.
 //               the mutex was not already locked.
 //
 //
@@ -125,7 +125,7 @@ release() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexDebug::debug_is_locked
 //     Function: MutexDebug::debug_is_locked
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if the current thread has locked the
 //  Description: Returns true if the current thread has locked the
 //               Mutex, false otherwise.  This method is only intended
 //               Mutex, false otherwise.  This method is only intended
 //               for use in debugging, hence the method name; in the
 //               for use in debugging, hence the method name; in the

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

@@ -40,9 +40,9 @@ private:
   INLINE MutexDebug(const MutexDebug &copy);
   INLINE MutexDebug(const MutexDebug &copy);
   INLINE void operator = (const MutexDebug &copy);
   INLINE void operator = (const MutexDebug &copy);
 
 
-public:
-  INLINE void lock() const;
-  INLINE void lock(Thread *current_thread) const;
+PUBLISHED:
+  BLOCKING INLINE void lock() const;
+  BLOCKING INLINE void lock(Thread *current_thread) const;
   INLINE void elevate_lock() const;
   INLINE void elevate_lock() const;
   INLINE void release() const;
   INLINE void release() const;
   INLINE bool debug_is_locked() const;
   INLINE bool debug_is_locked() const;

+ 3 - 3
panda/src/pipeline/mutexDirect.I

@@ -57,7 +57,7 @@ operator = (const MutexDirect &copy) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexDirect::lock
 //     Function: MutexDirect::lock
-//       Access: Public
+//       Access: Published
 //  Description: Grabs the mutex if it is available.  If it is not
 //  Description: Grabs the mutex if it is available.  If it is not
 //               available, blocks until it becomes available, then
 //               available, blocks until it becomes available, then
 //               grabs it.  In either case, the function does not
 //               grabs it.  In either case, the function does not
@@ -78,7 +78,7 @@ lock() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexDirect::release
 //     Function: MutexDirect::release
-//       Access: Public
+//       Access: Published
 //  Description: Releases the mutex.  It is an error to call this if
 //  Description: Releases the mutex.  It is an error to call this if
 //               the mutex was not already locked.
 //               the mutex was not already locked.
 //
 //
@@ -94,7 +94,7 @@ release() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexDirect::debug_is_locked
 //     Function: MutexDirect::debug_is_locked
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if the current thread has locked the
 //  Description: Returns true if the current thread has locked the
 //               Mutex, false otherwise.  This method is only intended
 //               Mutex, false otherwise.  This method is only intended
 //               for use in debugging, hence the method name; in the
 //               for use in debugging, hence the method name; in the

+ 2 - 2
panda/src/pipeline/mutexDirect.h

@@ -41,8 +41,8 @@ private:
   INLINE MutexDirect(const MutexDirect &copy);
   INLINE MutexDirect(const MutexDirect &copy);
   INLINE void operator = (const MutexDirect &copy);
   INLINE void operator = (const MutexDirect &copy);
 
 
-public:
-  INLINE void lock() const;
+PUBLISHED:
+  BLOCKING INLINE void lock() const;
   INLINE void release() const;
   INLINE void release() const;
   INLINE bool debug_is_locked() const;
   INLINE bool debug_is_locked() const;
 
 

+ 1 - 0
panda/src/pipeline/pipeline_composite2.cxx

@@ -9,6 +9,7 @@
 #include "pipelineCyclerTrivialImpl.cxx"
 #include "pipelineCyclerTrivialImpl.cxx"
 #include "pipelineCyclerTrueImpl.cxx"
 #include "pipelineCyclerTrueImpl.cxx"
 #include "pmutex.cxx"
 #include "pmutex.cxx"
+#include "pythonThread.cxx"
 #include "reMutex.cxx"
 #include "reMutex.cxx"
 #include "reMutexDirect.cxx"
 #include "reMutexDirect.cxx"
 #include "reMutexHolder.cxx"
 #include "reMutexHolder.cxx"

+ 3 - 3
panda/src/pipeline/pmutex.I

@@ -19,7 +19,7 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Mutex::Constructor
 //     Function: Mutex::Constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Mutex::
 INLINE Mutex::
@@ -47,7 +47,7 @@ Mutex(const char *)
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Mutex::Constructor
 //     Function: Mutex::Constructor
-//       Access: Public
+//       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Mutex::
 INLINE Mutex::
@@ -61,7 +61,7 @@ Mutex(const string &)
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Mutex::Destructor
 //     Function: Mutex::Destructor
-//       Access: Public
+//       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Mutex::
 INLINE Mutex::

+ 3 - 1
panda/src/pipeline/pmutex.h

@@ -48,9 +48,11 @@ class EXPCL_PANDA Mutex : public MutexDebug
 class EXPCL_PANDA Mutex : public MutexDirect
 class EXPCL_PANDA Mutex : public MutexDirect
 #endif  // DEBUG_THREADS
 #endif  // DEBUG_THREADS
 {
 {
-public:
+PUBLISHED:
   INLINE Mutex();
   INLINE Mutex();
+public:
   INLINE Mutex(const char *name);
   INLINE Mutex(const char *name);
+PUBLISHED:
   INLINE Mutex(const string &name);
   INLINE Mutex(const string &name);
   INLINE ~Mutex();
   INLINE ~Mutex();
 private:
 private:

+ 165 - 0
panda/src/pipeline/pythonThread.cxx

@@ -0,0 +1,165 @@
+// Filename: pythonThread.cxx
+// Created by:  drose (13Apr07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "pythonThread.h"
+#include "pnotify.h"
+
+#ifdef HAVE_PYTHON
+
+TypeHandle PythonThread::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonThread::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+PythonThread::
+PythonThread(PyObject *function, PyObject *args,
+             const string &name, const string &sync_name) :
+  Thread(name, sync_name)
+{
+  _result = NULL;
+
+  _function = function;
+  if (!PyCallable_Check(_function)) {
+    nassert_raise("Invalid function passed to PythonThread constructor");
+  }
+  Py_INCREF(_function);
+
+  if (args == Py_None) {
+    // None means no arguments; create an empty tuple.
+    _args = PyTuple_New(0);
+  } else {
+    _args = NULL;
+    if (PySequence_Check(args)) {
+      _args = PySequence_Tuple(args);
+    }
+    if (_args == NULL) {
+      nassert_raise("Invalid args passed to PythonThread constructor");
+    }
+  }
+
+  // Ensure that the Python threading system is initialized and ready
+  // to go.
+  PyEval_InitThreads();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonThread::Destructor
+//       Access: Published, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+PythonThread::
+~PythonThread() {
+  Py_DECREF(_function);
+  Py_XDECREF(_args);
+  Py_XDECREF(_result);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonThread::join
+//       Access: Published
+//  Description: Blocks the calling process until the thread
+//               terminates.  If the thread has already terminated,
+//               this returns immediately.
+//
+//               The PythonThread flavor of this function returns the
+//               same value returned by the thread function.
+////////////////////////////////////////////////////////////////////
+PyObject *PythonThread::
+join() {
+  Thread::join();
+
+  if (_result == NULL) {
+    // No result; return None.
+    return Py_BuildValue("");
+  }
+
+  Py_INCREF(_result);
+  return _result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonThread::thread_main
+//       Access: Protected, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void PythonThread::
+thread_main() {
+  // Create a new Python thread state data structure, so Python can
+  // properly lock itself.
+  PyGILState_STATE gstate;
+  gstate = PyGILState_Ensure();
+
+  // Call the user's function.
+  _result = PyObject_Call(_function, _args, NULL);
+  if (_result == (PyObject *)NULL && PyErr_Occurred()) {
+    handle_python_exception();
+  }
+
+  // Release the thread state data structure.
+  PyGILState_Release(gstate);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PythonThread::handle_python_exception
+//       Access: Private
+//  Description: Called when a Python exception is raised during
+//               processing of a thread.  Gets the error string and
+//               passes it back to the calling Python process in a
+//               sensible way.
+////////////////////////////////////////////////////////////////////
+void PythonThread::
+handle_python_exception() {
+  PyObject *exc, *val, *tb;
+  PyErr_Fetch(&exc, &val, &tb);
+
+  ostringstream strm;
+  strm << "\n";
+
+  PyObject *exc_name = PyObject_GetAttrString(exc, "__name__");
+  if (exc_name == (PyObject *)NULL) {
+    PyObject *exc_str = PyObject_Str(exc);
+    strm << PyString_AsString(exc_str);
+    Py_DECREF(exc_str);
+  } else {
+    PyObject *exc_str = PyObject_Str(exc_name);
+    strm << PyString_AsString(exc_str);
+    Py_DECREF(exc_str);
+    Py_DECREF(exc_name);
+  }
+  Py_DECREF(exc);
+
+  if (val != (PyObject *)NULL) {
+    PyObject *val_str = PyObject_Str(val);
+    strm << ": " << PyString_AsString(val_str);
+    Py_DECREF(val_str);
+    Py_DECREF(val);
+  }
+  if (tb != (PyObject *)NULL) {
+    Py_DECREF(tb);
+  }
+
+  strm << "\nException occurred within thread " << get_name();
+  string message = strm.str();
+  nout << message << "\n";
+
+  nassert_raise(message);
+}
+
+#endif  // HAVE_PYTHON

+ 80 - 0
panda/src/pipeline/pythonThread.h

@@ -0,0 +1,80 @@
+// Filename: pythonThread.h
+// Created by:  drose (13Apr07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PYTHONTHREAD_H
+#define PYTHONTHREAD_H
+
+#include "pandabase.h"
+
+#include "thread.h"
+
+#ifdef HAVE_PYTHON
+
+#undef HAVE_LONG_LONG  // NSPR and Python both define this.
+#undef _POSIX_C_SOURCE
+#include <Python.h>
+
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//       Class : PythonThread
+// Description : This class is exposed to Python to allow creation of
+//               a Panda thread from the Python level.  It will spawn
+//               a thread that executes an arbitrary Python functor.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA PythonThread : public Thread {
+PUBLISHED:
+  PythonThread(PyObject *function, PyObject *args,
+               const string &name, const string &sync_name);
+  virtual ~PythonThread();
+
+  BLOCKING PyObject *join();
+
+protected:
+  virtual void thread_main();
+
+private:
+  void handle_python_exception();
+
+private:
+  PyObject *_function;
+  PyObject *_args;
+  PyObject *_result;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    Thread::init_type();
+    register_type(_type_handle, "PythonThread",
+                  Thread::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+#endif  // HAVE_PYTHON
+
+#endif
+

+ 3 - 1
panda/src/pipeline/reMutex.h

@@ -40,9 +40,11 @@ class EXPCL_PANDA ReMutex : public MutexDebug
 class EXPCL_PANDA ReMutex : public ReMutexDirect
 class EXPCL_PANDA ReMutex : public ReMutexDirect
 #endif  // DEBUG_THREADS
 #endif  // DEBUG_THREADS
 {
 {
-public:
+PUBLISHED:
   INLINE ReMutex();
   INLINE ReMutex();
+public:
   INLINE ReMutex(const char *name);
   INLINE ReMutex(const char *name);
+PUBLISHED:
   INLINE ReMutex(const string &name);
   INLINE ReMutex(const string &name);
   INLINE ~ReMutex();
   INLINE ~ReMutex();
 private:
 private:

+ 5 - 5
panda/src/pipeline/reMutexDirect.I

@@ -69,7 +69,7 @@ operator = (const ReMutexDirect &copy) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReMutexDirect::lock
 //     Function: ReMutexDirect::lock
-//       Access: Public
+//       Access: Published
 //  Description: Grabs the reMutex if it is available.  If it is not
 //  Description: Grabs the reMutex if it is available.  If it is not
 //               available, blocks until it becomes available, then
 //               available, blocks until it becomes available, then
 //               grabs it.  In either case, the function does not
 //               grabs it.  In either case, the function does not
@@ -94,7 +94,7 @@ lock() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReMutexDirect::lock
 //     Function: ReMutexDirect::lock
-//       Access: Public
+//       Access: Published
 //  Description: This variant on lock() accepts the current thread as
 //  Description: This variant on lock() accepts the current thread as
 //               a parameter, if it is already known, as an
 //               a parameter, if it is already known, as an
 //               optimization.
 //               optimization.
@@ -111,7 +111,7 @@ lock(Thread *current_thread) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReMutexDirect::elevate_lock
 //     Function: ReMutexDirect::elevate_lock
-//       Access: Public
+//       Access: Published
 //  Description: This method increments the lock count, assuming the
 //  Description: This method increments the lock count, assuming the
 //               calling thread already holds the lock.  After this
 //               calling thread already holds the lock.  After this
 //               call, release() will need to be called one additional
 //               call, release() will need to be called one additional
@@ -136,7 +136,7 @@ elevate_lock() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReMutexDirect::release
 //     Function: ReMutexDirect::release
-//       Access: Public
+//       Access: Published
 //  Description: Releases the reMutex.  It is an error to call this if
 //  Description: Releases the reMutex.  It is an error to call this if
 //               the reMutex was not already locked.
 //               the reMutex was not already locked.
 //
 //
@@ -156,7 +156,7 @@ release() const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReMutexDirect::debug_is_locked
 //     Function: ReMutexDirect::debug_is_locked
-//       Access: Public
+//       Access: Published
 //  Description: Returns true if the current thread has locked the
 //  Description: Returns true if the current thread has locked the
 //               ReMutex, false otherwise.  This method is only intended
 //               ReMutex, false otherwise.  This method is only intended
 //               for use in debugging, hence the method name; in the
 //               for use in debugging, hence the method name; in the

+ 1 - 1
panda/src/pipeline/reMutexDirect.cxx

@@ -21,7 +21,7 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReMutexDirect::output
 //     Function: ReMutexDirect::output
-//       Access: Public
+//       Access: Published
 //  Description: This method is declared virtual in MutexDebug, but
 //  Description: This method is declared virtual in MutexDebug, but
 //               non-virtual in ReMutexDirect.
 //               non-virtual in ReMutexDirect.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -39,9 +39,9 @@ private:
   INLINE ReMutexDirect(const ReMutexDirect &copy);
   INLINE ReMutexDirect(const ReMutexDirect &copy);
   INLINE void operator = (const ReMutexDirect &copy);
   INLINE void operator = (const ReMutexDirect &copy);
 
 
-public:
-  INLINE void lock() const;
-  INLINE void lock(Thread *current_thread) const;
+PUBLISHED:
+  BLOCKING INLINE void lock() const;
+  BLOCKING INLINE void lock(Thread *current_thread) const;
   INLINE void elevate_lock() const;
   INLINE void elevate_lock() const;
   INLINE void release() const;
   INLINE void release() const;
 
 

+ 4 - 14
panda/src/pipeline/thread.cxx

@@ -128,12 +128,8 @@ output(ostream &out) const {
 //               returns.
 //               returns.
 //
 //
 //               priority is intended as a hint to the relative
 //               priority is intended as a hint to the relative
-//               importance of this thread, and global should be set
-//               true if the thread will perform a lot of blocking
-//               I/O, or false otherwise (see the NSPR documentation
-//               on global vs. local threads for more on this).  Both
-//               of these parameters may be ignored by the thread
-//               implementation.
+//               importance of this thread.  This may be ignored by
+//               the thread implementation.
 //
 //
 //               joinable should be set true if you intend to call
 //               joinable should be set true if you intend to call
 //               join() to wait for the thread to terminate, or false
 //               join() to wait for the thread to terminate, or false
@@ -143,7 +139,7 @@ output(ostream &out) const {
 //               successfully started, false otherwise.
 //               successfully started, false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool Thread::
 bool Thread::
-start(ThreadPriority priority, bool global, bool joinable) {
+start(ThreadPriority priority, bool joinable) {
   nassertr(!_started, false);
   nassertr(!_started, false);
 
 
   if (!support_threads) {
   if (!support_threads) {
@@ -152,13 +148,7 @@ start(ThreadPriority priority, bool global, bool joinable) {
     return false;
     return false;
   }
   }
 
 
-  if (threads_always_global) {
-    global = true;
-  } else if (threads_never_global) {
-    global = false;
-  }
-
-  _started = _impl.start(priority, global, joinable);
+  _started = _impl.start(priority, joinable);
 
 
   if (!_started) {
   if (!_started) {
     thread_cat.warning()
     thread_cat.warning()

+ 4 - 4
panda/src/pipeline/thread.h

@@ -75,17 +75,17 @@ PUBLISHED:
   INLINE static Thread *get_current_thread();
   INLINE static Thread *get_current_thread();
   INLINE static int get_current_pipeline_stage();
   INLINE static int get_current_pipeline_stage();
   INLINE static bool is_threading_supported();
   INLINE static bool is_threading_supported();
-  INLINE static void sleep(double seconds);
+  BLOCKING INLINE static void sleep(double seconds);
 
 
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
 
 
-public:
   INLINE bool is_started() const;
   INLINE bool is_started() const;
 
 
-  bool start(ThreadPriority priority, bool global, bool joinable);
+  bool start(ThreadPriority priority, bool joinable);
   INLINE void interrupt();
   INLINE void interrupt();
-  INLINE void join();
+  BLOCKING INLINE void join();
 
 
+public:
   INLINE static void prepare_for_exit();
   INLINE static void prepare_for_exit();
 
 
 private:
 private:

+ 1 - 1
panda/src/pipeline/threadDummyImpl.I

@@ -41,7 +41,7 @@ INLINE ThreadDummyImpl::
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool ThreadDummyImpl::
 INLINE bool ThreadDummyImpl::
-start(ThreadPriority, bool, bool) {
+start(ThreadPriority, bool) {
   return false;
   return false;
 }
 }
 
 

+ 1 - 1
panda/src/pipeline/threadDummyImpl.h

@@ -46,7 +46,7 @@ public:
   INLINE ThreadDummyImpl(Thread *parent_obj);
   INLINE ThreadDummyImpl(Thread *parent_obj);
   INLINE ~ThreadDummyImpl();
   INLINE ~ThreadDummyImpl();
 
 
-  INLINE bool start(ThreadPriority priority, bool global, bool joinable);
+  INLINE bool start(ThreadPriority priority, bool joinable);
   INLINE void interrupt();
   INLINE void interrupt();
   INLINE void join();
   INLINE void join();
 
 

+ 1 - 1
panda/src/pipeline/threadPosixImpl.cxx

@@ -57,7 +57,7 @@ ThreadPosixImpl::
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool ThreadPosixImpl::
 bool ThreadPosixImpl::
-start(ThreadPriority priority, bool global, bool joinable) {
+start(ThreadPriority priority, bool joinable) {
   _mutex.lock();
   _mutex.lock();
   if (thread_cat.is_debug()) {
   if (thread_cat.is_debug()) {
     thread_cat.debug() << "Starting " << *_parent_obj << "\n";
     thread_cat.debug() << "Starting " << *_parent_obj << "\n";

+ 1 - 1
panda/src/pipeline/threadPosixImpl.h

@@ -41,7 +41,7 @@ public:
   INLINE ThreadPosixImpl(Thread *parent_obj);
   INLINE ThreadPosixImpl(Thread *parent_obj);
   ~ThreadPosixImpl();
   ~ThreadPosixImpl();
 
 
-  bool start(ThreadPriority priority, bool global, bool joinable);
+  bool start(ThreadPriority priority, bool joinable);
   void interrupt();
   void interrupt();
   void join();
   void join();
 
 

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

@@ -21,6 +21,7 @@
 
 
 #include "pandabase.h"
 #include "pandabase.h"
 
 
+BEGIN_PUBLISH
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 // An enumerated type used by Thread to specify a suggested relative
 // An enumerated type used by Thread to specify a suggested relative
 // priority for a particular thread.
 // priority for a particular thread.
@@ -31,6 +32,7 @@ enum ThreadPriority {
   TP_high,
   TP_high,
   TP_urgent
   TP_urgent
 };
 };
+END_PUBLISH
 
 
 
 
 #endif
 #endif

+ 1 - 1
panda/src/pipeline/threadWin32Impl.cxx

@@ -48,7 +48,7 @@ ThreadWin32Impl::
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool ThreadWin32Impl::
 bool ThreadWin32Impl::
-start(ThreadPriority priority, bool global, bool joinable) {
+start(ThreadPriority priority, bool joinable) {
   _mutex.lock();
   _mutex.lock();
   if (thread_cat.is_debug()) {
   if (thread_cat.is_debug()) {
     thread_cat.debug() << "Starting " << *_parent_obj << "\n";
     thread_cat.debug() << "Starting " << *_parent_obj << "\n";

+ 1 - 1
panda/src/pipeline/threadWin32Impl.h

@@ -40,7 +40,7 @@ public:
   INLINE ThreadWin32Impl(Thread *parent_obj);
   INLINE ThreadWin32Impl(Thread *parent_obj);
   ~ThreadWin32Impl();
   ~ThreadWin32Impl();
 
 
-  bool start(ThreadPriority priority, bool global, bool joinable);
+  bool start(ThreadPriority priority, bool joinable);
   void interrupt();
   void interrupt();
   void join();
   void join();
 
 

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

@@ -26,7 +26,8 @@
     cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \
     cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \
     clockObject.h clockObject.I \
     clockObject.h clockObject.I \
     collideMask.h \
     collideMask.h \
-    portalMask.h \
+    copyOnWriteObject.h copyOnWriteObject.I \
+    copyOnWritePointer.h copyOnWritePointer.I \
     compareTo.I compareTo.h \
     compareTo.I compareTo.h \
     config_util.N config_util.h configurable.h \
     config_util.N config_util.h configurable.h \
     datagramInputFile.I datagramInputFile.h \
     datagramInputFile.I datagramInputFile.h \
@@ -52,6 +53,7 @@
     mouseData.I mouseData.h nameUniquifier.I nameUniquifier.h \
     mouseData.I mouseData.h nameUniquifier.I nameUniquifier.h \
     nodeCachedReferenceCount.h nodeCachedReferenceCount.I \
     nodeCachedReferenceCount.h nodeCachedReferenceCount.I \
     nonDeletor.h \
     nonDeletor.h \
+    portalMask.h \
     pta_double.h \
     pta_double.h \
     pta_float.h pta_int.h \
     pta_float.h pta_int.h \
     sparseArray.I sparseArray.h \
     sparseArray.I sparseArray.h \
@@ -79,6 +81,8 @@
     buttonHandle.cxx buttonRegistry.cxx \
     buttonHandle.cxx buttonRegistry.cxx \
     cachedTypedWritableReferenceCount.cxx \
     cachedTypedWritableReferenceCount.cxx \
     clockObject.cxx \
     clockObject.cxx \
+    copyOnWriteObject.cxx \
+    copyOnWritePointer.cxx \
     config_util.cxx configurable.cxx \
     config_util.cxx configurable.cxx \
     datagramInputFile.cxx datagramOutputFile.cxx \
     datagramInputFile.cxx datagramOutputFile.cxx \
     deferredDeletor.cxx \
     deferredDeletor.cxx \
@@ -122,7 +126,9 @@
     buttonRegistry.h \
     buttonRegistry.h \
     cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \
     cachedTypedWritableReferenceCount.h cachedTypedWritableReferenceCount.I \
     clockObject.h clockObject.I \
     clockObject.h clockObject.I \
-    collideMask.h portalMask.h \
+    collideMask.h \
+    copyOnWriteObject.h copyOnWriteObject.I \
+    copyOnWritePointer.h copyOnWritePointer.I \
     compareTo.I compareTo.h \
     compareTo.I compareTo.h \
     config_util.h configurable.h factory.I factory.h \
     config_util.h configurable.h factory.I factory.h \
     datagramInputFile.I datagramInputFile.h \
     datagramInputFile.I datagramInputFile.h \
@@ -148,6 +154,7 @@
     nameUniquifier.I nameUniquifier.h \
     nameUniquifier.I nameUniquifier.h \
     nodeCachedReferenceCount.h nodeCachedReferenceCount.I \
     nodeCachedReferenceCount.h nodeCachedReferenceCount.I \
     nonDeletor.h \
     nonDeletor.h \
+    portalMask.h \
     pta_double.h \
     pta_double.h \
     pta_float.h pta_int.h pta_ushort.h \
     pta_float.h pta_int.h pta_ushort.h \
     sparseArray.I sparseArray.h \
     sparseArray.I sparseArray.h \

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

@@ -27,6 +27,7 @@
 #include "cachedTypedWritableReferenceCount.h"
 #include "cachedTypedWritableReferenceCount.h"
 #include "clockObject.h"
 #include "clockObject.h"
 #include "configurable.h"
 #include "configurable.h"
+#include "copyOnWriteObject.h"
 #include "datagram.h"
 #include "datagram.h"
 #include "factoryParam.h"
 #include "factoryParam.h"
 #include "namable.h"
 #include "namable.h"
@@ -43,6 +44,7 @@
 #include "mouseButton.h"
 #include "mouseButton.h"
 #include "deferredDeletor.h"
 #include "deferredDeletor.h"
 #include "nonDeletor.h"
 #include "nonDeletor.h"
+#include "spamDeletor.h"
 
 
 #include "dconfig.h"
 #include "dconfig.h"
 
 
@@ -90,6 +92,7 @@ ConfigureFn(config_util) {
   CachedTypedWritableReferenceCount::init_type();
   CachedTypedWritableReferenceCount::init_type();
   ClockObject::init_type();
   ClockObject::init_type();
   Configurable::init_type();
   Configurable::init_type();
+  CopyOnWriteObject::init_type();
   Datagram::init_type();
   Datagram::init_type();
   FactoryParam::init_type();
   FactoryParam::init_type();
   Namable::init_type();
   Namable::init_type();
@@ -108,6 +111,7 @@ ConfigureFn(config_util) {
 
 
   DeferredDeletor::register_deletor();
   DeferredDeletor::register_deletor();
   NonDeletor::register_deletor();
   NonDeletor::register_deletor();
+  SpamDeletor::register_deletor();
 
 
   register_type(BamReader::_remove_flag, "remove");
   register_type(BamReader::_remove_flag, "remove");
 
 

Some files were not shown because too many files changed in this diff