a new extractEventE to fix an old memory leak

23 views
Skip to first unread message

Arjun Guha

unread,
Sep 8, 2011, 11:53:08 AM9/8/11
to fla...@googlegroups.com
Flapjax programs with nested event handlers can suffer memory leaks. The
drag and drop example in the OOPSLA paper is such a program and Section 3.5
of the paper tries to address the problem. I think the alternate solution
described below is cleaner.

Flapjax's primitive for turning DOM events into event streams is
extractEventE. All other DOM event streams, with the exception of setTimeout,
are thin wrappers around this primitive.

ExtractEventE calls element.addEventlListener, but
there is no corresponding call to element.removeEventListener.

Consider the drag-and-drop example in the Flapjax paper:


Drag-and-drop creates a new stream of mousemove events for each mousedown
event.  Each new stream calls addEventListener('mousemove', ...) anew, so on
the Nth drag we have N mousemove handlers on the draggable event. This is the
memory leak.

If you add a console.log in the inner event transformer, you'll see
two moves printed on the second drag, 3 on the third drag, etc. The OOPSLA 
paper briefly describes how Flapjax avoids the leak, but the technique is messy
 to implement and difficult to understand. It's not clear when event handlers
 are "collected" by Flapjax.

--

I suggest we introduce a new extractEventE function with the following
signature:

extractEventE : Behavior (DOMElement + False)
              * String // event name
             -> EventStream DOMEvent

The key difference is that the element-behavior may be false.
When a new false value is received, detach the previous DOM event handler
and do nothing else, producing an empty stream of events.

--

With this primitive, we can implement a memory-safe dragE that explicitly
detaches the old mousemove event. Previously, dragE had the following shape:
  
  var upE = extractEventE(document, 'mouseup');

  extractEventE(target,'mousedown').mapE(function(md) {
    ... extractEventE(target, 'mousemove') ...
  })

With the new extractEventE, we make the following small change:
  
  var upE = extractEventE(document, 'mouseup');

  extractEventE(target,'mousedown').mapE(function(md) {
    var falseE = upE.constantE(false);
    ... extractEventE(falseE.startsWith(target), 'mousemove') ...
  })

I think explicit detaching is the right model for Flapjax, since we cannot
reliably detach native event handlers automatically. Even if we could, using
weak references, I think explicit control would still be desirable.

--

This commit has the new extractEventE function and the re-written
drag-and-drop example:


This commit cleans up the half-baked attempt at weak references I did for
OOPSLA:


Arjun

Reply all
Reply to author
Forward
0 new messages