diff options
-rw-r--r-- | src/core/dom/Node.cpp | 20 | ||||
-rw-r--r-- | src/core/dom/Node.hpp | 92 | ||||
-rw-r--r-- | test/core/dom/NodeTest.cpp | 193 |
3 files changed, 247 insertions, 58 deletions
diff --git a/src/core/dom/Node.cpp b/src/core/dom/Node.cpp index 8df0787..7c4852a 100644 --- a/src/core/dom/Node.cpp +++ b/src/core/dom/Node.cpp @@ -187,7 +187,6 @@ void NodeManager::addRef(Node *tar, Node *src) // Store the tar <- src reference assert(dTar); dTar->incrNodeDegree(RefDir::in, src); - if (src) { // Store the src -> tar reference assert(dSrc); @@ -222,15 +221,23 @@ void NodeManager::deleteRef(Node *tar, Node *src, bool all) // is larger than the threshold value and this function was not // called from inside the deleteNode function marked.insert(tar); - if (marked.size() >= threshold) { - sweep(); - } } } + + // Call the garbage collector if the marked size is larger than the actual + // value + if (marked.size() >= threshold) { + sweep(); + } } void NodeManager::deleteNode(Node *n, NodeDescriptor *descr) { + // Abort if the node already is on the "deleted" list + if (deleted.find(n) != deleted.end()) { + return; + } + // Increment the recursion depth counter. The "deleteRef" function called // below // may descend further into this function and the actual deletion should be @@ -277,6 +284,11 @@ void NodeManager::purgeDeleted() void NodeManager::sweep() { + // Only execute sweep on the highest recursion level + if (deletionRecursionDepth > 0) { + return; + } + // Set containing nodes which are reachable from a rooted node std::unordered_set<Node *> reachable; diff --git a/src/core/dom/Node.hpp b/src/core/dom/Node.hpp index ce1e7f1..6869253 100644 --- a/src/core/dom/Node.hpp +++ b/src/core/dom/Node.hpp @@ -280,19 +280,19 @@ public: template <class T> Handle<T> acquire(const BaseHandle<T> &h) { - return Handle<T>(h, this); + return Handle<T>{h, this}; } template <class T> Handle<T> acquire(BaseHandle<T> &&h) { - return Handle<T>(h, this); + return Handle<T>{h, this}; } template <class T> Handle<T> acquire(T *t) { - return Handle<T>(t, this); + return Handle<T>{t, this}; } }; @@ -318,6 +318,11 @@ public: BaseHandle(T *ptr) : ptr(ptr) {} /** + * Returns the underlying pointer. + */ + T *get() const { return ptr; } + + /** * Provides access to the underlying node. */ T *operator->() { return ptr; } @@ -398,6 +403,25 @@ public: } /** + * Constructor of the handle class. + * + * @param ptr is the node the handle should represent. + */ + RootedHandle(T *ptr) : BaseHandle<T>(ptr) { addRef(); } + + /** + * Constructor of the handle class. + * + * @param h is another handle whose Node should be used. + */ + template <class T2> + RootedHandle(const BaseHandle<T2> &h) + : BaseHandle<T>(h.get()) + { + addRef(); + } + + /** * Assignment operator. Assigns the given handle to this handle instance. * Both handles are indistinguishable after the operation. * @@ -454,20 +478,6 @@ public: } /** - * Constructor of the handle class. - * - * @param ptr is the node the handle should represent. - */ - RootedHandle(T *ptr) : BaseHandle<T>(ptr) { addRef(); } - - /** - * Constructor of the handle class. - * - * @param h is another handle whose Node should be used. - */ - RootedHandle(BaseHandle<T> h) : BaseHandle<T>(h.ptr) { addRef(); } - - /** * Destructor of the RootedHandle class, deletes all refrences the class is * still holding. */ @@ -511,7 +521,20 @@ public: * * @param h is the handle that should be asigned to this instance. */ - Handle(const Handle<T> &h) : BaseHandle<T>(h.ptr), owner(h.owner) + Handle(const Handle<T> &h) : BaseHandle<T>(h.get()), owner(h.getOwner()) + { + addRef(); + } + + /** + * Copies the given handle of another derived type to this handle instance. + * Both handles are indistinguishable after the operation (except for the + * type). Note that especially the handle owner is copied. + * + * @param h is the handle that should be asigned to this instance. + */ + template<class T2> + Handle(const Handle<T2> &h) : BaseHandle<T>(h.get()), owner(h.getOwner()) { addRef(); } @@ -521,7 +544,7 @@ public: * * @param h is the handle to be moved to this instance. */ - Handle(Handle<T> &&h) : BaseHandle<T>(h.ptr), owner(h.owner) + Handle(Handle<T> &&h) : BaseHandle<T>(h.get()), owner(h.getOwner()) { h.ptr = nullptr; } @@ -537,7 +560,7 @@ public: { deleteRef(); this->ptr = h.ptr; - this->owner = h.owner; + this->owner = h.getOwner(); addRef(); return *this; } @@ -552,7 +575,7 @@ public: { deleteRef(); this->ptr = h.ptr; - this->owner = h.owner; + this->owner = h.getOwner(); h.ptr = nullptr; return *this; } @@ -573,34 +596,31 @@ public: * @param owner is the node which owns this handle instance. The ptr node * is guaranteed to live at least as long as the owner. */ - Handle(const BaseHandle<T> &h, Node *owner) - : BaseHandle<T>(h.ptr), owner(owner) + template <class T2> + Handle(const BaseHandle<T2> &h, Node *owner) + : BaseHandle<T>(h.get()), owner(owner) { addRef(); } /** - * Constructor of the handle class. - * - * @param h is another handle whose Node should be used. - * @param owner is the node which owns this handle instance. The ptr node - * is guaranteed to live at least as long as the owner. - */ - Handle(BaseHandle<T> &&h, Node *owner) : BaseHandle<T>(h.ptr), owner(owner) - { - h.ptr = nullptr; - } - - /** * Destructor of the Handle class, deletes all refrences the class is still * holding. */ ~Handle() { deleteRef(); } + + /** + * Returns the reference to the owner of the handle. + * + * @return the handle owner. + */ + Node* getOwner() const { + return owner; + } }; using RootedNode = RootedHandle<Node>; using NodeHandle = Handle<Node>; - } } diff --git a/test/core/dom/NodeTest.cpp b/test/core/dom/NodeTest.cpp index 3c50cda..ce49bfe 100644 --- a/test/core/dom/NodeTest.cpp +++ b/test/core/dom/NodeTest.cpp @@ -176,10 +176,10 @@ TEST(Handle, equalsAndAssign) Node *n1 = new Node(mgr), *n2 = new Node(mgr); - RootedNode rh1{n1}; - RootedNode rh2{n2}; + RootedHandle<Node> rh1{n1}; + RootedHandle<Node> rh2{n2}; - NodeHandle h2{n2, n1}; + Handle<Node> h2{n2, n1}; // Equals operator ASSERT_TRUE(rh1 == n1); @@ -189,7 +189,7 @@ TEST(Handle, equalsAndAssign) ASSERT_TRUE(h2 == rh2); // Assignment operator - RootedNode rh2b; + RootedHandle<Node> rh2b; ASSERT_FALSE(rh2b == rh2); rh2b = rh2; @@ -199,14 +199,14 @@ TEST(Handle, equalsAndAssign) rh2b = h2; ASSERT_TRUE(rh2b == h2); - NodeHandle h2b; + Handle<Node> h2b; ASSERT_FALSE(rh2 == h2b); ASSERT_FALSE(h2 == h2b); h2b = h2; ASSERT_TRUE(rh2 == h2b); ASSERT_TRUE(h2 == h2b); - NodeHandle h2c{h2b, n1}; + Handle<Node> h2c{h2b, n1}; ASSERT_TRUE(h2b == h2c); } @@ -221,17 +221,24 @@ private: public: TestNode(NodeManager &mgr, bool &alive) : Node(mgr), alive(alive) { + //std::cout << "create TestNode @" << this << std::endl; alive = true; } - ~TestNode() override { alive = false; } + ~TestNode() override + { + //std::cout << "delete TestNode @" << this << std::endl; + alive = false; + } - void addRef(BaseHandle<Node> h) { refs.push_back(acquire(h)); } + template<class T> + void addRef(T n) { refs.push_back(acquire(n)); } - void deleteRef(BaseHandle<Node> h) + template<class T> + void deleteRef(T n) { for (auto it = refs.begin(); it != refs.end();) { - if (*it == h) { + if (*it == n) { it = refs.erase(it); } else { it++; @@ -243,7 +250,6 @@ public: TEST(NodeManager, linearDependencies) { std::array<bool, 4> a; - a.fill(false); NodeManager mgr(1); { @@ -276,7 +282,6 @@ TEST(NodeManager, linearDependencies) TEST(NodeManager, cyclicDependencies) { std::array<bool, 4> a; - a.fill(false); NodeManager mgr(1); { @@ -307,10 +312,30 @@ TEST(NodeManager, cyclicDependencies) } } +TEST(NodeManager, selfReferentialCyclicDependencies) +{ + std::array<bool, 2> a; + + NodeManager mgr(1); + { + TestNode *n1; + n1 = new TestNode(mgr, a[1]); + + { + RootedHandle<TestNode> hr{new TestNode(mgr, a[0])}; + ASSERT_TRUE(a[0] && a[1]); + hr->addRef(n1); + n1->addRef(n1); + } + + // All nodes must have set their "alive" flag to false + ASSERT_FALSE(a[0] || a[1]); + } +} + TEST(NodeManager, doubleRooted) { std::array<bool, 4> a; - a.fill(false); NodeManager mgr(1); { @@ -328,13 +353,13 @@ TEST(NodeManager, doubleRooted) ASSERT_TRUE(v); } - // Create cyclical dependency between n2 and n1 - n1->addRef(n2); - n2->addRef(n1); - // Reference n1 and n2 in the rooted nodes hr1->addRef(n1); hr2->addRef(n2); + + // Create cyclical dependency between n2 and n1 + n1->addRef(n2); + n2->addRef(n1); } // hr2 is dead, all other nodes are still alive @@ -352,7 +377,6 @@ TEST(NodeManager, doubleRooted) TEST(NodeManager, disconnectSubgraph) { std::array<bool, 4> a; - a.fill(false); NodeManager mgr(1); { @@ -388,6 +412,139 @@ TEST(NodeManager, disconnectSubgraph) } } } + +TEST(NodeManager, disconnectDoubleRootedSubgraph) +{ + std::array<bool, 5> a; + + NodeManager mgr(1); + { + TestNode *n1, *n2, *n3; + n1 = new TestNode(mgr, a[1]); + n2 = new TestNode(mgr, a[2]); + n3 = new TestNode(mgr, a[3]); + + { + RootedHandle<TestNode> hr1{new TestNode(mgr, a[0])}; + { + RootedHandle<TestNode> hr2{new TestNode(mgr, a[4])}; + + // Create a cyclic dependency chain with two rooted nodes + hr1->addRef(n1); + n1->addRef(n2); + n2->addRef(n3); + n3->addRef(n1); + hr2->addRef(n3); + + // All nodes must have set their "alive" flag to true + for (bool v : a) { + ASSERT_TRUE(v); + } + + // Remove the reference from n3 to n1 + n3->deleteRef(n1); + + // Still all nodes must have set their "alive" flag to true + for (bool v : a) { + ASSERT_TRUE(v); + } + + // Remove the reference from n1 to n2 + n1->deleteRef(n2); + + // Node 2 must be dead, all others alive + ASSERT_FALSE(a[2]); + ASSERT_TRUE(a[0] && a[1] && a[3] && a[4]); + } + + // Node 2, 3, hr2 must be dead, all others alive + ASSERT_FALSE(a[2] || a[3] || a[4]); + ASSERT_TRUE(a[0] && a[1]); + } + + // All nodes must have set their "alive" flag to false + for (bool v : a) { + ASSERT_FALSE(v); + } + } +} + +RootedHandle<TestNode> createFullyConnectedGraph(NodeManager &mgr, int nElem, + bool alive[]) +{ + std::vector<RootedHandle<TestNode>> nodes; + + // Create the nodes + for (int i = 0; i < nElem; i++) { + nodes.push_back(RootedHandle<TestNode>{new TestNode{mgr, alive[i]}}); + } + + // Add all connections + for (int i = 0; i < nElem; i++) { + for (int j = 0; j < nElem; j++) { + nodes[i]->addRef(nodes[j]); + } + } + + return nodes[0]; +} + +TEST(NodeManager, fullyConnectedGraph) +{ + constexpr int nElem = 64; + std::array<bool, nElem> a; + + NodeManager mgr(1); + { + RootedHandle<TestNode> n = createFullyConnectedGraph(mgr, nElem, &a[0]); + for (bool v : a) { + ASSERT_TRUE(v); + } + } + for (bool v : a) { + ASSERT_FALSE(v); + } +} + +class HidingTestNode : public TestNode { + +private: + RootedHandle<Node> hidden; + +public: + + HidingTestNode(NodeManager &mgr, bool &alive) : TestNode(mgr, alive) {}; + + template<class T> + void setHiddenRef(T t) { + hidden = t; + } + +}; + +TEST(NodeManager, hiddenRootedGraph) +{ + constexpr int nElem = 16; + std::array<bool, nElem> a; + bool b; + NodeManager mgr(1); + + { + RootedHandle<HidingTestNode> n{new HidingTestNode{mgr, b}}; + n->setHiddenRef(createFullyConnectedGraph(mgr, nElem, &a[0])); + + ASSERT_TRUE(b); + for (bool v : a) { + ASSERT_TRUE(v); + } + } + + ASSERT_FALSE(b); + for (bool v : a) { + ASSERT_FALSE(v); + } +} + } } |