Browse Source

better thread safety

David Rose 19 years ago
parent
commit
b407c2929b

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

@@ -47,6 +47,8 @@
     stringDecoder.h stringDecoder.I \
     subStream.I subStream.h subStreamBuf.h \
     textEncoder.h textEncoder.I \
+    threadSafePointerTo.I threadSafePointerTo.h \
+    threadSafePointerToBase.I threadSafePointerToBase.h \
     tokenBoard.I \
     tokenBoard.h trueClock.I trueClock.h \
     typedReferenceCount.I typedReferenceCount.h typedef.h \
@@ -95,6 +97,8 @@
     stringDecoder.cxx \
     subStream.cxx subStreamBuf.cxx \
     textEncoder.cxx \
+    threadSafePointerTo.cxx \
+    threadSafePointerToBase.cxx \
     trueClock.cxx \
     typedReferenceCount.cxx \
     unicodeLatinMap.cxx \
@@ -149,6 +153,8 @@
     stringDecoder.h stringDecoder.I \
     subStream.I subStream.h subStreamBuf.h \
     textEncoder.h textEncoder.I \
+    threadSafePointerTo.I threadSafePointerTo.h \
+    threadSafePointerToBase.I threadSafePointerToBase.h \
     tokenBoard.I \
     tokenBoard.h trueClock.I trueClock.h \
     typedReferenceCount.I typedReferenceCount.h typedef.h \

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

@@ -27,3 +27,4 @@
 #include "pointerToBase.cxx"
 #include "pointerToVoid.cxx"
 #include "profileTimer.cxx"
+#include "pta_uchar.cxx"

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

@@ -1,4 +1,3 @@
-#include "pta_uchar.cxx"
 #include "ramfile.cxx"
 #include "referenceCount.cxx"
 #include "reversedNumericData.cxx"
@@ -8,6 +7,8 @@
 #include "subStream.cxx"
 #include "subStreamBuf.cxx"
 #include "textEncoder.cxx"
+#include "threadSafePointerTo.cxx"
+#include "threadSafePointerToBase.cxx"
 #include "trueClock.cxx"
 #include "typedReferenceCount.cxx"
 #include "unicodeLatinMap.cxx"

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

