Handle recursive calls to GetObject, AggregateObject and Start

This commit is contained in:
Mathieu Lacage
2009-11-19 20:51:55 +01:00
parent dae6fde0d4
commit fe86ae64d8
2 changed files with 77 additions and 21 deletions

View File

@@ -163,24 +163,49 @@ Object::DoGetObject (TypeId tid) const
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 @@ Object::AggregateObject (Ptr<Object> o)
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 @@ Object::AggregateObject (Ptr<Object> o)
// 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

View File

@@ -127,6 +127,12 @@ public:
* 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 @@ public:
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 @@ protected:
* 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);
/**