Optimization: use a shared reference counter instead of a per-object counter in Object::Ref/Unref
authorMathieu Lacage <mathieu.lacage@sophia.inria.fr>
Fri, 06 Nov 2009 17:47:44 +0100
changeset 5486 9bf34ba759a3
parent 5485 1739c7bafd29
child 5487 9485bee6b7eb
Optimization: use a shared reference counter instead of a per-object counter in Object::Ref/Unref
src/core/object-ref-count.h
src/core/object.cc
src/core/object.h
src/core/wscript
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/object-ref-count.h	Fri Nov 06 17:47:44 2009 +0100
@@ -0,0 +1,121 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2009 Mathieu Lacage
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@gmail.com>
+ */
+#ifndef OBJECT_REF_COUNT_H
+#define OBJECT_REF_COUNT_H
+
+#include "empty.h"
+
+namespace ns3 {
+
+/**
+ * This templated class provides the refcounting implementation
+ * for the ns3::Object class.
+ *
+ * The tricky aspect of the implementation of this class is that
+ * instead of storing a count directly, it keeps track of a pointer
+ * to a count to allow multiple instances of the class to share the
+ * same pointer/count. This is mainly used by the aggregation support
+ * of the ns3::Object class
+ */
+template <typename T, typename PARENT = empty>
+class ObjectRefCount : public PARENT
+{
+public:
+  ObjectRefCount ()
+    : m_count (new int (1))
+  {}
+  ObjectRefCount (const ObjectRefCount &o)
+    : m_count (new int (1))
+  {}
+  ObjectRefCount & operator = (const ObjectRefCount &o)
+  {
+    *m_count = *o.m_count;
+    return *this;
+  }
+  ~ObjectRefCount () 
+  {
+    m_count = 0;
+  }
+
+  /**
+   * Get the reference count of the object.  Normally not needed; for language bindings.
+   */
+  int GetReferenceCount (void) const {
+    return *m_count;
+  }
+  /**
+   * Increment the reference count. This method should not be called
+   * by user code. Object instances are expected to be used in conjunction
+   * of the Ptr template which would make calling Ref unecessary and 
+   * dangerous.
+   */
+  inline void Ref (void) const
+    {
+      (*m_count)++;
+    }
+  /**
+   * Decrement the reference count. This method should not be called
+   * by user code. Object instances are expected to be used in conjunction
+   * of the Ptr template which would make calling Ref unecessary and 
+   * dangerous.
+   */
+  inline void Unref (void) const
+    {
+      (*m_count)--;
+      if ((*m_count) == 0)
+        {
+	  const_cast<ObjectRefCount<T,PARENT>*>(this)->DoDelete ();
+        }
+    }
+protected:
+  /**
+   * \param other another object
+   *
+   * This method makes this object and the input other object
+   * share the same reference count.
+   */
+  void ShareCount (ObjectRefCount *other)
+    {
+      (*m_count) += (*other->m_count);
+      delete other->m_count;
+      other->m_count = m_count;
+    }
+  /**
+   * Called just before deleting this object: when two
+   * objects share the same reference count, the user
+   * who is deleting them must be careful to delete the
+   * associated count only once and this is done by calling 
+   * this method to get a reference to the count and, then,
+   * calling delete on the count.
+   *
+   * \sa ns3::Object::DoDelete
+   */
+  int *PeekCountPtr (void) const
+    {
+      return m_count;
+    }
+private:
+  virtual void DoDelete (void) = 0;
+  mutable int *m_count;
+};
+
+} // namespace ns3
+
+#endif /* OBJECT_REF_COUNT_H */
--- a/src/core/object.cc	Fri Nov 06 11:27:17 2009 +0100
+++ b/src/core/object.cc	Fri Nov 06 17:47:44 2009 +0100
@@ -79,8 +79,7 @@
 
 
 Object::Object ()
-  : m_count (1),
-    m_tid (Object::GetTypeId ()),
+  : m_tid (Object::GetTypeId ()),
     m_disposed (false),
     m_aggregates ((struct Aggregates *)malloc (sizeof (struct Aggregates))),
     m_getObjectCount (0)
@@ -112,8 +111,7 @@
   m_aggregates = 0;
 }
 Object::Object (const Object &o)
-  : m_count (1),
-    m_tid (o.m_tid),
+  : m_tid (o.m_tid),
     m_disposed (false),
     m_aggregates ((struct Aggregates *)malloc (sizeof (struct Aggregates))),
     m_getObjectCount (0)
@@ -121,11 +119,6 @@
   m_aggregates->n = 1;
   m_aggregates->buffer[0] = this;
 }
-uint32_t
-Object::GetReferenceCount (void) const
-{
-  return m_count;
-}
 void
 Object::Construct (const AttributeList &attributes)
 {
@@ -231,6 +224,10 @@
       Object *current = aggregates->buffer[i];
       current->m_aggregates = aggregates;
     }
