[Laszlo-user] [Laszlo-dev] API change proposal: <node>/destroy behavior, when destroying child nodes recursively

P T Withington ptw at pobox.com
Tue Jan 27 07:47:56 PST 2009


On 2009-01-27, at 09:46EST, André Bargull wrote:

>
> On 1/27/2009 1:03 AM, P T Withington wrote:
>> On 2009-01-26, at 18:24EST, André Bargull wrote:
>>> Oh wait, you just said nothing in the LFC depends on the current  
>>> way things work. I really believed in you,
>> But wait, I said:
>> "where a child node _needs_ to access it's parent when the parent  
>> is in the process of being  deleted"
>> so, there may be cases where it _does_ access the parent, but does  
>> it _need_ to?  I don't think it does. So it is just a matter of  
>> fixing those cases to be careful.
>
> How do you plan to find those cases?

One way would be to set parent to null and then fix the bugs it reveals.

> Whenever (immediate)parent is referenced, you need to add extra if- 
> condition to protect against nullpointer dereferencing.

The alternative is to make __LZdeleted public and say:

"Everywhere you would have had to check for parent being null, instead  
check to see if __LZdeleted is true".

It works out to the same amount of work, but in the latter case, bugs  
go undetected.

> For example what is going to happen for constraints? The example [2]  
> isn't a real-life example (it makes no sense to do anything like  
> that), but through replication [1] and other means, you can end up  
> destroying the parent-node and then it's really difficult to find  
> the source of possible runtime errors.

This is exactly what led me down this path.  I was looking at  
replication in nav.lzx and realized that it was making hundreds of  
useless operations because as each child was destroyed, it told the  
parent to update its layout, which caused the parent to adjust the  
position of each child (even though that child would be destroyed soon).

>> Both of your examples remind me of the chainsaw user who cuts the  
>> branch he is standing on and then complains that the chainsaw did  
>> not prevent him from a horrible accident.  Are there really nodes  
>> that believe they can delete their own parent (knowing that will  
>> result in their own deletion)?
>
> For example an one-way message dialog, if you close the dialog it  
> should be destroyed, too. So you add a button which calls destroy on  
> the parent-node on "onclick".
>
> And I can also make the examples less obvious [1]:
> - when replication is set to lazy, everything works
> - but when replication is "normal", the app breaks and produces the  
> same error as in my earlier example

Doesn't this just indicate a bug in the interaction of replication and  
the focus manager?  My take is that you are updating your dataset, so  
the replicator destroys and re-allocates nodes to represent the  
updated dataset, but the focusmanager is still hanging on to the  
destroyed node.

In the long run, the focus manager will leak nodes... which is a bug,  
to me.

Either the focus manager needs to store its state in terms of "dom  
position" (i.e., a path to the node that has focus, rather than a  
reference to the actual node), or the replication manager has to  
cooperate with the focus manager to tell it when it has replaced the  
node that has focus.

Why does it work for lazy replication?  Because the lazy replicator is  
much more careful not to destroy a node that maps to a data element  
that is still in the range when it is updated -- it leaves those nodes  
in place.  This would be one way to fix the bug for normal  
replication, and it would presumably make it more efficient too!   
Change your example to show the node ID's and you will see what I mean:

<canvas debug="true" >
  <dataset name="ds" ><item /></dataset>
  <view width="100%" height="100%" layout="axis:y" >
    <view>
      <datapath xpath="ds:/item" replication="lazy" />
      <view width="180" height="40" bgcolor="#eaeaea" clickable="true"  
focusable="true" >
        <text text="${this.__LZUID + (this.__LZdeleted ?  
' (deleted)' : '')}" />
        <handler name="onclick" >
          canvas.ds.appendChild(new lz.DataElement("item"));
        </handler>
      </view>
    </view>
  </view>
</canvas>

In a perfect world, we would not need to destroy nodes, because we  
have a garbage-collected runtime, but we have to at least know how to  
unhook a node from the node heirarchy so that the garbage collector  
can collect it.  This is what destroy is all about.  The change I  
propose will help us to find the places in the runtime that are  
hanging on to nodes that we intended to garbage collect (destroyed),  
and help reduce leaks.




More information about the Laszlo-user mailing list