@@ -0,0 +1,259 @@
+// Filename: threadSafePointerTo.I
+// Created by:  drose (28Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerTo::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafePointerTo<T>::
+ThreadSafePointerTo(To *ptr) : ThreadSafePointerToBase<T>(ptr) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerTo::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafePointerTo<T>::
+ThreadSafePointerTo(const ThreadSafePointerTo<T> &copy) :
+  ThreadSafePointerToBase<T>((const ThreadSafePointerToBase<T> &)copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerTo::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafePointerTo<T>::
+~ThreadSafePointerTo() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerTo::Dereference operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE TYPENAME ThreadSafePointerTo<T>::To &ThreadSafePointerTo<T>::
+operator *() const {
+  return *((To *)AtomicAdjust::get_ptr(this->_void_ptr));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerTo::Member access operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE TYPENAME ThreadSafePointerTo<T>::To *ThreadSafePointerTo<T>::
+operator -> () const {
+  return (To *)AtomicAdjust::get_ptr(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerTo::Typecast operator
+//       Access: Public
+//  Description: We also have the typecast operator to automatically
+//               convert ThreadSafePointerTo's to the required kind of actual
+//               pointer.  This introduces ambiguities which the
+//               compiler will resolve one way or the other, but we
+//               don't care which way it goes because either will be
+//               correct.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafePointerTo<T>::
+operator TYPENAME ThreadSafePointerToBase<T>::To *() const {
+  return (To *)AtomicAdjust::get_ptr(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerTo::p
+//       Access: Published
+//  Description: Returns an ordinary pointer instead of a ThreadSafePointerTo.
+//               Useful to work around compiler problems, particularly
+//               for implicit upcasts.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE TYPENAME ThreadSafePointerTo<T>::To *ThreadSafePointerTo<T>::
+p() const {
+  return (To *)AtomicAdjust::get_ptr(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerTo::Assignment operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafePointerTo<T> &ThreadSafePointerTo<T>::
+operator = (To *ptr) {
+  reassign(ptr);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerTo::Assignment operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafePointerTo<T> &ThreadSafePointerTo<T>::
+operator = (const ThreadSafePointerTo<T> &copy) {
+  reassign((const ThreadSafePointerToBase<T> &)copy);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafeConstPointerTo<T>::
+ThreadSafeConstPointerTo(const TYPENAME ThreadSafeConstPointerTo<T>::To *ptr) :
+  ThreadSafePointerToBase<T>((TYPENAME ThreadSafeConstPointerTo<T>::To *)ptr)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafeConstPointerTo<T>::
+ThreadSafeConstPointerTo(const ThreadSafePointerTo<T> &copy) :
+  ThreadSafePointerToBase<T>((const ThreadSafePointerToBase<T> &)copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafeConstPointerTo<T>::
+~ThreadSafeConstPointerTo() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafeConstPointerTo<T>::
+ThreadSafeConstPointerTo(const ThreadSafeConstPointerTo<T> &copy) :
+  ThreadSafePointerToBase<T>((const ThreadSafePointerToBase<T> &)copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::Dereference operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE const TYPENAME ThreadSafeConstPointerTo<T>::To &ThreadSafeConstPointerTo<T>::
+operator *() const {
+  return *((To *)AtomicAdjust::get_ptr(this->_void_ptr));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::Member access operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE const TYPENAME ThreadSafeConstPointerTo<T>::To *ThreadSafeConstPointerTo<T>::
+operator -> () const {
+  return (To *)AtomicAdjust::get_ptr(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::Typecast operator
+//       Access: Public
+//  Description: We also have the typecast operator to automatically
+//               convert ThreadSafeConstPointerTo's to the required kind of actual
+//               pointer.  This introduces ambiguities which the
+//               compiler will resolve one way or the other, but we
+//               don't care which way it goes because either will be
+//               correct.
+////////////////////////////////////////////////////////////////////
+
+template<class T>
+INLINE ThreadSafeConstPointerTo<T>::
+operator const TYPENAME ThreadSafePointerToBase<T>::To *() const {
+  return (To *)AtomicAdjust::get_ptr(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::p
+//       Access: Published
+//  Description: Returns an ordinary pointer instead of a ThreadSafeConstPointerTo.
+//               Useful to work around compiler problems, particularly
+//               for implicit upcasts.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE const TYPENAME ThreadSafeConstPointerTo<T>::To *ThreadSafeConstPointerTo<T>::
+p() const {
+  return (To *)AtomicAdjust::get_ptr(this->_void_ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::Assignment operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafeConstPointerTo<T> &ThreadSafeConstPointerTo<T>::
+operator = (const To *ptr) {
+  reassign((To *)ptr);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::Assignment operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafeConstPointerTo<T> &ThreadSafeConstPointerTo<T>::
+operator = (const ThreadSafePointerTo<T> &copy) {
+  reassign((const ThreadSafePointerToBase<T> &)copy);
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafeConstPointerTo::Assignment operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafeConstPointerTo<T> &ThreadSafeConstPointerTo<T>::
+operator = (const ThreadSafeConstPointerTo<T> &copy) {
+  reassign((const ThreadSafePointerToBase<T> &)copy);
+  return *this;
+}

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

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

+ 113 - 0
panda/src/express/threadSafePointerTo.h

@@ -0,0 +1,113 @@
+// Filename: threadSafePointerTo.h
+// Created by:  drose (28Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 THREADSAFEPOINTERTO_H
+#define THREADSAFEPOINTERTO_H
+
+#include "pandabase.h"
+#include "threadSafePointerToBase.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ThreadSafePointerTo
+// Description : This works exactly like PointerTo, except that the
+//               object is designed to be thread-safe: it is generally
+//               safe to make unprotected assignments to this pointer,
+//               in the sense that the last assignment will win and
+//               the reference counts will be properly maintained.
+////////////////////////////////////////////////////////////////////
+template <class T>
+class ThreadSafePointerTo : public ThreadSafePointerToBase<T> {
+public:
+  typedef TYPENAME ThreadSafePointerToBase<T>::To To;
+PUBLISHED:
+  INLINE ThreadSafePointerTo(To *ptr = (To *)NULL);
+  INLINE ThreadSafePointerTo(const ThreadSafePointerTo<T> &copy);
+  INLINE ~ThreadSafePointerTo();
+
+public:
+  INLINE To &operator *() const;
+  INLINE To *operator -> () const;
+  INLINE operator TYPENAME ThreadSafePointerToBase<T>::To *() const;
+
+PUBLISHED:
+  // When downcasting to a derived class from a ThreadSafePointerTo<BaseClass>,
+  // C++ would normally require you to cast twice: once to an actual
+  // BaseClass pointer, and then again to your desired pointer.  You
+  // can use the handy function p() to avoid this first cast and make
+  // your code look a bit cleaner.
+
+  // e.g. instead of (MyType *)(BaseClass *)ptr, use (MyType *)ptr.p()
+
+  // If your base class is a derivative of TypedObject, you might want
+  // to use the DCAST macro defined in typedObject.h instead,
+  // e.g. DCAST(MyType, ptr).  This provides a clean downcast that
+  // doesn't require .p() or any double-casting, and it can be
+  // run-time checked for correctness.
+  INLINE To *p() const;
+
+  INLINE ThreadSafePointerTo<T> &operator = (To *ptr);
+  INLINE ThreadSafePointerTo<T> &operator = (const ThreadSafePointerTo<T> &copy);
+
+  // These functions normally wouldn't need to be redefined here, but
+  // we do so anyway just to help out interrogate (which doesn't seem
+  // to want to automatically export the ThreadSafePointerToBase class).  When
+  // this works again in interrogate, we can remove these.
+  INLINE bool is_null() const { return ThreadSafePointerToBase<T>::is_null(); }
+  INLINE void clear() { ThreadSafePointerToBase<T>::clear(); }
+};
+
+
+////////////////////////////////////////////////////////////////////
+//       Class : ThreadSafeConstPointerTo
+// Description : 
+////////////////////////////////////////////////////////////////////
+template <class T>
+class ThreadSafeConstPointerTo : public ThreadSafePointerToBase<T> {
+public:
+  typedef TYPENAME ThreadSafePointerToBase<T>::To To;
+PUBLISHED:
+  INLINE ThreadSafeConstPointerTo(const To *ptr = (const To *)NULL);
+  INLINE ThreadSafeConstPointerTo(const ThreadSafePointerTo<T> &copy);
+  INLINE ThreadSafeConstPointerTo(const ThreadSafeConstPointerTo<T> &copy);
+  INLINE ~ThreadSafeConstPointerTo();
+
+public:
+  INLINE const To &operator *() const;
+  INLINE const To *operator -> () const;
+  INLINE operator const TYPENAME ThreadSafePointerToBase<T>::To *() const;
+
+PUBLISHED:
+  INLINE const To *p() const;
+
+  INLINE ThreadSafeConstPointerTo<T> &operator = (const To *ptr);
+  INLINE ThreadSafeConstPointerTo<T> &operator = (const ThreadSafePointerTo<T> &copy);
+  INLINE ThreadSafeConstPointerTo<T> &operator = (const ThreadSafeConstPointerTo<T> &copy);
+
+  // This functions normally wouldn't need to be redefined here, but
+  // we do so anyway just to help out interrogate (which doesn't seem
+  // to want to automatically export the ThreadSafePointerToBase class).  When
+  // this works again in interrogate, we can remove this.
+  INLINE void clear() { ThreadSafePointerToBase<T>::clear(); }
+};
+
+#define TSPT(type) ThreadSafePointerTo< type >
+#define TSCPT(type) ThreadSafeConstPointerTo< type >
+
+#include "threadSafePointerTo.I"
+
+#endif

+ 157 - 0
panda/src/express/threadSafePointerToBase.I

@@ -0,0 +1,157 @@
+// Filename: threadSafePointerToBase.I
+// Created by:  drose (28Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerToBase::Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafePointerToBase<T>::
+ThreadSafePointerToBase(To *ptr) {
+  reassign(ptr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerToBase::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafePointerToBase<T>::
+ThreadSafePointerToBase(const ThreadSafePointerToBase<T> &copy) {
+  reassign(copy);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerToBase::Destructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE ThreadSafePointerToBase<T>::
+~ThreadSafePointerToBase() {
+  reassign((To *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerToBase::reassign
+//       Access: Protected
+//  Description: This is the main work of the ThreadSafePointerTo family.  When
+//               the pointer is reassigned, decrement the old
+//               reference count and increment the new one.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE void ThreadSafePointerToBase<T>::
+reassign(To *ptr) {
+  To *old_ptr = (To *)AtomicAdjust::get_ptr(_void_ptr);
+  if (ptr == old_ptr) {
+    return;
+  }
+
+#ifdef HAVE_THREADS
+  void *orig_ptr = AtomicAdjust::compare_and_exchange_ptr(_void_ptr, old_ptr, ptr);
+  while (orig_ptr != old_ptr) {
+    // Some other thread assigned it first.  Try again.
+    old_ptr = (To *)AtomicAdjust::get_ptr(_void_ptr);
+    if (ptr == old_ptr) {
+      return;
+    }
+
+    orig_ptr = AtomicAdjust::compare_and_exchange_ptr(_void_ptr, old_ptr, ptr);
+  }
+#else  // HAVE_THREADS
+  _void_ptr = ptr;
+#endif  // HAVE_THREADS
+
+  if (ptr != (To *)NULL) {
+    ptr->ref();
+#ifdef DO_MEMORY_USAGE
+    if (MemoryUsage::get_track_memory_usage()) {
+      update_type(ptr);
+    }
+#endif
+  }
+  
+  // Now delete the old pointer.
+  if (old_ptr != (To *)NULL) {
+    unref_delete(old_ptr);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerToBase::reassign
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE void ThreadSafePointerToBase<T>::
+reassign(const ThreadSafePointerToBase<To> &copy) {
+  reassign((To *)copy._void_ptr);
+}
+
+#ifdef DO_MEMORY_USAGE
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerToBase::update_type
+//       Access: Protected
+//  Description: Ensures that the MemoryUsage record for the pointer
+//               has the right type of object, if we know the type
+//               ourselves.
+////////////////////////////////////////////////////////////////////
+template<class T>
+void ThreadSafePointerToBase<T>::
+update_type(To *ptr) {
+  TypeHandle type = get_type_handle(To);
+  if (type == TypeHandle::none()) {
+    do_init_type(To);
+    type = get_type_handle(To);
+  }
+  if (type != TypeHandle::none()) {
+    MemoryUsage::update_type(ptr, type);
+  }
+}
+#endif  // DO_MEMORY_USAGE
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerToBase::clear
+//       Access: Published
+//  Description: A convenient way to set the ThreadSafePointerTo object to NULL.
+//               (Assignment to a NULL pointer also works, of course.)
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE void ThreadSafePointerToBase<T>::
+clear() {
+  reassign((To *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadSafePointerToBase::output
+//       Access: Published
+//  Description: A handy function to output ThreadSafePointerTo's as a hex
+//               pointer followed by a reference count.
+////////////////////////////////////////////////////////////////////
+template<class T>
+INLINE void ThreadSafePointerToBase<T>::
+output(ostream &out) const {
+  out << _void_ptr;
+  if (_void_ptr != (void *)NULL) {
+    out << ":" << ((To *)_void_ptr)->get_ref_count();
+  }
+}

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

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

+ 71 - 0
panda/src/express/threadSafePointerToBase.h

@@ -0,0 +1,71 @@
+// Filename: threadSafePointerToBase.h
+// Created by:  drose (28Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 THREADSAFEPOINTERTOBASE_H
+#define THREADSAFEPOINTERTOBASE_H
+
+#include "pandabase.h"
+#include "pointerToVoid.h"
+#include "referenceCount.h"
+#include "typedef.h"
+#include "memoryUsage.h"
+#include "config_express.h"
+#include "atomicAdjust.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ThreadSafePointerToBase
+// Description : This is the base class for ThreadSafePointerTo and
+//               ThreadSafeConstPointerTo.  Don't try to use it
+//               directly; use either derived class instead.
+////////////////////////////////////////////////////////////////////
+template <class T>
+class ThreadSafePointerToBase : public PointerToVoid {
+public:
+  typedef T To;
+
+protected:
+  INLINE ThreadSafePointerToBase(To *ptr);
+  INLINE ThreadSafePointerToBase(const ThreadSafePointerToBase<T> &copy);
+  INLINE ~ThreadSafePointerToBase();
+
+  INLINE void reassign(To *ptr);
+  INLINE void reassign(const ThreadSafePointerToBase<To> &copy);
+
+#ifdef DO_MEMORY_USAGE
+  void update_type(To *ptr);
+#endif  // DO_MEMORY_USAGE
+
+  // No assignment or retrieval functions are declared in
+  // ThreadSafePointerToBase, because we will have to specialize on const
+  // vs. non-const later.
+
+PUBLISHED:
+  INLINE void clear();
+
+  void output(ostream &out) const;
+};
+
+template<class T>
+INLINE ostream &operator <<(ostream &out, const ThreadSafePointerToBase<T> &pointer) {
+  pointer.output(out);
+  return out;
+}
+
+#include "threadSafePointerToBase.I"
+
+#endif

+ 4 - 0
panda/src/gobj/geom.cxx

@@ -26,6 +26,7 @@
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "boundingSphere.h"
+#include "mutexHolder.h"
 
 UpdateSeq Geom::_next_modified;
 PStatCollector Geom::_draw_primitive_setup_pcollector("Draw:Primitive:Setup");
@@ -801,6 +802,7 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 clear_cache() {
+  MutexHolder holder(_cache_lock);
   for (Cache::iterator ci = _cache.begin();
        ci != _cache.end();
        ++ci) {
@@ -823,6 +825,7 @@ clear_cache() {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 clear_cache_stage(Thread *current_thread) {
+  MutexHolder holder(_cache_lock);
   for (Cache::iterator ci = _cache.begin();
        ci != _cache.end();
        ++ci) {
@@ -1240,6 +1243,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 void Geom::CacheEntry::
 evict_callback() {
+  MutexHolder holder(_source->_cache_lock);
   Cache::iterator ci = _source->_cache.find(this);
   nassertv(ci != _source->_cache.end());
   nassertv((*ci) == this);

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

@@ -40,6 +40,7 @@
 #include "boundingVolume.h"
 #include "pStatCollector.h"
 #include "deletedChain.h"
+#include "pmutex.h"
 
 class GeomContext;
 class PreparedGraphicsObjects;
@@ -254,6 +255,7 @@ private:
   typedef CycleDataStageWriter<CData> CDStageWriter;
 
   Cache _cache;
+  Mutex _cache_lock;
 
   // This works just like the Texture contexts: each Geom keeps a
   // record of all the PGO objects that hold the Geom, and vice-versa.

+ 23 - 11
panda/src/gobj/geomMunger.cxx

@@ -117,22 +117,27 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
 
   Geom::CacheEntry temp_entry(source_data, this);
   temp_entry.local_object();
+
+  geom->_cache_lock.lock();
   Geom::Cache::const_iterator ci = geom->_cache.find(&temp_entry);
-  if (ci != geom->_cache.end()) {
+  if (ci == geom->_cache.end()) {
+    geom->_cache_lock.release();
+  } else {
     entry = (*ci);
+    geom->_cache_lock.release();
     nassertv(entry->_source == geom);
-
+    
     // Here's an element in the cache for this computation.  Record a
     // cache hit, so this element will stay in the cache a while
     // longer.
     entry->refresh(current_thread);
-
+    
     // Now check that it's fresh.
     Geom::CDCacheReader cdata(entry->_cycler, current_thread);
-    nassertv(cdata->_source == geom);
-    if (cdata->_geom_result != (Geom *)NULL &&
-	geom->get_modified(current_thread) <= cdata->_geom_result->get_modified(current_thread) &&
-	data->get_modified(current_thread) <= cdata->_data_result->get_modified(current_thread)) {
+    if (cdata->_source == geom &&
+        cdata->_geom_result != (Geom *)NULL &&
+        geom->get_modified(current_thread) <= cdata->_geom_result->get_modified(current_thread) &&
+        data->get_modified(current_thread) <= cdata->_data_result->get_modified(current_thread)) {
       // The cache entry is still good; use it.
       
       geom = cdata->_geom_result;
@@ -149,16 +154,23 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
   // Ok, invoke the munger.
   PStatTimer timer(_munge_pcollector, current_thread);
 
-  CPT(Geom) orig_geom = geom;
+  PT(Geom) orig_geom = (Geom *)geom.p();
   data = munge_data(data);
   munge_geom_impl(geom, data, current_thread);
 
   // Record the new result in the cache.
   if (entry == (Geom::CacheEntry *)NULL) {
     // Create a new entry for the result.
-    entry = new Geom::CacheEntry((Geom *)orig_geom.p(), source_data, this);
-    bool inserted = ((Geom *)orig_geom.p())->_cache.insert(entry).second;
-    nassertv(inserted);
+    entry = new Geom::CacheEntry(orig_geom, source_data, this);
+    {
+      MutexHolder holder(orig_geom->_cache_lock);
+      bool inserted = orig_geom->_cache.insert(entry).second;
+      if (!inserted) {
+        // Some other thread must have beat us to the punch.  Never
+        // mind.
+        return;
+      }
+    }
   
     // And tell the cache manager about the new entry.  (It might
     // immediately request a delete from the cache of the thing we

+ 20 - 4
panda/src/gobj/geomVertexData.cxx

@@ -677,9 +677,15 @@ convert_to(const GeomVertexFormat *new_format) const {
 
   CacheEntry temp_entry(new_format);
   temp_entry.local_object();
+
+  _cache_lock.lock();
   Cache::const_iterator ci = _cache.find(&temp_entry);
-  if (ci != _cache.end()) {
+  if (ci == _cache.end()) {
+    _cache_lock.release();
+
+  } else {
     entry = (*ci);
+    _cache_lock.release();
     nassertr(entry->_source == this, NULL);
 
     // Here's an element in the cache for this computation.  Record a
@@ -717,8 +723,15 @@ convert_to(const GeomVertexFormat *new_format) const {
   if (entry == (CacheEntry *)NULL) {
     // Create a new entry for the result.
     entry = new CacheEntry((GeomVertexData *)this, new_format);
-    bool inserted = ((GeomVertexData *)this)->_cache.insert(entry).second;
-    nassertr(inserted, new_data);
+    {
+      MutexHolder holder(_cache_lock);
+      bool inserted = ((GeomVertexData *)this)->_cache.insert(entry).second;
+      if (!inserted) {
+        // Some other thread must have beat us to the punch.  Never
+        // mind.
+        return new_data;
+      }
+    }
     
     // And tell the cache manager about the new entry.  (It might
     // immediately request a delete from the cache of the thing we
@@ -727,7 +740,7 @@ convert_to(const GeomVertexFormat *new_format) const {
   }
 
   // Finally, store the cached result on the entry.
-  CDCacheWriter cdata(entry->_cycler, true);
+  CDCacheWriter cdata(entry->_cycler, true, current_thread);
   cdata->_result = new_data;
 
   return new_data;
@@ -1095,6 +1108,7 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::
 clear_cache() {
+  MutexHolder holder(_cache_lock);
   for (Cache::iterator ci = _cache.begin();
        ci != _cache.end();
        ++ci) {
@@ -1117,6 +1131,7 @@ clear_cache() {
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::
 clear_cache_stage() {
+  MutexHolder holder(_cache_lock);
   for (Cache::iterator ci = _cache.begin();
        ci != _cache.end();
        ++ci) {
@@ -1471,6 +1486,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::CacheEntry::
 evict_callback() {
+  MutexHolder holder(_source->_cache_lock);
   Cache::iterator ci = _source->_cache.find(this);
   nassertv(ci != _source->_cache.end());
   nassertv((*ci) == this);

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

@@ -252,6 +252,7 @@ private:
   typedef CycleDataStageWriter<CData> CDStageWriter;
 
   Cache _cache;
+  Mutex _cache_lock;
 
 private:
   void update_animated_vertices(CData *cdata, Thread *current_thread);

+ 5 - 1
panda/src/pipeline/pipelineCyclerTrueImpl.I

@@ -70,7 +70,9 @@ INLINE const CycleData *PipelineCyclerTrueImpl::
 read(Thread *current_thread) const {
   TAU_PROFILE("const CycleData *PipelineCyclerTrueImpl::read(Thread *)", " ", TAU_USER);
   int pipeline_stage = current_thread->get_pipeline_stage();
+#ifdef _DEBUG
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+#endif
   _lock.lock(current_thread);
   return _data[pipeline_stage];
 }
@@ -238,7 +240,7 @@ increment_write(CycleData *pointer) const {
 INLINE void PipelineCyclerTrueImpl::
 release_write(CycleData *pointer) {
   TAU_PROFILE("void PipelineCyclerTrueImpl::release_write(CycleData *)", " ", TAU_USER);
-#ifdef NDEBUG
+#ifdef _DEBUG
   int pipeline_stage = Thread::get_current_pipeline_stage();
   return release_write_stage(pipeline_stage, pointer);
 #else
@@ -270,7 +272,9 @@ get_num_stages() {
 INLINE const CycleData *PipelineCyclerTrueImpl::
 read_stage(int pipeline_stage, Thread *current_thread) const {
   TAU_PROFILE("const CycleData *PipelineCyclerTrueImpl::read_stage(int)", " ", TAU_USER);
+#ifdef _DEBUG
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+#endif
   _lock.lock(current_thread);
   return _data[pipeline_stage];
 }