Handle recursive calls to GetObject, AggregateObject and Start
authorMathieu Lacage <mathieu.lacage@sophia.inria.fr>
Thu, 19 Nov 2009 20:51:55 +0100
changeset 5758 bac30d4b6115
parent 5754 7f0de9a416ea
child 5759 5d37cccf96c3
Handle recursive calls to GetObject, AggregateObject and Start
src/core/object.cc
src/core/object.h
--- a/src/core/object.cc	Wed Nov 18 12:42:33 2009 +0000
+++ b/src/core/object.cc	Thu Nov 19 20:51:55 2009 +0100
@@ -163,24 +163,49 @@
 void
 Object::Start (void)
 {
+  /**
+   * Note: the code here is a bit tricky because we need to protect ourselves from
+   * modifications in the aggregate array while DoStart is called. The user's
+   * implementation of the DoStart method could call GetObject (which could
+   * reorder the array) and it could call AggregateObject which would add an 
+   * object at the end of the array. To be safe, we restart iteration over the 
+   * array whenever we call some user code, just in case.
+   */
+ restart:
   uint32_t n = m_aggregates->n;
   for (uint32_t i = 0; i < n; i++)
     {
       Object *current = m_aggregates->buffer[i];
-      current->DoStart ();
-      current->m_started = true;
+      if (!current->m_started)
+        {
+          current->DoStart ();
+          current->m_started = true;
+          goto restart;
+        }
     }
 }
 void 
 Object::Dispose (void)
 {
+  /**
+   * Note: the code here is a bit tricky because we need to protect ourselves from
+   * modifications in the aggregate array while DoDispose is called. The user's
+   * DoDispose implementation could call GetObject (which could reorder the array) 
+   * and it could call AggregateObject which would add an object at the end of the array.
+   * So, to be safe, we restart the iteration over the array whenever we call some
+   * user code.
+   */
+ restart:
   uint32_t n = m_aggregates->n;
   for (uint32_t i = 0; i < n; i++)
     {
       Object *current = m_aggregates->buffer[i];
-      NS_ASSERT (!current->m_disposed);
-      current->DoDispose ();
-      current->m_disposed = true;
+      if (!current->m_disposed)
+        {
+          current->DoDispose ();
+          current->m_disposed = true;
+          goto restart;
+        }
     }
 }
 void
@@ -216,21 +241,25 @@
   struct Aggregates *aggregates = 
     (struct Aggregates *)malloc (sizeof(struct Aggregates)+(total-1)*sizeof(Object*));
   aggregates->n = total;
+
+  // copy our buffer to the new buffer
   memcpy (&aggregates->buffer[0], 
           &m_aggregates->buffer[0], 
           m_aggregates->n*sizeof(Object*));
-  // append the other aggregates in the new buffer
+
+  // append the other buffer into the new buffer too
   for (uint32_t i = 0; i < other->m_aggregates->n; i++)
     {
       aggregates->buffer[m_aggregates->n+i] = other->m_aggregates->buffer[i];
       UpdateSortedArray (aggregates, m_aggregates->n + i);
     }
 
-  // free both aggregate buffers
-  free (m_aggregates);
-  free (other->m_aggregates);
+  // keep track of the old aggregate buffers for the iteration
+  // of NotifyNewAggregates
+  struct Aggregates *a = m_aggregates;
+  struct Aggregates *b = other->m_aggregates;
 
-  // Then, assign that buffer to every object
+  // Then, assign the new aggregation buffer to every object
   uint32_t n = aggregates->n;
   for (uint32_t i = 0; i < n; i++)
     {
@@ -241,12 +270,25 @@
   // share the counts
   ShareCount (other);
 
-  // Finally, call NotifyNewAggregate in the listed chain
-  for (uint32_t i = 0; i < n; i++)
+  // Finally, call NotifyNewAggregate on all the objects aggregates together.
+  // We purposedly use the old aggregate buffers to iterate over the objects
+  // because this allows us to assume that they will not change from under 
+  // our feet, even if our users call AggregateObject from within their
+  // NotifyNewAggregate method.
+  for (uint32_t i = 0; i < a->n; i++)
     {
-      Object *current = m_aggregates->buffer[i];
+      Object *current = a->buffer[i];
       current->NotifyNewAggregate ();
     }
+  for (uint32_t i = 0; i < b->n; i++)
+    {
+      Object *current = b->buffer[i];
+      current->NotifyNewAggregate ();
+    }
+
+  // Now that we are done with them, we can free our old aggregate buffers
+  free (a);
+  free (b);
 }
 /**
  * This function must be implemented in the stack that needs to notify
--- a/src/core/object.h	Wed Nov 18 12:42:33 2009 +0000
+++ b/src/core/object.h	Thu Nov 19 20:51:55 2009 +0100
@@ -127,6 +127,12 @@
    * This method aggregates the two objects together: after this
    * method returns, it becomes possible to call GetObject
    * on one to get the other, and vice-versa. 
+   *
+   * This method calls the virtual method NotifyNewAggregates to
+   * notify all aggregated objects that they have been aggregated
+   * together.
+   *
+   * \sa NotifyNewAggregate
    */
   void AggregateObject (Ptr<Object> other);
 
@@ -141,26 +147,32 @@
   AggregateIterator GetAggregateIterator (void) const;
 
   /**
-   * Execute starting code of an object. What this method does is really up
-   * to the user.
+   * This method calls the virtual DoStart method on all the objects
+   * aggregated to this object. DoStart will be called only once over
+   * the lifetime of an object, just like DoDispose is called only
+   * once.
+   *
+   * \sa DoStart
    */
   void Start (void);
 
 protected:
  /**
-  * This function is called by the AggregateObject on all the objects connected in the listed chain.
-  * This way the new object aggregated will be used if needed by the NotifyNewAggregate corresponding
-  * to each object connected in the listed chain. It should be implemented by objects needing an
-  * additional/special behavior when aggregated to another object.
+  * This method is invoked whenever two sets of objects are aggregated together.
+  * It is invoked exactly once for each object in both sets.
+  * This method can be overriden by subclasses who wish to be notified of aggregation
+  * events. These subclasses must chain up to their base class NotifyNewAggregate method.
+  * It is safe to call GetObject and AggregateObject from within this method.
   */
-  virtual void NotifyNewAggregate ();
+  virtual void NotifyNewAggregate (void);
   /**
    * This method is called only once by Object::Start. If the user
    * calls Object::Start multiple times, DoStart is called only the
    * first time.
    *
    * Subclasses are expected to override this method and _chain up_
-   * to their parent's implementation once they are done.
+   * to their parent's implementation once they are done. It is
+   * safe to call GetObject and AggregateObject from within this method.
    */
   virtual void DoStart (void);
   /**
@@ -173,6 +185,8 @@
    * i.e., for simplicity, the destructor of every subclass should
    * be empty and its content should be moved to the associated
    * DoDispose method.
+   *
+   * It is safe to call GetObject from within this method.
    */
   virtual void DoDispose (void);
   /**