core: allow multiple aggregation of an object

This commit is contained in:
Tommaso Pecorella
2024-03-15 02:53:15 +00:00
parent df984cf4b0
commit bb3cbdd28f
4 changed files with 221 additions and 8 deletions

View File

@@ -18,6 +18,8 @@ Changes from ns-3.41 to ns-3-dev
### New API ### New API
* Objects now can be aggregated to multiple objects though the `Object::UnidirectionalAggregateObject` function. Objects aggregated in such a way can not use `GetObject` to access the objects they are aggregated to.
### Changes to existing API ### Changes to existing API
* `InetSocketAddress::SetTos()` and `InetSocketAddress::GetTos()` have been removed. * `InetSocketAddress::SetTos()` and `InetSocketAddress::GetTos()` have been removed.
@@ -30,8 +32,7 @@ Applications have a new Attribute to set the IPv4 ToS field.
### Changed behavior ### Changed behavior
* Fixed the corner rebound direction in `RandomWalk2d[Outdoor]MobilityModel` and the * Fixed the corner rebound direction in `RandomWalk2d[Outdoor]MobilityModel` and the initial direction in case of node starting from a border or corner.
initial direction in case of node starting from a border or corner.
Changes from ns-3.40 to ns-3.41 Changes from ns-3.40 to ns-3.41
------------------------------- -------------------------------

View File

@@ -59,16 +59,27 @@ bool
Object::AggregateIterator::HasNext() const Object::AggregateIterator::HasNext() const
{ {
NS_LOG_FUNCTION(this); NS_LOG_FUNCTION(this);
return m_current < m_object->m_aggregates->n; return (m_current < m_object->m_aggregates->n) ||
(m_uniAggrIter != m_object->m_unidirectionalAggregates.end());
} }
Ptr<const Object> Ptr<const Object>
Object::AggregateIterator::Next() Object::AggregateIterator::Next()
{ {
NS_LOG_FUNCTION(this); NS_LOG_FUNCTION(this);
Object* object = m_object->m_aggregates->buffer[m_current]; if (m_current < m_object->m_aggregates->n)
m_current++; {
return object; Object* object = m_object->m_aggregates->buffer[m_current];
m_current++;
return object;
}
else if (m_uniAggrIter != m_object->m_unidirectionalAggregates.end())
{
auto object = *m_uniAggrIter;
m_uniAggrIter++;
return object;
}
return nullptr;
} }
Object::AggregateIterator::AggregateIterator(Ptr<const Object> object) Object::AggregateIterator::AggregateIterator(Ptr<const Object> object)
@@ -76,6 +87,7 @@ Object::AggregateIterator::AggregateIterator(Ptr<const Object> object)
m_current(0) m_current(0)
{ {
NS_LOG_FUNCTION(this << object); NS_LOG_FUNCTION(this << object);
m_uniAggrIter = object->m_unidirectionalAggregates.begin();
} }
TypeId TypeId
@@ -127,6 +139,7 @@ Object::~Object()
std::free(m_aggregates); std::free(m_aggregates);
} }
m_aggregates = nullptr; m_aggregates = nullptr;
m_unidirectionalAggregates.clear();
} }
Object::Object(const Object& o) Object::Object(const Object& o)
@@ -153,6 +166,7 @@ Object::DoGetObject(TypeId tid) const
NS_LOG_FUNCTION(this << tid); NS_LOG_FUNCTION(this << tid);
NS_ASSERT(CheckLoose()); NS_ASSERT(CheckLoose());
// First check if the object is in the normal aggregates.
uint32_t n = m_aggregates->n; uint32_t n = m_aggregates->n;
TypeId objectTid = Object::GetTypeId(); TypeId objectTid = Object::GetTypeId();
for (uint32_t i = 0; i < n; i++) for (uint32_t i = 0; i < n; i++)
@@ -179,6 +193,20 @@ Object::DoGetObject(TypeId tid) const
return const_cast<Object*>(current); return const_cast<Object*>(current);
} }
} }
// Next check if it's a unidirectional aggregate
for (auto& uniItem : m_unidirectionalAggregates)
{
TypeId cur = uniItem->GetInstanceTypeId();
while (cur != tid && cur != objectTid)
{
cur = cur.GetParent();
}
if (cur == tid)
{
return uniItem;
}
}
return nullptr; return nullptr;
} }
@@ -206,6 +234,17 @@ restart:
goto restart; goto restart;
} }
} }
// note: no need to restart because unidirectionally aggregated objects
// can not change the status of the actual object.
for (auto& uniItem : m_unidirectionalAggregates)
{
if (!uniItem->m_initialized)
{
uniItem->DoInitialize();
uniItem->m_initialized = true;
}
}
} }
bool bool
@@ -239,6 +278,17 @@ restart:
goto restart; goto restart;
} }
} }
// note: no need to restart because unidirectionally aggregated objects
// can not change the status of the actual object.
for (auto& uniItem : m_unidirectionalAggregates)
{
if (!uniItem->m_disposed && uniItem->GetReferenceCount() == 1)
{
uniItem->DoDispose();
uniItem->m_disposed = true;
}
}
} }
void void
@@ -280,11 +330,13 @@ Object::AggregateObject(Ptr<Object> o)
{ {
aggregates->buffer[m_aggregates->n + i] = other->m_aggregates->buffer[i]; aggregates->buffer[m_aggregates->n + i] = other->m_aggregates->buffer[i];
const TypeId typeId = other->m_aggregates->buffer[i]->GetInstanceTypeId(); const TypeId typeId = other->m_aggregates->buffer[i]->GetInstanceTypeId();
// note: DoGetObject scans also the unidirectional aggregates
if (DoGetObject(typeId)) if (DoGetObject(typeId))
{ {
NS_FATAL_ERROR("Object::AggregateObject(): " NS_FATAL_ERROR("Object::AggregateObject(): "
"Multiple aggregation of objects of type " "Multiple aggregation of objects of type "
<< other->GetInstanceTypeId() << " on objects of type " << typeId); << other->GetInstanceTypeId() << " on objects of type "
<< GetInstanceTypeId());
} }
UpdateSortedArray(aggregates, m_aggregates->n + i); UpdateSortedArray(aggregates, m_aggregates->n + i);
} }
@@ -323,7 +375,47 @@ Object::AggregateObject(Ptr<Object> o)
std::free(b); std::free(b);
} }
/** void
Object::UnidirectionalAggregateObject(Ptr<Object> o)
{
NS_LOG_FUNCTION(this << o);
NS_ASSERT(!m_disposed);
NS_ASSERT(!o->m_disposed);
NS_ASSERT(CheckLoose());
NS_ASSERT(o->CheckLoose());
Object* other = PeekPointer(o);
const TypeId typeId = other->GetInstanceTypeId();
// note: DoGetObject scans also the unidirectional aggregates
if (DoGetObject(typeId))
{
NS_FATAL_ERROR("Object::UnidirectionalAggregateObject(): "
"Multiple aggregation of objects of type "
<< other->GetInstanceTypeId() << " on objects of type "
<< GetInstanceTypeId());
}
m_unidirectionalAggregates.emplace_back(other);
// Finally, call NotifyNewAggregate on all the objects aggregates by this object.
// We skip the aggregated Object and its aggregates because they are not
// mutually aggregated to the others.
// Unfortunately, we have to make a copy of the aggregated objects, because
// NotifyNewAggregate might change it...
std::list<Object*> aggregates;
for (uint32_t i = 0; i < m_aggregates->n; i++)
{
aggregates.emplace_back(m_aggregates->buffer[i]);
}
for (auto& item : aggregates)
{
item->NotifyNewAggregate();
}
}
/*
* This function must be implemented in the stack that needs to notify * This function must be implemented in the stack that needs to notify
* other stacks connected to the node of their presence in the node. * other stacks connected to the node of their presence in the node.
*/ */

