<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE bugzilla SYSTEM "https://bugs.webkit.org/page.cgi?id=bugzilla.dtd">

<bugzilla version="5.0.4.1"
          urlbase="https://bugs.webkit.org/"
          
          maintainer="admin@webkit.org"
>

    <bug>
          <bug_id>154015</bug_id>
          
          <creation_ts>2016-02-08 16:02:41 -0800</creation_ts>
          <short_desc>Modern IDB: Ref-cycles and leaks</short_desc>
          <delta_ts>2016-12-14 20:40:50 -0800</delta_ts>
          <reporter_accessible>1</reporter_accessible>
          <cclist_accessible>1</cclist_accessible>
          <classification_id>1</classification_id>
          <classification>Unclassified</classification>
          <product>WebKit</product>
          <component>WebCore Misc.</component>
          <version>Safari 9</version>
          <rep_platform>Unspecified</rep_platform>
          <op_sys>Unspecified</op_sys>
          <bug_status>ASSIGNED</bug_status>
          <resolution></resolution>
          
          <see_also>https://bugs.webkit.org/show_bug.cgi?id=158004</see_also>
          <bug_file_loc></bug_file_loc>
          <status_whiteboard></status_whiteboard>
          <keywords>InRadar</keywords>
          <priority>P2</priority>
          <bug_severity>Normal</bug_severity>
          <target_milestone>---</target_milestone>
          <dependson>154032</dependson>
    
    <dependson>154054</dependson>
    
    <dependson>154061</dependson>
    
    <dependson>154110</dependson>
    
    <dependson>154187</dependson>
    
    <dependson>158093</dependson>
    
    <dependson>158632</dependson>
    
    <dependson>158643</dependson>
    
    <dependson>158694</dependson>
          <blocked>165889</blocked>
          <everconfirmed>1</everconfirmed>
          <reporter name="Brady Eidson">beidson</reporter>
          <assigned_to name="Brady Eidson">beidson</assigned_to>
          <cc>achristensen</cc>
    
    <cc>ap</cc>
    
    <cc>darin</cc>
    
    <cc>ggaren</cc>
          

      

      

      

          <comment_sort_order>oldest_to_newest</comment_sort_order>  
          <long_desc isprivate="0" >
    <commentid>1163049</commentid>
    <comment_count>0</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-08 16:02:41 -0800</bug_when>
    <thetext>Modern IDB: Ref-cycles and leaks

IDBDatabase, IDBTransaction, IDBRequest, IDBObjectStore, IDBIndex, IDBCursor...  all ref each other in a big circle of ref cycling.

Let&apos;s fix, shall we?</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1163061</commentid>
    <comment_count>1</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-08 16:50:13 -0800</bug_when>
    <thetext>I&apos;ll be exploring this bit by bit to come up with the best lifetime strategy and logging my findings here.

First finding that directs future exploration:
On a very basic test page that:
-opens a new database
-creates one object store
-lets the transaction finish
-navigates away to a new page

...The IDBOpenDBRequest remains alive with a ref-count of 1. The holder of that ref is the IDBTransaction that represents the version change transaction.

Additionally, there&apos;s a circular reference between the IDBOpenDBRequest and the IDBTransaction.

The transaction doesn&apos;t need the request anymore after the last event has fired, so that ref should be broken, which would break the ref on the transaction itself.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1163280</commentid>
    <comment_count>2</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-09 14:04:26 -0800</bug_when>
    <thetext>Next in line is IDBDatabase:

It has a couple of refs that come and go during event dispatch, all nicely balanced.

It&apos;s initial ref is held as the result of the OpenDBRequest. So as long as the open DB request is cleaned up, we&apos;re fine.

In the case of an upgrade needed, it&apos;s next ref is held by the version change IDBTransaction.

And, in fact, that ref lasts as long as the transaction object, which is &quot;a very very long time.&quot;

