From cb77fbe2ff316e8cd308ea3b6b79d67aaf6e88de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 28 Jun 2018 17:59:06 +0200 Subject: [PATCH] Fix AllocationFailure when multiple instances --- expression_reference.cpp | 5 +++ expression_reference.h | 5 +-- layout_cursor.h | 2 +- layout_reference.cpp | 6 ++-- layout_reference.h | 4 +-- test.cpp | 4 +-- tree_node.cpp | 32 ++++++++++------- tree_node.h | 5 +++ tree_pool.h | 32 +++++++++++++++-- tree_reference.h | 77 ++++++++++++++++++++++++++++++++-------- 10 files changed, 133 insertions(+), 39 deletions(-) diff --git a/expression_reference.cpp b/expression_reference.cpp index b1ef3e524..9c7c29d64 100644 --- a/expression_reference.cpp +++ b/expression_reference.cpp @@ -6,3 +6,8 @@ TreeNode * ExpressionRef::failedAllocationNode() { static AllocationFailedExpressionRef FailureRef; return FailureRef.node(); } + +template<> +ExpressionReference ExpressionRef::failedAllocationRef() { + return ExpressionReference(failedAllocationNode()); +} diff --git a/expression_reference.h b/expression_reference.h index 9b02d944d..390ab704a 100644 --- a/expression_reference.h +++ b/expression_reference.h @@ -12,10 +12,11 @@ public: using TreeReference::TreeReference; // Allow every ExpressionReference to be transformed into an ExpressionReference, i.e. Expression - operator ExpressionReference() const { + operator ExpressionReference() { return ExpressionReference(this->node()); } + static ExpressionReference failedAllocationRef(); static TreeNode * failedAllocationNode(); void addChild(ExpressionReference e) { @@ -32,7 +33,7 @@ public: TreeReference::replaceChildAtIndex(oldChildIndex, newChild); } - float approximate() const { + float approximate() { return this->castedNode()->approximate(); } diff --git a/layout_cursor.h b/layout_cursor.h index 9d7eaa6f8..1281b1479 100644 --- a/layout_cursor.h +++ b/layout_cursor.h @@ -25,7 +25,7 @@ public: printf("\n"); } - bool isDefined() const { return m_layoutRef.isDefined(); } + bool isDefined() { return m_layoutRef.isDefined(); } /* Getters and setters */ diff --git a/layout_reference.cpp b/layout_reference.cpp index ca87dc84b..505ae3f57 100644 --- a/layout_reference.cpp +++ b/layout_reference.cpp @@ -11,9 +11,9 @@ TreeNode * LayoutRef::failedAllocationNode() { } template -LayoutCursor LayoutReference::cursor() const { +LayoutCursor LayoutReference::cursor() { return LayoutCursor(this->castedNode()); } -template LayoutCursor LayoutReference::cursor() const; -template LayoutCursor LayoutReference::cursor() const; +template LayoutCursor LayoutReference::cursor(); +template LayoutCursor LayoutReference::cursor(); diff --git a/layout_reference.h b/layout_reference.h index c93316ce7..606e8f037 100644 --- a/layout_reference.h +++ b/layout_reference.h @@ -14,13 +14,13 @@ public: /* Allow every LayoutReference to be transformed into a * LayoutReference, i.e. Layout */ - operator LayoutReference() const { + operator LayoutReference() { return LayoutReference(this->node()); } static TreeNode * failedAllocationNode(); - LayoutCursor cursor() const; + LayoutCursor cursor(); LayoutReference childAtIndex(int i) { TreeReference treeRefChild = TreeReference::treeChildAtIndex(i); diff --git a/test.cpp b/test.cpp index b66e3440b..80f6df654 100644 --- a/test.cpp +++ b/test.cpp @@ -126,7 +126,7 @@ void testPoolExpressionAllocationFail() { f1.replaceWith(f11); float result2 = a1.approximate(); - assert(result2 == 0); + assert(result2 == -1); } void testPoolExpressionAllocationFail2() { @@ -211,7 +211,7 @@ int main() { runTest(testPoolEmpties); runTest(testCursorCreateAndRetain); runTest(testCursorMoveLeft); - //runTest(testPoolExpressionAllocationFail); + runTest(testPoolExpressionAllocationFail); runTest(testPoolExpressionAllocationFail2); printf("\n*******************\nEnd of tests\n*******************\n\n"); return 0; diff --git a/tree_node.cpp b/tree_node.cpp index d4ae12a57..6a2d4e612 100644 --- a/tree_node.cpp +++ b/tree_node.cpp @@ -8,23 +8,31 @@ void TreeNode::release() { m_referenceCounter--; if (m_referenceCounter == 0) { - if (numberOfChildren() != 0) { - int lastIdentifier = lastChild()->identifier(); - TreeNode * child = next(); - bool lastChildReleased = false; - while (!lastChildReleased) { - lastChildReleased = child->identifier() == lastIdentifier; - int nextSiblingIdentifier = lastChildReleased ? -1 : child->nextSibling()->identifier(); - child->release(); - if (nextSiblingIdentifier != -1) { - child = TreePool::sharedPool()->node(nextSiblingIdentifier); - } + releaseChildrenAndDestroy(); + } +} + +void TreeNode::releaseChildren() { + if (numberOfChildren() != 0) { + int lastIdentifier = lastChild()->identifier(); + TreeNode * child = next(); + bool lastChildReleased = false; + while (!lastChildReleased) { + lastChildReleased = child->identifier() == lastIdentifier; + int nextSiblingIdentifier = lastChildReleased ? -1 : child->nextSibling()->identifier(); + child->release(); + if (nextSiblingIdentifier != -1) { + child = TreePool::sharedPool()->node(nextSiblingIdentifier); } } - TreePool::sharedPool()->discardTreeNode(this); } } +void TreeNode::releaseChildrenAndDestroy() { + releaseChildren(); + TreePool::sharedPool()->discardTreeNode(this); +} + // Hierarchy TreeNode * TreeNode::parentTree() const { diff --git a/tree_node.h b/tree_node.h index a56e63f06..b662d3f5c 100644 --- a/tree_node.h +++ b/tree_node.h @@ -31,6 +31,8 @@ public: virtual void init(float f) {} void retain() { m_referenceCounter++; } void release(); + void releaseChildren(); + void releaseChildrenAndDestroy(); void rename(int identifier) { m_identifier = identifier; m_referenceCounter = 1; @@ -102,6 +104,9 @@ public: return reinterpret_cast(reinterpret_cast(const_cast(this)) + size()); } + // Hierarchy operations + void moveAndReleaseAllChildren(); + protected: TreeNode() : m_identifier(-1), diff --git a/tree_pool.h b/tree_pool.h index 002eecbc3..96a6edd1e 100644 --- a/tree_pool.h +++ b/tree_pool.h @@ -8,6 +8,7 @@ class TreePool { friend class TreeNode; public: + static constexpr int AllocationFailureIdentifier = 0; static TreePool * sharedPool(); // Node @@ -19,7 +20,7 @@ public: int nodeIdentifier = generateIdentifier(); if (nodeIdentifier == -1) { T::failedAllocationNode()->retain(); - return T::failedAllocationNode(); // TODO return static node "failedAllocation" + return T::failedAllocationNode(); } void * ptr = alloc(sizeof(T)); if (ptr == nullptr) { @@ -46,9 +47,34 @@ public: return copy; } - void log(); + // Allocation failure handling + + bool nodeWasReplacedWithAllocationFailure(int id) { + return m_allocationFailureRetainCount[id] > 0; + } + + void releaseAllocationFailure(int id) { + int currrentRetainCount = m_allocationFailureRetainCount[id]; + assert(currrentRetainCount > 0); + m_allocationFailureRetainCount[id] = currrentRetainCount - 1; + if (m_allocationFailureRetainCount[id] == 0) { + freeIdentifier(id); + } + } + + void registerIdentiferAsAllocationFailure(int id, int retainCount) { + if (retainCount == 0) { + freeIdentifier(id); + } else { + assert(id >= 0 && id < MaxNumberOfNodes); + m_nodeForIdentifier[id] = first(); // TODO for now the first node in the pool is the allocation failure node (WARNING when implementing for Layouts... HAVE 2 POOLS?) + m_allocationFailureRetainCount[id] = retainCount; + } + } // Debug + void log(); + int numberOfNodes() const { int count = 0; AllPool nodes = const_cast(this)->allNodes(); @@ -72,6 +98,7 @@ private: void registerNode(TreeNode * node) { m_nodeForIdentifier[node->identifier()] = node; } + void renameNode(TreeNode * node) { node->rename(generateIdentifier()); registerNode(node); @@ -151,6 +178,7 @@ private: char * m_cursor; char m_buffer[BufferSize]; TreeNode * m_nodeForIdentifier[MaxNumberOfNodes]; + int m_allocationFailureRetainCount[MaxNumberOfNodes]; }; #endif diff --git a/tree_reference.h b/tree_reference.h index 3f7626b99..9c456cbce 100644 --- a/tree_reference.h +++ b/tree_reference.h @@ -33,12 +33,15 @@ public: inline bool operator==(TreeReference t) { return m_identifier == t.identifier(); } void setTo(const TreeReference & tr) { - m_identifier = tr.identifier(); - TreePool::sharedPool()->node(m_identifier)->retain(); + setIdentifierAndRetain(tr.identifier()); } - TreeReference clone() const { - TreeNode * nodeCopy = TreePool::sharedPool()->deepCopy(node()); + TreeReference clone() { + TreeNode * myNode = node(); + if (myNode->isAllocationFailure()) { + return TreeReference(TreePool::sharedPool()->node(TreePool::AllocationFailureIdentifier)); + } + TreeNode * nodeCopy = TreePool::sharedPool()->deepCopy(myNode); return TreeReference(nodeCopy); } @@ -50,39 +53,50 @@ public: } } - bool isDefined() const { return m_identifier >= 0 && TreePool::sharedPool()->node(m_identifier) != nullptr; } + bool isDefined() { return m_identifier >= 0 && node() != nullptr; } + bool isAllocationFailure() { return node()->isAllocationFailure(); } - int nodeRetainCount() const { return node()->retainCount(); } + int nodeRetainCount() { return node()->retainCount(); } void incrementNumberOfChildren() { return node()->incrementNumberOfChildren(); } void decrementNumberOfChildren() { return node()->decrementNumberOfChildren(); } - operator TreeReference() const { + operator TreeReference() { return TreeReference(this->node()); } - T * castedNode() const { + T * castedNode() { // TODO: Here, assert that the node type is indeed T // ?? Might be allocation failure, not T + updateIdentifier(); return static_cast(TreePool::sharedPool()->node(m_identifier)); } - TreeNode * node() const { + TreeNode * node() { + updateIdentifier(); return TreePool::sharedPool()->node(m_identifier); } + void updateIdentifier() { + /* If the node was replaced with an allocation failure, change the + * identifier */ + if (TreePool::sharedPool()->nodeWasReplacedWithAllocationFailure(m_identifier)) { + TreePool::sharedPool()->releaseAllocationFailure(m_identifier); + setIdentifierAndRetain(TreePool::AllocationFailureIdentifier); + } + } int identifier() const { return m_identifier; } // Hierarchy - int numberOfChildren() const { + int numberOfChildren() { return node()->numberOfChildren(); } - TreeReference parent() const { + TreeReference parent() { return TreeReference(node()->parentTree()); } - TreeReference treeChildAtIndex(int i) const { + TreeReference treeChildAtIndex(int i) { return TreeReference(node()->childTreeAtIndex(i)); } @@ -105,7 +119,10 @@ public: } void replaceChildAtIndex(int oldChildIndex, TreeReference newChild) { - // TODO decrement the children count of the new child parent + if (newChild.isAllocationFailure()) { + replaceWithAllocationFailure(); + return; + } TreeReference p = newChild.parent(); if (p.isDefined()) { p.decrementNumberOfChildren(); @@ -118,6 +135,32 @@ public: oldChild.node()->release(); } + void replaceWithAllocationFailure() { + TreeReference p = parent(); + int currentRetainCount = node()->retainCount(); + + // Move the node to the end of the pool and decrease children count of parent + TreePool::sharedPool()->move(node(), TreePool::sharedPool()->last()); + if (p.isDefined()) { + p.decrementNumberOfChildren(); + } + + // Release all children and delete the node in the pool + node()->releaseChildrenAndDestroy(); + + /* In case another ref is pointing to the node, register the identifier as + * AllocationFailure. If the retainCount is 1 (this reference is the only + * one pointing to the node), the pool will register the identifier as + * free. */ + TreePool::sharedPool()->registerIdentiferAsAllocationFailure(m_identifier, currentRetainCount - 1); + setIdentifierAndRetain(TreePool::AllocationFailureIdentifier); + + // Replace parent with AllocationFailure + if (p.isDefined()) { + p.replaceWithAllocationFailure(); + } + } + void swapChildren(int i, int j) { assert(i >= 0 && i < numberOfChildren()); assert(j >= 0 && j < numberOfChildren()); @@ -143,10 +186,14 @@ protected: if (node == nullptr) { m_identifier = -1; } else { - m_identifier = node->identifier(); - node->retain(); + setIdentifierAndRetain(node->identifier()); } } + void setIdentifierAndRetain(int newId) { + m_identifier = newId; + node()->retain(); + } +private: int m_identifier; };