View File

@@ -137,6 +137,8 @@ class Object : public SimpleRefCount<Object, ObjectBase, ObjectDeleter>
AggregateIterator(Ptr<const Object> object); AggregateIterator(Ptr<const Object> object);
Ptr<const Object> m_object; //!< Parent Object. Ptr<const Object> m_object; //!< Parent Object.
uint32_t m_current; //!< Current position in parent's aggregates. uint32_t m_current; //!< Current position in parent's aggregates.
/// Iterator to the unidirectional aggregates.
std::vector<Ptr<Object>>::const_iterator m_uniAggrIter;
}; };
/** Constructor. */ /** Constructor. */
@@ -199,6 +201,44 @@ class Object : public SimpleRefCount<Object, ObjectBase, ObjectDeleter>
*/ */
void AggregateObject(Ptr<Object> other); void AggregateObject(Ptr<Object> other);
/**
* Aggregate an Object to another Object.
*
* \param [in] other The other Object pointer
*
* This method aggregates the an object to another Object:
* after this method returns, it becomes possible to call GetObject()
* on the aggregating Object to get the other, but not vice-versa.
*
* This method calls the virtual method NotifyNewAggregates() to
* notify all aggregated Objects that they have been aggregated
* together.
*
* This method is useful only if there is the need to aggregate an
* object to more than one object at the same time, and should be avoided
* if not strictly necessary.
* In particular, objects aggregated with this method should be destroyed
* only after making sure that the objects they are aggregated to are
* destroyed as well. However, the destruction of the aggregating objects
* will take care of the unidirectional aggregated objects gracefully.
*
* Beware that an object aggregated to another with this function
* behaves differently than other aggregates in the following ways.
* Suppose that Object B is aggregated unidirectionally:
* - It can be aggregated unidirectionally to more than one objects
* (e.g., A1 and A2).
* - It is not possible to call GetObject on B to find an aggregate of
* object A1 or A2.
* - When A1 or A2 are initialized, B is initialized, whichever happens first.
* - When A1 or A2 are destroyed, B is destroyed, whichever happens last.
* - If B is initialized, A1 and A2 are unaffected.
* - If B is forcefully destroyed, A1 and A2 are unaffected.
*
*
* \sa AggregateObject()
*/
void UnidirectionalAggregateObject(Ptr<Object> other);
/** /**
* Get an iterator to the Objects aggregated to this one. * Get an iterator to the Objects aggregated to this one.
* *
@@ -436,6 +476,17 @@ class Object : public SimpleRefCount<Object, ObjectBase, ObjectDeleter>
* so the size of the array is indirectly a reference count. * so the size of the array is indirectly a reference count.
*/ */
Aggregates* m_aggregates; Aggregates* m_aggregates;
/**
* An array of unidirectional aggregates, i.e., objects that are
* aggregated to the current object, but not vice-versa.
*
* This is useful (and suggested) only for Objects that should
* be aggregated to multiple other Objects, where the normal
* Aggregation would create an issue.
*/
std::vector<Ptr<Object>> m_unidirectionalAggregates;
/** /**
* The number of times the Object was accessed with a * The number of times the Object was accessed with a
* call to GetObject(). * call to GetObject().

View File

@@ -464,6 +464,74 @@ AggregateObjectTestCase::DoRun()
NS_TEST_ASSERT_MSG_NE(baseA, nullptr, "Unable to GetObject on released object"); NS_TEST_ASSERT_MSG_NE(baseA, nullptr, "Unable to GetObject on released object");
} }
/**
* \ingroup object-tests
* Test we can aggregate Objects.
*/
class UnidirectionalAggregateObjectTestCase : public TestCase
{
public:
/** Constructor. */
UnidirectionalAggregateObjectTestCase();
/** Destructor. */
~UnidirectionalAggregateObjectTestCase() override;
private:
void DoRun() override;
};
UnidirectionalAggregateObjectTestCase::UnidirectionalAggregateObjectTestCase()
: TestCase("Check Object unidirectional aggregation functionality")
{
}
UnidirectionalAggregateObjectTestCase::~UnidirectionalAggregateObjectTestCase()
{
}
void
UnidirectionalAggregateObjectTestCase::DoRun()
{
Ptr<BaseA> baseAOne = CreateObject<BaseA>();
NS_TEST_ASSERT_MSG_NE(baseAOne, nullptr, "Unable to CreateObject<BaseA>");
Ptr<BaseA> baseATwo = CreateObject<BaseA>();
NS_TEST_ASSERT_MSG_NE(baseATwo, nullptr, "Unable to CreateObject<BaseA>");
Ptr<BaseB> baseB = CreateObject<BaseB>();
NS_TEST_ASSERT_MSG_NE(baseB, nullptr, "Unable to CreateObject<BaseB>");
//
// Make an unidirectional aggregation of a BaseA object and a BaseB object.
//
baseAOne->UnidirectionalAggregateObject(baseB);
baseATwo->UnidirectionalAggregateObject(baseB);
//
// We should be able to ask the aggregation (through baseA) for the BaseB part
// on either BaseA objects
//
NS_TEST_ASSERT_MSG_NE(baseAOne->GetObject<BaseB>(),
nullptr,
"Cannot GetObject (through baseAOne) for BaseB Object");
NS_TEST_ASSERT_MSG_NE(baseATwo->GetObject<BaseB>(),
nullptr,
"Cannot GetObject (through baseATwo) for BaseB Object");
NS_TEST_ASSERT_MSG_EQ(
baseAOne->GetObject<BaseB>(),
baseATwo->GetObject<BaseB>(),
"GetObject (through baseAOne and baseATwo) for BaseB Object are not equal");
//
// We should not be able to ask the aggregation (through baseB) for the BaseA part
// of the aggregation.
//
NS_TEST_ASSERT_MSG_NE(!baseB->GetObject<BaseA>(),
0,
"Can GetObject (through baseB) for BaseA Object");
}
/** /**
* \ingroup object-tests * \ingroup object-tests
* Test an Object factory can create Objects * Test an Object factory can create Objects
@@ -565,6 +633,7 @@ ObjectTestSuite::ObjectTestSuite()
{ {
AddTestCase(new CreateObjectTestCase); AddTestCase(new CreateObjectTestCase);
AddTestCase(new AggregateObjectTestCase); AddTestCase(new AggregateObjectTestCase);
AddTestCase(new UnidirectionalAggregateObjectTestCase);
AddTestCase(new ObjectFactoryTestCase); AddTestCase(new ObjectFactoryTestCase);
} }