Yes, thanks again to André for his stellar contributions!

Regards,
Max Carlson
OpenLaszlo.org

On 3/29/10 2:48 PM, P T Withington wrote:
Max, clearly these all need to be addressed, either before you check in or in 
an amendment to your checkin.

Thank you as usual André for a most thorough review!

On 2010-03-29, at 16:45, André Bargull wrote:

Did anybody said comments are no longer allowed to delay the checkin?  :-P

LzTextSprite (swf8):
+  if (! this['scrolldel']) {
+    this.scrolldel = new LzDelegate(this, "__LZupdateScrollAttrs");
+  }
and later:
+    if (this.scrolldel.hasevents) {
+        this.scrolldel.destroy();
+    }
I'm asking myself why you've used the "save" property access (bracket syntax) when no 
harm can occur, but later the "brute force" method (scrolldel could be undefined)? I'd 
just remove the bracket syntax and use direct property access.


LzXMLLoader
+        // To keep delegates from resurrecting us.  See LzDelegate#execute
+        this.__LZdeleted = true;
LzXMLLoader is a LzLoader, which is a LzEventable, so you must not set 
_LZdeleted.

LzDelegate
-        } else {
-            keep.push( ev );
+            this.__events.splice(i, 1);
+            break;
         }
You cannot use 'break' here, a delegate can be registered to an event more than 
once.

LzHTTPDataProvider
+    /**
+     * @access private
+     * @param LzHTTPDataRequest dreq:
+     * @param LzDataElement data:
+     */
+    override function destroy() {
Wrong comment..

LzDataset
override function destroy () {
     this.$lzc$set_childNodes([ ]);
+    this.dataprovider.destroy();
You must not destroy the dataprovider, dataproviders are singleton objects! For 
example the default dataprovider for a dataset is canvas.defaultdataprovider, 
so all datasets share the same dataprovider!





On 3/29/2010 8:05 PM, P T Withington wrote:
Approved.

Let's check this in so we can get some QA on it (and before someone thinks up 
another amendment to delay the vote).

Nice work on a thorny problem!

On 2010-03-24, at 17:45, Max Carlson wrote:


Change 20100314-maxcarlson-0 by maxcarl...@bank on 2010-03-14 00:54:51 PST
    in /Users/maxcarlson/openlaszlo/trunk-clean
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: UPDATE 6: Automatically remove invalid delegates in 
LzEventable.destroy()

Bugs Fixed: LPP-8822 - Improve layout performance

Technical Reviewer: ptw
QA Reviewer: hminsky

Release Notes: It is no longer necessary to manually track and release 
delegates whose target is 'this' to prevent memory leaks.  This is handled 
automatically by the event system.

Details: Updated to address Tucker's comments:
General comment:

It really sucks that LzDataAttrBind pretends to be an LzDelegate but isn't 
really.  We probably should use interface/implements to make that more 
explicit.  At least then the swf10 back-end could give us some help.

Nits:

1) LzEventable#105 Not sure what this means:


      // uncomment to LFC delegates cleaned up, e.g. datapath and inputtext
      //if ($debug) Debug.warn('removing %w delegates from %w.', 
this.__delegates.length, this);

I removed this.

2) LzNode#400:

  /** Tracks constraint delegates
      @access private */

Might be better to say:

  /** Tracks delegates and data bindings for states so they can be 
applied/removed

because that is what we have reduced this to.  Perhaps someday we can come up 
with a less kludgey mechanism than state reaching out and grabbing these things 
from the node constructor.

Done.

Issues:

1) LZNode#2080

    //remove __LZconstraintdelegates
    // We have to do this to remove LzDataAttrBinds

I don't follow.  It seems to me that in LzNode/dataBindAttribute, the 
LzDataAttrBind object needs to go on __LZconstraintDelegates, so it can be 
applied/unapplied by LzState.  But LzDataAttrBind's constructor should register 
the binding on __delegates so that it can be auto-cleaned when the node it is 
binding is destroyed.  We really want to separate these two tasks so future 
generations don't have to work this out again.  Then you can truly just let 
__LZconstraintdelegates drop on the floor in LzNode/destroy.

Yeah, you're right!  Done.

2) LzDataAttrBind/unregisterAll:  Does anyone actually call this any more?  
LzEventable/destroy should just be calling LzDataAttrBind/destroy

Removed unregisterAll().

3) LaszloEvents#227:  There needs to be a comment here.  Something like "Delegates 
between LzEventables can cause memory leaks.  We record such delegates so they can be 
cleaned up in destroy".

Done.

4) LaszloEvents#227:  Do you not have to test that the context _is_ actually an 
LzEventable here?  At least in the runtimes that don't enforce types?

Ooh good point!  I improved the testing and ensured LzDelegate.__tracked is 
correctly maintained.

5) LaszloView#1795:  Why did you un-deprecate releaseLayouts?  I thought we 
were trying to get rid of this?

I removed it since it's deprecated in 4.7.1 - but there doesn't seem to be a 
replacement.  It could be useful when you want to move things around without 
layouts interfering - but you could lock/unlock the layouts instead.  Maybe we 
should make the layouts array public so folks could do this?  The replication 
manager does this during updates...

Otherwise, the same as r5 (Updated to address Andre's comments):
LzDelegate:

+    if (eventSender !== this.c) {
+      var _dels:Array = this.c['__delegates'];
+      if (_dels == null) {
+        this.c.__delegates = [ this ];
+      } else {
+        _dels.push( this );
+      }
+    }

1) This will create multiple entries for each delegate, you should add a 
boolean flag to track whether the delegate was already added to the __delegates 
array to prevent more than one entry.

Done.

2) What about the issue with the swf8 kernel using LzDelegates?

I updated the swf8 kernel to be very careful about unregistering all delegates 
used on instances.


LzDelegate:

+public function destroy ():void {
+    this.unregisterAll();
+    this.c = null;
+    this.m = null;
+}

Please add "@access private" to hide the function from the docs - can you 
remind me why functions are added to the docs by default?! There is a JIRA issue to 
switch this default behaviour, isn't it?

Done.  I think it's that way to force developers to notice that they need to 
write documentation...

LzNode:

+      for ( var i = dels.length - 1; i>= 0; i-- ){
+        if (dels[i].c) {
+          dels[ i ].destroy();
+        }
       }

Won't work for LzDataAttrBind - there is no "c" attribute, so the if-test will 
fail.

Done.  I added a __LZdeleted flag to LzDelegate that can be used instead.

LaszloEvents - Add __events array to track events registered on the delegate 
and make LzDelegate static.  Add hasevents boolean to track whether calls to 
unregisterAll() are needed.  Add __tracked flag to prevent adding to 
__delegates more than once.  Add __LZdeleted flag to track when to destroy(), 
mirroring LzEventable.

LzHTTPDataProvider - Track LzHTTPLoaders and call destroy() on each one when 
destroyed.

LzDataset - Call destroy() on the dataprovider when destroyed.

kernel/*/LzHTTPLoader - Add destroy() method which is called when a loader 
should be destroyed.  Add stub methods for swf9 and dhtml.

