Browse Source

This should fix LP bug #954257; replace locked BamWriters mechanism with atomic list.

rdb 11 years ago
parent
commit
0181fae076
3 changed files with 108 additions and 31 deletions
  1. 5 13
      panda/src/putil/bamWriter.cxx
  2. 94 14
      panda/src/putil/typedWritable.cxx
  3. 9 4
      panda/src/putil/typedWritable.h

+ 5 - 13
panda/src/putil/bamWriter.cxx

@@ -61,12 +61,7 @@ BamWriter::
   StateMap::iterator si;
   for (si = _state_map.begin(); si != _state_map.end(); ++si) {
     TypedWritable *object = (TypedWritable *)(*si).first;
-    LightMutexHolder holder(TypedWritable::_bam_writers_lock);
-    nassertv(object->_bam_writers != (TypedWritable::BamWriters *)NULL);
-    TypedWritable::BamWriters::iterator wi = 
-      find(object->_bam_writers->begin(), object->_bam_writers->end(), this);
-    nassertv(wi != object->_bam_writers->end());
-    object->_bam_writers->erase(wi);
+    object->remove_bam_writer(this);
   }
 }
 
@@ -633,13 +628,10 @@ enqueue_object(const TypedWritable *object) {
     bool inserted =
       _state_map.insert(StateMap::value_type(object, StoreState(_next_object_id))).second;
     nassertr(inserted, false);
-    {
-      LightMutexHolder holder(TypedWritable::_bam_writers_lock);
-      if (object->_bam_writers == ((TypedWritable::BamWriters *)NULL)) {
-        ((TypedWritable *)object)->_bam_writers = new TypedWritable::BamWriters;
-      }
-      object->_bam_writers->push_back(this);
-    }
+
+    // Store ourselves on the TypedWritable so that we get notified
+    // when it destructs.
+    (const_cast<TypedWritable*>(object))->add_bam_writer(this);
     _next_object_id++;
 
   } else {

+ 94 - 14
panda/src/putil/typedWritable.cxx

@@ -20,8 +20,6 @@
 #include "lightMutexHolder.h"
 #include "bam.h"
 
-LightMutex TypedWritable::_bam_writers_lock;
-
 TypeHandle TypedWritable::_type_handle;
 TypedWritable* const TypedWritable::Null = (TypedWritable*)0L;
 
@@ -33,19 +31,22 @@ TypedWritable* const TypedWritable::Null = (TypedWritable*)0L;
 TypedWritable::
 ~TypedWritable() {
   // Remove the object pointer from the BamWriters that reference it.
-  if (_bam_writers != (BamWriters *)NULL) {
-    BamWriters temp;
-    {
-      LightMutexHolder holder(_bam_writers_lock);
-      _bam_writers->swap(temp);
-      delete _bam_writers;
-      _bam_writers = NULL;
-    }
-    BamWriters::iterator wi;
-    for (wi = temp.begin(); wi != temp.end(); ++wi) {
-      BamWriter *writer = (*wi);
-      writer->object_destructs(this);
+  BamWriterLink *link;
+  do {
+    link = (BamWriterLink *)AtomicAdjust::get_ptr(_bam_writers);
+    if (link == (BamWriterLink *)NULL) {
+      // List is unlocked and empty - no writers to remove.
+      return;
     }
+    link = (BamWriterLink *)(((uintptr_t)link) & ~(uintptr_t)0x1);
+  } while (link != AtomicAdjust::
+    compare_and_exchange_ptr(_bam_writers, (void *)link, (void *)NULL));
+
+  while (link != (BamWriterLink *)NULL) {
+    BamWriterLink *next_link = link->_next;
+    link->_writer->object_destructs(this);
+    delete link;
+    link = next_link;
   }
 }
 
@@ -308,3 +309,82 @@ decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr,
   ref_ptr->unref();
   return true;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::add_bam_writer
+//       Access: Private
+//  Description: Called by the BamWriter to add itself to this
+//               TypedWritable's list of BamWriters, so that it can
+//               receive notification whenever this object destructs.
+//               This method may be safely called from any thread.
+////////////////////////////////////////////////////////////////////
+void TypedWritable::
+add_bam_writer(BamWriter *writer) {
+  nassertv(writer != (BamWriter *)NULL);
+
+  BamWriterLink *begin;
+  BamWriterLink *new_link = new BamWriterLink;
+  new_link->_writer = writer;
+
+  // Assert that we got at least a 2-byte aligned pointer from new.
+  nassertv(((uintptr_t)new_link & (uintptr_t)0x1) == 0);
+
+  // This spins if the lower bit is 1, ie. if the pointer is locked.
+  do {
+    begin = (BamWriterLink *)AtomicAdjust::get_ptr(_bam_writers);
+    begin = (BamWriterLink *)(((uintptr_t)begin) & ~(uintptr_t)0x1);
+    new_link->_next = begin;
+  } while (begin != AtomicAdjust::
+    compare_and_exchange_ptr(_bam_writers, (void *)begin, (void *)new_link));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::remove_bam_writer
+//       Access: Private
+//  Description: The converse of add_bam_writer.
+//               This method may be safely called from any thread.
+////////////////////////////////////////////////////////////////////
+void TypedWritable::
+remove_bam_writer(BamWriter *writer) {
+  nassertv(writer != (BamWriter *)NULL);
+
+  BamWriterLink *begin;
+
+  // Grab the head pointer and lock it in one atomic operation.
+  // We lock it by tagging the pointer.
+  do {
+    begin = (BamWriterLink *)AtomicAdjust::get_ptr(_bam_writers);
+    begin = (BamWriterLink *)(((uintptr_t)begin) & ~(uintptr_t)0x1);
+    if (begin == NULL) {
+      // The list is empty, nothing to remove.
+      return;
+    }
+  } while (begin != AtomicAdjust::
+    compare_and_exchange_ptr(_bam_writers, (void *)begin,
+                       (void *)((uintptr_t)begin | (uintptr_t)0x1)));
+
+  // Find the writer in the list.
+  BamWriterLink *prev_link = (BamWriterLink *)NULL;
+  BamWriterLink *link = begin;
+
+  while (link != NULL && link->_writer != writer) {
+    prev_link = link;
+    link = link->_next;
+  }
+
+  if (link == (BamWriterLink *)NULL) {
+    // Not found.  Just unlock and leave.
+    _bam_writers = (void *)begin;
+    return;
+  }
+
+  if (prev_link == (BamWriterLink *)NULL) {
+    // It's the first link.  Replace and unlock in one atomic op.
+    _bam_writers = (void *)link->_next;
+  } else {
+    prev_link->_next = link->_next;
+    _bam_writers = (void *)begin; // Unlock
+  }
+
+  delete link;
+}

+ 9 - 4
panda/src/putil/typedWritable.h

@@ -69,13 +69,18 @@ PUBLISHED:
                                          const string &data,
                                          BamReader *reader = NULL);
 
-private:
+public:
+  void add_bam_writer(BamWriter *writer);
+  void remove_bam_writer(BamWriter *writer);
+
   // We may need to store a list of the BamWriter(s) that have a
   // reference to this object, so that we can remove the object from
   // those tables when it destructs.
-  typedef pvector<BamWriter *> BamWriters;
-  BamWriters *_bam_writers;
-  static LightMutex _bam_writers_lock;
+  struct BamWriterLink {
+    BamWriter *_writer;
+    BamWriterLink *_next;
+  };
+  AtomicAdjust::Pointer _bam_writers; // Tagged pointer
 
   UpdateSeq _bam_modified;