mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-23 15:50:49 +01:00
Fix AllocationFailure when multiple instances
This commit is contained in:
@@ -6,3 +6,8 @@ TreeNode * ExpressionRef::failedAllocationNode() {
|
||||
static AllocationFailedExpressionRef FailureRef;
|
||||
return FailureRef.node();
|
||||
}
|
||||
|
||||
template<>
|
||||
ExpressionReference<ExpressionNode> ExpressionRef::failedAllocationRef() {
|
||||
return ExpressionReference<ExpressionNode>(failedAllocationNode());
|
||||
}
|
||||
|
||||
@@ -12,10 +12,11 @@ public:
|
||||
using TreeReference<T>::TreeReference;
|
||||
|
||||
// Allow every ExpressionReference<T> to be transformed into an ExpressionReference<ExpressionNode>, i.e. Expression
|
||||
operator ExpressionReference<ExpressionNode>() const {
|
||||
operator ExpressionReference<ExpressionNode>() {
|
||||
return ExpressionReference<ExpressionNode>(this->node());
|
||||
}
|
||||
|
||||
static ExpressionReference<ExpressionNode> failedAllocationRef();
|
||||
static TreeNode * failedAllocationNode();
|
||||
|
||||
void addChild(ExpressionReference<ExpressionNode> e) {
|
||||
@@ -32,7 +33,7 @@ public:
|
||||
TreeReference<T>::replaceChildAtIndex(oldChildIndex, newChild);
|
||||
}
|
||||
|
||||
float approximate() const {
|
||||
float approximate() {
|
||||
return this->castedNode()->approximate();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
bool isDefined() const { return m_layoutRef.isDefined(); }
|
||||
bool isDefined() { return m_layoutRef.isDefined(); }
|
||||
|
||||
|
||||
/* Getters and setters */
|
||||
|
||||
@@ -11,9 +11,9 @@ TreeNode * LayoutRef::failedAllocationNode() {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
LayoutCursor LayoutReference<T>::cursor() const {
|
||||
LayoutCursor LayoutReference<T>::cursor() {
|
||||
return LayoutCursor(this->castedNode());
|
||||
}
|
||||
|
||||
template LayoutCursor LayoutReference<LayoutNode>::cursor() const;
|
||||
template LayoutCursor LayoutReference<CharLayoutNode>::cursor() const;
|
||||
template LayoutCursor LayoutReference<LayoutNode>::cursor();
|
||||
template LayoutCursor LayoutReference<CharLayoutNode>::cursor();
|
||||
|
||||
@@ -14,13 +14,13 @@ public:
|
||||
|
||||
/* Allow every LayoutReference<T> to be transformed into a
|
||||
* LayoutReference<LayoutNode>, i.e. Layout */
|
||||
operator LayoutReference<LayoutNode>() const {
|
||||
operator LayoutReference<LayoutNode>() {
|
||||
return LayoutReference<LayoutNode>(this->node());
|
||||
}
|
||||
|
||||
static TreeNode * failedAllocationNode();
|
||||
|
||||
LayoutCursor cursor() const;
|
||||
LayoutCursor cursor();
|
||||
|
||||
LayoutReference<LayoutNode> childAtIndex(int i) {
|
||||
TreeReference<T> treeRefChild = TreeReference<T>::treeChildAtIndex(i);
|
||||
|
||||
4
test.cpp
4
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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<TreeNode *>(reinterpret_cast<char *>(const_cast<TreeNode *>(this)) + size());
|
||||
}
|
||||
|
||||
// Hierarchy operations
|
||||
void moveAndReleaseAllChildren();
|
||||
|
||||
protected:
|
||||
TreeNode() :
|
||||
m_identifier(-1),
|
||||
|
||||
32
tree_pool.h
32
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<TreePool *>(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
|
||||
|
||||
@@ -33,12 +33,15 @@ public:
|
||||
inline bool operator==(TreeReference<TreeNode> 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<T> clone() const {
|
||||
TreeNode * nodeCopy = TreePool::sharedPool()->deepCopy(node());
|
||||
TreeReference<T> clone() {
|
||||
TreeNode * myNode = node();
|
||||
if (myNode->isAllocationFailure()) {
|
||||
return TreeReference<T>(TreePool::sharedPool()->node(TreePool::AllocationFailureIdentifier));
|
||||
}
|
||||
TreeNode * nodeCopy = TreePool::sharedPool()->deepCopy(myNode);
|
||||
return TreeReference<T>(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<TreeNode>() const {
|
||||
operator TreeReference<TreeNode>() {
|
||||
return TreeReference<TreeNode>(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<T*>(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<T> parent() const {
|
||||
TreeReference<T> parent() {
|
||||
return TreeReference(node()->parentTree());
|
||||
}
|
||||
|
||||
TreeReference<T> treeChildAtIndex(int i) const {
|
||||
TreeReference<T> treeChildAtIndex(int i) {
|
||||
return TreeReference(node()->childTreeAtIndex(i));
|
||||
}
|
||||
|
||||
@@ -105,7 +119,10 @@ public:
|
||||
}
|
||||
|
||||
void replaceChildAtIndex(int oldChildIndex, TreeReference<TreeNode> newChild) {
|
||||
// TODO decrement the children count of the new child parent
|
||||
if (newChild.isAllocationFailure()) {
|
||||
replaceWithAllocationFailure();
|
||||
return;
|
||||
}
|
||||
TreeReference<TreeNode> p = newChild.parent();
|
||||
if (p.isDefined()) {
|
||||
p.decrementNumberOfChildren();
|
||||
@@ -118,6 +135,32 @@ public:
|
||||
oldChild.node()->release();
|
||||
}
|
||||
|
||||
void replaceWithAllocationFailure() {
|
||||
TreeReference<TreeNode> 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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user