From 6075c82a95678d1c135594dbda6aa97467080a5c Mon Sep 17 00:00:00 2001 From: dragoncoder047 <101021094+dragoncoder047@users.noreply.github.com> Date: Fri, 31 May 2024 12:47:12 -0400 Subject: [PATCH] floatingimages overlap problem --- docs/2024/a-hash-mapped-mess/index.html | 8 ++++++++ markdown/pickle_slow_progress.md | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/docs/2024/a-hash-mapped-mess/index.html b/docs/2024/a-hash-mapped-mess/index.html index b1759e5..5b96335 100644 --- a/docs/2024/a-hash-mapped-mess/index.html +++ b/docs/2024/a-hash-mapped-mess/index.html @@ -138,15 +138,23 @@

Except, in practice, it’s not that simple.

Obviously, hashmaps for object properties aren’t static – they will have properties inserted, updated, and removed. Updating an existing property is almost trivial – you just find the corresponding node in the hashmap and replace the value.

The algorithms outlined for adding or deleting a property end up causing problems. Deleting a property just clears the node, it doesn’t delete the node, and so the property-addition algorithm can check for and re-use cleared nodes instead of creating new children. This means that a simple add-or-update implementation might end up accidentally inserting the value twice, because it found a cleared node higher-up in the tree than the existing node’s position and stopped too soon.

+

Consider what happens if you have, say, three nodes called foo, bar, and baz. First foo is inserted to an empty tree, so it becomes the root node, Then bar and baz are added, and become children of foo.

+
+

Now foo is deleted. The first node matching it is cleared - no problem. There are no foos in the tree.

+
+

bar is updated. Since there is a free node above the old bar, there ends up two bar nodes.

Up until now, there isn’t any problem. Finding any node, even in the tree with the duplicated bar, finds the correct value.

+
+

The problem arises when you try to delete bar on this duplicated tree – and since the old bar node wasn’t ever deleted, this “shadow” node now rears it ugly head and causes the key bar to revert to its old value, instead of being deleted like it was supposed to be.

+

I spent a long time trying to figure out how to combat this problem. The easiest solution, which I implemented, is to traverse the entire hash’s search path, not just stopping at the first free node, when updating a value. If the new value is set by filling a free node (rather than simply updating the existing node with the same key), there may be shadow nodes under it, so the rest of the tree has to be traversed, and these shadow nodes cleared. This guarantees there will only be one node for any given key in use at the same time.


The one last bug, that I still haven’t fixed, is the way these hashmaps work with the garbage collector. When you delete a node, you don’t actually delete the node, you just clear it / mark it as free. The memory is still in use as far as the garbage collector is concerned, and is never freed. Even if the an object has no properties currently, if the object had, say, a thousand different properties at some point in the past, there will now be a thousand unused nodes in the hashmap that the garbage collector just won’t collect.

diff --git a/markdown/pickle_slow_progress.md b/markdown/pickle_slow_progress.md index 7b4e199..da92de6 100644 --- a/markdown/pickle_slow_progress.md +++ b/markdown/pickle_slow_progress.md @@ -15,6 +15,7 @@ Obviously, hashmaps for object properties aren't static -- they will have proper The algorithms outlined for adding or deleting a property end up causing problems. Deleting a property just clears the node, it doesn't delete the node, and so the property-addition algorithm can check for and re-use cleared nodes instead of creating new children. This means that a simple add-or-update implementation might end up accidentally inserting the value twice, because it found a cleared node higher-up in the tree than the existing node's position and stopped too soon. +
```{.mermaid .float-right} graph TD foo --> bar @@ -22,7 +23,9 @@ graph TD ``` Consider what happens if you have, say, three nodes called `foo`, `bar`, and `baz`. First `foo` is inserted to an empty tree, so it becomes the root node, Then `bar` and `baz` are added, and become children of `foo`. +
+
```{.mermaid .float-right} graph TD foo[ ] --> bar @@ -30,7 +33,9 @@ graph TD ``` Now `foo` is deleted. The first node matching it is cleared - no problem. There are no `foo`s in the tree. +
+
```{.mermaid .float-right} graph TD foo["bar (new)"] --> bar["bar (old)"] @@ -40,7 +45,9 @@ graph TD `bar` is updated. Since there is a free node above the old `bar`, there ends up two `bar` nodes. Up until now, there isn't any problem. Finding any node, even in the tree with the duplicated `bar`, finds the correct value. +
+
```{.mermaid .float-right} graph TD foo[ ] --> bar["bar (old)"] @@ -48,6 +55,7 @@ graph TD ``` The problem arises when you try to delete `bar` on this duplicated tree -- and since the old `bar` node wasn't ever deleted, this "shadow" node now rears it ugly head and causes the key `bar` to revert to its old value, instead of being deleted like it was supposed to be. +
I spent a long time trying to figure out how to combat this problem. The easiest solution, which I implemented, is to traverse the entire hash's search path, not just stopping at the first free node, when updating a value. If the new value is set by filling a free node (rather than simply updating the existing node with the same key), there may be shadow nodes under it, so the rest of the tree has to be traversed, and these shadow nodes cleared. This guarantees there will only be one node for any given key in use at the same time.