+
+  // share the counts
+  ShareCount (other);
+
   // Finally, call NotifyNewAggregate in the listed chain
   for (uint32_t i = 0; i < n; i++)
     {
@@ -271,7 +268,7 @@
 bool 
 Object::Check (void) const
 {
-  return (m_count > 0);
+  return (GetReferenceCount () > 0);
 }
 
 /* In some cases, when an event is scheduled against a subclass of
@@ -289,13 +286,12 @@
   for (uint32_t i = 0; i < n; i++)
     {
       Object *current = m_aggregates->buffer[i];
-      refcount += current->m_count;
+      refcount += current->GetReferenceCount ();
     }
   return (refcount > 0);
 }
-
 void
-Object::MaybeDelete (void) const
+Object::DoDelete (void)
 {
   // First, check if any of the attached
   // Object has a non-zero count.
@@ -303,7 +299,7 @@
   for (uint32_t i = 0; i < n; i++)
     {
       Object *current = m_aggregates->buffer[i];
-      if (current->m_count != 0)
+      if (current->GetReferenceCount () != 0)
         {
           return;
         }
@@ -319,6 +315,8 @@
         }
     }
 
+  int *count = PeekCountPtr ();
+
   // all attached objects have a zero count so, 
   // we can delete them all.
   struct Aggregates *aggregates = m_aggregates;
@@ -331,6 +329,8 @@
       Object *current = aggregates->buffer[0];
       delete current;
     }
+
+  delete count;
 }
 } // namespace ns3
 
--- a/src/core/object.h	Fri Nov 06 11:27:17 2009 +0100
+++ b/src/core/object.h	Fri Nov 06 17:47:44 2009 +0100
@@ -28,6 +28,7 @@
 #include "attribute.h"
 #include "object-base.h"
 #include "attribute-list.h"
+#include "object-ref-count.h"
 
 
 namespace ns3 {
@@ -55,7 +56,7 @@
  * invoked from the Object::Unref method before destroying the object, even if the user 
  * did not call Object::Dispose directly.
  */
-class Object : public ObjectBase
+class Object : public ObjectRefCount<Object,ObjectBase>
 {
 public:
   static TypeId GetTypeId (void);
@@ -99,26 +100,6 @@
   virtual TypeId GetInstanceTypeId (void) const;
 
   /**
-   * Increment the reference count. This method should not be called
-   * by user code. Object instances are expected to be used in conjunction
-   * of the Ptr template which would make calling Ref unecessary and 
-   * dangerous.
-   */
-  inline void Ref (void) const;
-  /**
-   * Decrement the reference count. This method should not be called
-   * by user code. Object instances are expected to be used in conjunction
-   * of the Ptr template which would make calling Ref unecessary and 
-   * dangerous.
-   */
-  inline void Unref (void) const;
-
-  /**
-   * Get the reference count of the object.  Normally not needed; for language bindings.
-   */
-  uint32_t GetReferenceCount (void) const;
-
-  /**
    * \returns a pointer to the requested interface or zero if it could not be found.
    */
   template <typename T>
@@ -234,13 +215,6 @@
   bool Check (void) const;
   bool CheckLoose (void) const;
   /**
-   * Attempt to delete this object. This method iterates
-   * over all aggregated objects to check if they all 
-   * have a zero refcount. If yes, the object and all
-   * its aggregates are deleted. If not, nothing is done.
-   */
-  void MaybeDelete (void) const;
-  /**
    * \param tid an TypeId
    *
    * Invoked from ns3::CreateObject only.
@@ -259,16 +233,15 @@
   void Construct (const AttributeList &attributes);
 
   void UpdateSortedArray (struct Aggregates *aggregates, uint32_t i) const;
+  /**
+   * Attempt to delete this object. This method iterates
+   * over all aggregated objects to check if they all 
+   * have a zero refcount. If yes, the object and all
+   * its aggregates are deleted. If not, nothing is done.
+   */
+  virtual void DoDelete (void);
 
   /**
-   * The reference count for this object. Each aggregate
-   * has an individual reference count. When the global
-   * reference count (the sum of all reference counts) 
-   * reaches zero, the object and all its aggregates is 
-   * deleted.
-   */
-  mutable uint32_t m_count;
-  /**
    * Identifies the type of this object instance.
    */
   TypeId m_tid;
@@ -363,22 +336,6 @@
  *   The Object implementation which depends on templates
  *************************************************************************/
 
-void
-Object::Ref (void) const
-{
-  m_count++;
-}
-void
-Object::Unref (void) const
-{
-  NS_ASSERT (Check ());
-  m_count--;
-  if (m_count == 0)
-    {
-      MaybeDelete ();
-    }
-}
-
 template <typename T>
 Ptr<T> 
 Object::GetObject () const
--- a/src/core/wscript	Fri Nov 06 11:27:17 2009 +0100
+++ b/src/core/wscript	Fri Nov 06 17:47:44 2009 +0100
@@ -126,6 +126,7 @@
         'abort.h',
         'names.h',
         'vector.h',
+        'object-ref-count.h',
         ]
 
     if sys.platform == 'win32':