To see if there&apos;s anything circular there, I&apos;ll now dig in to the transaction lifetime.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1163322</commentid>
    <comment_count>3</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-09 16:10:30 -0800</bug_when>
    <thetext>(In reply to comment #2)
&gt; Next in line is IDBDatabase:
&gt; 
&gt; It has a couple of refs that come and go during event dispatch, all nicely
&gt; balanced.
&gt; 
&gt; It&apos;s initial ref is held as the result of the OpenDBRequest. So as long as
&gt; the open DB request is cleaned up, we&apos;re fine.
&gt; 
&gt; In the case of an upgrade needed, it&apos;s next ref is held by the version
&gt; change IDBTransaction.
&gt; 
&gt; And, in fact, that ref lasts as long as the transaction object, which is &quot;a
&gt; very very long time.&quot;
&gt; 
&gt; To see if there&apos;s anything circular there, I&apos;ll now dig in to the
&gt; transaction lifetime.

Transactions leak a couple of different ways.

One huge way is that TransactionOperations leak! Yikes. That one is an easy fix.

https://bugs.webkit.org/show_bug.cgi?id=154054 for that</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1163345</commentid>
    <comment_count>4</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-09 16:55:18 -0800</bug_when>
    <thetext>(In reply to comment #3)
&gt; (In reply to comment #2)
&gt; &gt; Next in line is IDBDatabase:
&gt; &gt; 
&gt; &gt; It has a couple of refs that come and go during event dispatch, all nicely
&gt; &gt; balanced.
&gt; &gt; 
&gt; &gt; It&apos;s initial ref is held as the result of the OpenDBRequest. So as long as
&gt; &gt; the open DB request is cleaned up, we&apos;re fine.
&gt; &gt; 
&gt; &gt; In the case of an upgrade needed, it&apos;s next ref is held by the version
&gt; &gt; change IDBTransaction.
&gt; &gt; 
&gt; &gt; And, in fact, that ref lasts as long as the transaction object, which is &quot;a
&gt; &gt; very very long time.&quot;
&gt; &gt; 
&gt; &gt; To see if there&apos;s anything circular there, I&apos;ll now dig in to the
&gt; &gt; transaction lifetime.
&gt; 
&gt; Transactions leak a couple of different ways.
&gt; 
&gt; One huge way is that TransactionOperations leak! Yikes. That one is an easy
&gt; fix.
&gt; 
&gt; https://bugs.webkit.org/show_bug.cgi?id=154054 for that

So with the fix for 154054, and with the previous fix for IDBOpenDBRequests, the most basic example of IDB usage works without leaks.

Attaching that &quot;basic-test.html&quot; that I&apos;ve been using so far.

I&apos;ll start adding to it now to explore other options in the IDB cloud. (Non OpenDB requests, object stores, indexes, cursors...)</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1163347</commentid>
    <comment_count>5</comment_count>
      <attachid>270964</attachid>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-09 17:07:25 -0800</bug_when>
    <thetext>Created attachment 270964
Basic test with object store

This is NOT the basic test I mentioned - it&apos;s the basic test + one created object store.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1163349</commentid>
    <comment_count>6</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-09 17:08:08 -0800</bug_when>
    <thetext>(In reply to comment #5)
&gt; Created attachment 270964 [details]
&gt; Basic test with object store
&gt; 
&gt; This is NOT the basic test I mentioned - it&apos;s the basic test + one created
&gt; object store.

And, no surprise, creating the object store re-introduces some ref cycles.

The object store refs the transaction, so the transaction doesn&apos;t die, and the transaction refs the database, so it doesn&apos;t die either.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1163351</commentid>
    <comment_count>7</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-09 17:11:46 -0800</bug_when>
    <thetext>(In reply to comment #6)
&gt; (In reply to comment #5)
&gt; &gt; Created attachment 270964 [details]
&gt; &gt; Basic test with object store
&gt; &gt; 
&gt; &gt; This is NOT the basic test I mentioned - it&apos;s the basic test + one created
&gt; &gt; object store.
&gt; 
&gt; And, no surprise, creating the object store re-introduces some ref cycles.
&gt; 
&gt; The object store refs the transaction, so the transaction doesn&apos;t die, and
&gt; the transaction refs the database, so it doesn&apos;t die either.

The IDBObjectStore needs to keep a reference to the transaction (IDBObjectStore.transaction), and the transaction has to keep references to all of its object stores for IDBTransaction.objectStore(&lt;name&gt;);

BUT - It only needs to do so until it&apos;s finished.

And I doubt it clears out those references.

Checking now...</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1163385</commentid>
    <comment_count>8</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-09 21:35:29 -0800</bug_when>
    <thetext>(In reply to comment #7)
&gt; (In reply to comment #6)
&gt; &gt; (In reply to comment #5)
&gt; &gt; &gt; Created attachment 270964 [details]
&gt; &gt; &gt; Basic test with object store
&gt; &gt; &gt; 
&gt; &gt; &gt; This is NOT the basic test I mentioned - it&apos;s the basic test + one created
&gt; &gt; &gt; object store.
&gt; &gt; 
&gt; &gt; And, no surprise, creating the object store re-introduces some ref cycles.
&gt; &gt; 
&gt; &gt; The object store refs the transaction, so the transaction doesn&apos;t die, and
&gt; &gt; the transaction refs the database, so it doesn&apos;t die either.
&gt; 
&gt; The IDBObjectStore needs to keep a reference to the transaction
&gt; (IDBObjectStore.transaction), and the transaction has to keep references to
&gt; all of its object stores for IDBTransaction.objectStore(&lt;name&gt;);
&gt; 
&gt; BUT - It only needs to do so until it&apos;s finished.
&gt; 
&gt; And I doubt it clears out those references.
&gt; 
&gt; Checking now...

This was easy to resolve for object stores (https://bugs.webkit.org/show_bug.cgi?id=154061)

But the link between object stores and their indexes will be much more difficult to break.

As long as the object store is reachable by script, it has to keep all of its referenced indexes alive. Similarly, as long as an index is reachable by script, it has to keep its object store alive.

That one will be harder to break.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1163386</commentid>
    <comment_count>9</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-09 21:36:33 -0800</bug_when>
    <thetext>Note for future self: If we come across some cycle that&apos;s &quot;impossible&quot; to break, like possibly the one above, we can at the very least leverage ActiveDOMObject and break them all forcefully when ::stop() is called.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1163847</commentid>
    <comment_count>10</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-02-11 13:36:47 -0800</bug_when>
    <thetext>(In reply to comment #9)
&gt; Note for future self: If we come across some cycle that&apos;s &quot;impossible&quot; to
&gt; break, like possibly the one above, we can at the very least leverage
&gt; ActiveDOMObject and break them all forcefully when ::stop() is called.

This one is definitely possible to break with a great strategy presented by Geoff.

But implementing it perfectly is proving problematic.

Covered by https://bugs.webkit.org/show_bug.cgi?id=154110</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1186563</commentid>
    <comment_count>11</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-04-22 20:16:54 -0700</bug_when>
    <thetext>While I hope to get back to exploring more leaks soon, and Darin has expressed interest in exploring them as well, there&apos;s still a big elephant in the room: Testing.

The strategy implement by Google back when V8 was in the project was &quot;observeGC&quot;

It went as follows:
    objectObserver = internals.observeGC(object);
    object = null;

    gc();

    shouldBeTrue(&quot;objectObserver&quot;);

If we had exactly this, that would be fine.
If we had any other mechanism that allowed for notification or inspection as to whether or not a particular object was GC&apos;ed, that would be fine as well.

Whichever we go with, it seems extremely likely we&apos;d need direct JSC support.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1196445</commentid>
    <comment_count>12</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-05-25 13:42:37 -0700</bug_when>
    <thetext>https://bugs.webkit.org/show_bug.cgi?id=158004 likely introduced some more leaks from Workers</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1196512</commentid>
    <comment_count>13</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-05-25 14:54:38 -0700</bug_when>
    <thetext>I&apos;ve been given a hint on how to implement observeGC-like functionality using existing JSC API.

Filed https://bugs.webkit.org/show_bug.cgi?id=158093 for that</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1212739</commentid>
    <comment_count>14</comment_count>
    <who name="Brady Eidson">beidson</who>
    <bug_when>2016-07-20 08:30:25 -0700</bug_when>
    <thetext>&lt;rdar://problem/27445937&gt;</thetext>
  </long_desc>
      
          <attachment
              isobsolete="0"
              ispatch="0"
              isprivate="0"
          >
            <attachid>270964</attachid>
            <date>2016-02-09 17:07:25 -0800</date>
            <delta_ts>2016-02-09 17:07:25 -0800</delta_ts>
            <desc>Basic test with object store</desc>
            <filename>basic-test.html</filename>
            <type>text/html</type>
            <size>1414</size>
            <attacher name="Brady Eidson">beidson</attacher>
            
              <data encoding="base64">PHNjcmlwdD4KCmZ1bmN0aW9uIGdjKCkgewogICAgaWYgKHR5cGVvZiBHQ0NvbnRyb2xsZXIgIT09
ICJ1bmRlZmluZWQiKQogICAgICAgIEdDQ29udHJvbGxlci5jb2xsZWN0KCk7CiAgICBlbHNlIHsK
ICAgICAgICB2YXIgZ2NSZWMgPSBmdW5jdGlvbiAobikgewogICAgICAgICAgICBpZiAobiA8IDEp
CiAgICAgICAgICAgICAgICByZXR1cm4ge307CiAgICAgICAgICAgIHZhciB0ZW1wID0ge2k6ICJh
YiIgKyBpICsgKGkgLyAxMDAwMDApfTsKICAgICAgICAgICAgdGVtcCArPSAiZm9vIjsKICAgICAg
ICAgICAgZ2NSZWMobi0xKTsKICAgICAgICB9OwogICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwg
MTAwMDsgaSsrKQogICAgICAgICAgICBnY1JlYygxMCkKICAgIH0KfQoKZnVuY3Rpb24gbG9nKG1z
ZykKewogICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImxvZ2dlciIpLmlubmVySFRNTCArPSBt
c2cgKyAiPGJyPiI7Cn0KCnZhciBvcGVuUmVxdWVzdCA9IHdpbmRvdy5pbmRleGVkREIub3Blbigi
VGVzdERhdGFiYXNlIik7Cm9wZW5SZXF1ZXN0Lm9udXBncmFkZW5lZWRlZCA9IHByZXBhcmVEYXRh
YmFzZTsKCnZhciBvYmplY3RTdG9yZTsKCmZ1bmN0aW9uIHByZXBhcmVEYXRhYmFzZShldmVudCkK
ewogICAgb2JqZWN0U3RvcmUgPSBldmVudC50YXJnZXQudHJhbnNhY3Rpb24uZGIuY3JlYXRlT2Jq
ZWN0U3RvcmUoImhlbGxvIik7CgogICAgZXZlbnQudGFyZ2V0LnRyYW5zYWN0aW9uLm9uYWJvcnQg
PSBmdW5jdGlvbihldmVudCkgewogICAgICAgIGxvZygiSW5pdGlhbCB1cGdyYWRlIHZlcnNpb25j
aGFuZ2UgdHJhbnNhY3Rpb24gdW5leHBlY3RlZCBhYm9ydGVkIik7CiAgICB9CgogICAgZXZlbnQu
dGFyZ2V0LnRyYW5zYWN0aW9uLm9uY29tcGxldGUgPSBmdW5jdGlvbihldmVudCkgewogICAgICAg
IGxvZygiSW5pdGlhbCB1cGdyYWRlIHZlcnNpb25jaGFuZ2UgdHJhbnNhY3Rpb24gY29tcGxldGUi
KTsKICAgIH0KCiAgICBldmVudC50YXJnZXQudHJhbnNhY3Rpb24ub25lcnJvciA9IGZ1bmN0aW9u
KGV2ZW50KSB7CiAgICAgICAgbG9nKCJJbml0aWFsIHVwZ3JhZGUgdmVyc2lvbmNoYW5nZSB0cmFu
c2FjdGlvbiBlcnJvciAiICsgZXZlbnQpOwogICAgfQp9CgpmdW5jdGlvbiBjbGlja2VkKCkKewog
ICAgb3BlblJlcXVlc3QgPSBudWxsOwogICAgb2JqZWN0U3RvcmUgPSBudWxsOwp9Cgo8L3Njcmlw
dD4KPGJvZHk+CjxhIGhyZWY9Imh0dHA6Ly93d3cuYnJhZGVlb2guY29tLyI+Q2xpY2sgZm9yIEI8
L2E+PGJyPjxicj4KPGJ1dHRvbiBvbmNsaWNrPSJjbGlja2VkKCk7Ij5DbGljayB0byBjbGVhciBz
dHVmZjwvYnV0dG9uPjxicj4KPGJ1dHRvbiBvbmNsaWNrPSJnYygpOyI+Q2xpY2sgdG8gR0M8L2J1
dHRvbj4KPGRpdiBpZD0ibG9nZ2VyIj48L2Rpdj4KPC9ib2R5Pgo8L2h0bWw+Cg==
</data>

          </attachment>
      

    </bug>

</bugzilla>