LzState, LzNode - Use LzDelegate/LzDataAttrBind.__LZdeleted to decide whether 
to call destroy().

kernel/swf/* - Ensure all delegates not used by global services are tracked and 
destroyed.  Use new hasevents flag to avoid unneeded calls to unregisterAll().

Otherwise, the same:

LzNode,LzState - Rename __LZdelegates ->   __LZconstraintdelegates, shorten 
array lookups where possible, use LzDelegate.destroy() instead of unregisterAll().

LaszloEvents - Add check/warning when a delegate is created for an invalid 
context.  Move __delegates tracking to register() and only add LzEventables 
that are registering for a different context.  Add warnings for unneeded 
unregisterAll() calls, but comment them out due to too many warnings.  Add 
destroy() API that neuters and unregisters the delegate.

LzDataAttrBind - Add destroy() API for symmetry with LzDelegate.

LzDatapath - Use super.destroy() for efficiency.

LaszloView - releaseLayouts() now calls layout.destroy().

LzState - Rename __LZstatedelegates ->   __LZstateconstraintdelegates.

Otherwise, the same as before:

LzInputText,LzAnimatorGroup,LzDatapointer,LzDataset - Remove extra delegate 
unregistration as it's now handled by the event system.

LzEventable - Add _delegates array to hold delegates that have a context 
pointing to this object.  Clean out delegate contexts in destroy().

LaszloEvents - LzDelegate registers on its contexts' _delegates array.

LaszloLayout,resizelayout - don't track delegates anymore.


Tests: Run modified leak-components test in swf10 - the memory monitor in the 
upper left shows much less leakage with this patch: you can click the monitor 
to force garbage collection.  Also run smokecheck across runtimes.

Files:
M       test/components/lz/leak-components.lzx
M       WEB-INF/lps/lfc/kernel/swf/LzTextSprite.as
M       WEB-INF/lps/lfc/kernel/swf/LzMediaLoader.lzs
M       WEB-INF/lps/lfc/kernel/swf/LzInputTextSprite.as
M       WEB-INF/lps/lfc/kernel/swf/LzXMLLoader.lzs
M       WEB-INF/lps/lfc/kernel/swf/LzMakeLoadSprite.as
M       WEB-INF/lps/lfc/kernel/swf/LzHTTPLoader.as
M       WEB-INF/lps/lfc/kernel/swf/LzSprite.as
M       WEB-INF/lps/lfc/kernel/dhtml/LzHTTPLoader.js
M       WEB-INF/lps/lfc/kernel/swf9/LzHTTPLoader.as
M       WEB-INF/lps/lfc/core/LzNode.lzs
M       WEB-INF/lps/lfc/core/LzEventable.lzs
M       WEB-INF/lps/lfc/views/LzInputText.lzs
M       WEB-INF/lps/lfc/views/LaszloView.lzs
M       WEB-INF/lps/lfc/helpers/LzState.lzs
M       WEB-INF/lps/lfc/events/LaszloEvents.lzs
M       WEB-INF/lps/lfc/controllers/LzAnimatorGroup.lzs
M       WEB-INF/lps/lfc/controllers/LaszloLayout.lzs
M       WEB-INF/lps/lfc/data/LzDatapointer.lzs
M       WEB-INF/lps/lfc/data/LzHTTPDataProvider.lzs
M       WEB-INF/lps/lfc/data/LzDataset.lzs
M       WEB-INF/lps/lfc/data/LzDatapath.lzs
M       WEB-INF/lps/lfc/data/LzDataAttrBind.lzs
M       docs/src/developers/delegates.dbk
M       lps/components/rpc/ajax.lzx
M       lps/components/extensions/html.lzx
M       lps/components/utils/layouts/resizelayout.lzx
M       lps/components/utils/replicator/replicator.lzx
M       lps/components/av/rtmpstatus.lzx
M       lps/components/base/basetabelement.lzx
M       lps/components/base/basescrollbar.lzx
M       lps/components/base/basecomponent.lzx
M       lps/components/base/basewindow.lzx
M       lps/components/base/submit.lzx
M       lps/components/base/basetrackgroup.lzx
M       lps/components/base/basedatacombobox.lzx

Changeset: 
http://svn.openlaszlo.org/openlaszlo/patches/20100314-maxcarlson-0.tar







Reply via email to