Call AdjustAmountOfExternalAllocatedMemory when V8ArrayBuffer constructed and destructed
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 21 Aug 2012 22:32:32 +0000 (22:32 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 21 Aug 2012 22:32:32 +0000 (22:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=92993

Patch by Ulan Degenbaev <ulan@chromium.org> on 2012-08-21
Reviewed by Kenneth Russell.

Call AdjustAmountOfExternalAllocatedMemory when V8ArrayBuffer
is constructed and destructed so that V8's garbage collection
heuristics can account for the memory held by these objects.

.:

* ManualTests/typed-array-memory.html: Added.

Source/WebCore:

* WebCore.gypi:
* bindings/v8/SerializedScriptValue.cpp:
* bindings/v8/custom/V8ArrayBufferCustom.cpp:
(WebCore::V8ArrayBufferDeallocationObserver::instance):
(WebCore):
(WebCore::V8ArrayBuffer::constructorCallback):
* bindings/v8/custom/V8ArrayBufferCustom.h: Added.
(WebCore):
* bindings/v8/custom/V8ArrayBufferViewCustom.cpp:
* bindings/v8/custom/V8ArrayBufferViewCustom.h:
(WebCore::constructWebGLArray):
* dom/MessageEvent.cpp:
(WebCore::MessageEvent::MessageEvent):
(WebCore::MessageEvent::initMessageEvent):

Source/WTF:

* wtf/ArrayBuffer.h:
(WTF):
(ArrayBufferDeallocationObserver):
(WTF::ArrayBufferContents::ArrayBufferContents):
(WTF::ArrayBufferContents::transfer):
(ArrayBufferContents):
(ArrayBuffer):
(WTF::ArrayBuffer::setDeallocationObserver):
(WTF::ArrayBufferContents::~ArrayBufferContents):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@126196 268f45cc-cd09-0410-ab3c-d52691b4dbfc

ChangeLog
ManualTests/typed-array-memory.html [new file with mode: 0644]
Source/WTF/ChangeLog
Source/WTF/wtf/ArrayBuffer.h
Source/WebCore/ChangeLog
Source/WebCore/WebCore.gypi
Source/WebCore/bindings/v8/custom/V8ArrayBufferCustom.cpp
Source/WebCore/bindings/v8/custom/V8ArrayBufferCustom.h [new file with mode: 0644]
Source/WebCore/bindings/v8/custom/V8ArrayBufferViewCustom.cpp
Source/WebCore/bindings/v8/custom/V8ArrayBufferViewCustom.h

index f3aba42..9e6e689 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2012-08-21  Ulan Degenbaev  <ulan@chromium.org>
+
+        Call AdjustAmountOfExternalAllocatedMemory when V8ArrayBuffer constructed and destructed
+        https://bugs.webkit.org/show_bug.cgi?id=92993
+
+        Reviewed by Kenneth Russell.
+
+        Call AdjustAmountOfExternalAllocatedMemory when V8ArrayBuffer
+        is constructed and destructed so that V8's garbage collection
+        heuristics can account for the memory held by these objects.
+
+        * ManualTests/typed-array-memory.html: Added.
+
 2012-08-21  Martin Robinson  <mrobinson@igalia.com>
 
         [GTK] Using a native window for the WebView breaks GtkOverlay
diff --git a/ManualTests/typed-array-memory.html b/ManualTests/typed-array-memory.html
new file mode 100644 (file)
index 0000000..1d62429
--- /dev/null
@@ -0,0 +1,221 @@
+<html>
+<head>
+<title>ArrayBuffer External Memory test</title>
+<script>
+
+var log;
+function print(message, color)
+{
+    var paragraph = document.createElement("div");
+    paragraph.appendChild(document.createTextNode(message));
+    paragraph.style.fontFamily = "monospace";
+    if (color)
+        paragraph.style.color = color;
+    log.appendChild(paragraph);
+}
+
+function pass(msg)
+{
+    print("PASS: " + msg, "green");
+}
+
+function fail(msg)
+{
+    print("FAIL: " + msg, "red");
+}
+
+var KB = 1024;
+var MB = KB * KB;
+var noise = KB;
+
+function externalMemory() {
+    return getV8Statistics().amount_of_external_allocated_memory;
+}
+
+function collectGarbage() {
+    for (var i = 0; i < 10; i++) gc();
+}
+
+function allocationsThatIncreaseExternalMemory() {
+    function test(expression) {
+        var before = externalMemory();
+        (function () { eval(expression); }) ();
+        var now = externalMemory();
+        if (now < before + MB - noise) {
+            fail(expression + " did not increase the amount of external memory (" +
+                  before + ", " + now + ").");
+        } else {
+            pass(expression + " increased the amount of external memory.");
+        }
+        collectGarbage();
+        var after = externalMemory();
+        if (after > now + noise) {
+            fail("Garbage collection after " + expression +
+                 " did not return the amount of external memory to the initial value (" +
+                 now + ", " + after + ").");
+        } else {
+            pass("Garbage collection after " + expression +
+                 " returned the amount of external memory to the initial value.");
+        }
+    }
+
+    test("(new ArrayBuffer(MB))");
+    test("(new Float32Array(MB))");
+    test("(new Float64Array(MB))");
+    test("(new Int8Array(MB))");
+    test("(new Int16Array(MB))");
+    test("(new Int32Array(MB))");
+    test("(new Uint8Array(MB))");
+    test("(new Uint16Array(MB))");
+    test("(new Uint32Array(MB))");
+    var largeJSArray = [];
+    for (var i = 0; i < MB; i++) largeJSArray.push(i);
+    test("(new Float32Array(largeJSArray))");
+    test("(new Float64Array(largeJSArray))");
+    test("(new Int8Array(largeJSArray))");
+    test("(new Int16Array(largeJSArray))");
+    test("(new Int32Array(largeJSArray))");
+    test("(new Uint8Array(largeJSArray))");
+    test("(new Uint16Array(largeJSArray))");
+    test("(new Uint32Array(largeJSArray))");
+    var int8Array = new Int8Array(MB);
+    test("(new  Float32Array(int8Array))");
+    test("(new  Float64Array(int8Array))");
+    test("(new  Int8Array(int8Array))");
+    test("(new  Int16Array(int8Array))");
+    test("(new  Int32Array(int8Array))");
+    test("(new  Uint8Array(int8Array))");
+    test("(new  Uint16Array(int8Array))");
+    test("(new  Uint32Array(int8Array))");
+}
+
+
+function allocationsThatDoNotChangeExternalMemory() {
+    function test(expression) {
+        var before = externalMemory();
+        (function () { eval(expression); }) ();
+        var now = externalMemory();
+        if (now > before + noise) {
+            fail(expression + " increased the amount of external memory (" + before + ", " + now + ").");
+        } else {
+            pass(expression + " did not increase the amount of external memory.");
+        }
+        collectGarbage();
+        var after = externalMemory();
+        if (after < now - noise) {
+            fail("Garbage collection after " + expression + " decreased the amount of external memory (" +
+                 now + ", " + after + ").");
+        } else {
+            pass("Garbage collection after " + expression +
+                 " did not decrease the amount of external memory.");
+        }
+    }
+    var arrayBuffer = new ArrayBuffer(MB);
+    test("(new  Float32Array(arrayBuffer))");
+    test("(new  Float64Array(arrayBuffer))");
+    test("(new  Int8Array(arrayBuffer))");
+    test("(new  Int16Array(arrayBuffer))");
+    test("(new  Int32Array(arrayBuffer))");
+    test("(new  Uint8Array(arrayBuffer))");
+    test("(new  Uint16Array(arrayBuffer))");
+    test("(new  Uint32Array(arrayBuffer))");
+    var int8Array = new Int8Array(MB);
+    test("(new  Float32Array(int8Array.buffer))");
+    test("(new  Float64Array(int8Array.buffer))");
+    test("(new  Int8Array(int8Array.buffer))");
+    test("(new  Int16Array(int8Array.buffer))");
+    test("(new  Int32Array(int8Array.buffer))");
+    test("(new  Uint8Array(int8Array.buffer))");
+    test("(new  Uint16Array(int8Array.buffer))");
+    test("(new  Uint32Array(int8Array.buffer))");
+}
+
+
+function transfersThatDecreaseExternalMemory() {
+    var workerSource =
+"function externalMemory() {\n" +
+"    return getV8Statistics().amount_of_external_allocated_memory;\n" +
+"}\n" +
+"function collectGarbage() {\n" +
+"    for (var i = 0; i < 10; i++) gc();\n" +
+"}\n" +
+"var before = externalMemory();\n" +
+"self.onmessage = function(e) {\n" +
+"    var now = externalMemory();\n" +
+"    e.data = null;\n" +
+"    collectGarbage();\n" +
+"    var after = externalMemory();\n" +
+"    self.postMessage(before + ' ' + now + ' ' + after);\n" +
+"}\n";
+
+    var blob = new Blob([workerSource]);
+    var worker = new Worker(window.webkitURL.createObjectURL(blob));
+    worker.onmessage = function (e) {
+        print("message from worker: " + e.data, "blue");
+    }
+    function test(expression)
+    {
+        var buffer = eval(expression);
+        try {
+            var before = externalMemory();
+            worker.webkitPostMessage(buffer, [buffer]);
+            var now = externalMemory();
+            if (now > before - MB + noise) {
+                fail("Transfer of " + expression + " did not decrease the amount of external memory (" +
+                     before + ", " + now + ").");
+            } else {
+                pass("Transfer of " + expression + " decreased the amount of external memory.");
+            }
+            collectGarbage();
+            var after = externalMemory();
+            if (after < now - noise) {
+                fail("Garbage collection after transfer of " + expression +
+                     " decreased the amount of external memory (" + now + ", " + after + ").");
+            } else {
+                pass("Garbage collection after transfer of " + expression +
+                     " did not decrease the amount of external memory.");
+            }
+        } catch (e) {
+            fail("Transfer of " + name + ": could not webkitPostMessage: " + e);
+            return false;
+        }
+        return true;
+    }
+    test("(new ArrayBuffer(MB))");
+    test("(new Float32Array(MB)).buffer");
+    test("(new Float64Array(MB)).buffer");
+    test("(new Int8Array(MB)).buffer");
+    test("(new Int16Array(MB)).buffer");
+    test("(new Int32Array(MB)).buffer");
+    test("(new Uint8Array(MB)).buffer");
+    test("(new Uint16Array(MB)).buffer");
+    test("(new Uint32Array(MB)).buffer");
+}
+
+
+function runAll() {
+    log = document.getElementById("log1");
+    if (typeof gc == "undefined" || typeof getV8Statistics == "undefined") {
+        print("Run chrome browser with --js-flags='--expose_gc --track_gc_object_stats'", "red");
+    } else {
+         allocationsThatIncreaseExternalMemory();
+         collectGarbage();
+         allocationsThatDoNotChangeExternalMemory();
+         collectGarbage();
+         log = document.getElementById("log2");
+         transfersThatDecreaseExternalMemory();
+         collectGarbage();
+    }
+}
+
+</script>
+</head>
+<body onload="runAll()">
+<p>This test checks that allocation and deallocation of typed arrays correctly
+adjusts the amount of external memory in V8.</p>
+<div id='log1'></div>
+<p>This test checks that transfer of an array buffer to worker decreases amount of
+external memory in the main V8 isolate.</p>
+<div id='log2'></div>
+</body>
+</html>
index 92d4d8d..48e2d62 100644 (file)
@@ -1,3 +1,24 @@
+2012-08-21  Ulan Degenbaev  <ulan@chromium.org>
+
+        Call AdjustAmountOfExternalAllocatedMemory when V8ArrayBuffer constructed and destructed
+        https://bugs.webkit.org/show_bug.cgi?id=92993
+
+        Reviewed by Kenneth Russell.
+
+        Call AdjustAmountOfExternalAllocatedMemory when V8ArrayBuffer
+        is constructed and destructed so that V8's garbage collection
+        heuristics can account for the memory held by these objects.
+
+        * wtf/ArrayBuffer.h:
+        (WTF):
+        (ArrayBufferDeallocationObserver):
+        (WTF::ArrayBufferContents::ArrayBufferContents):
+        (WTF::ArrayBufferContents::transfer):
+        (ArrayBufferContents):
+        (ArrayBuffer):
+        (WTF::ArrayBuffer::setDeallocationObserver):
+        (WTF::ArrayBufferContents::~ArrayBufferContents):
+
 2012-08-21  Benjamin Poulain  <bpoulain@apple.com>
 
         Store CString data in the CStringBuffer to avoid the double indirection
index d2c7249..aa59cf7 100644 (file)
@@ -36,12 +36,25 @@ namespace WTF {
 class ArrayBuffer;
 class ArrayBufferView;
 
+#if defined(WTF_USE_V8)
+// The current implementation assumes that the instance of this class is a
+// singleton living for the entire process's lifetime.
+class ArrayBufferDeallocationObserver {
+public:
+    virtual void ArrayBufferDeallocated(unsigned sizeInBytes) = 0;
+};
+#endif
+
+
 class ArrayBufferContents {
     WTF_MAKE_NONCOPYABLE(ArrayBufferContents);
 public:
     ArrayBufferContents() 
         : m_data(0)
         , m_sizeInBytes(0)
+#if defined(WTF_USE_V8)
+        , m_deallocationObserver(0)
+#endif
     { }
 
     inline ~ArrayBufferContents();
@@ -53,6 +66,9 @@ private:
     ArrayBufferContents(void* data, unsigned sizeInBytes) 
         : m_data(data)
         , m_sizeInBytes(sizeInBytes)
+#if defined(WTF_USE_V8)
+        , m_deallocationObserver(0)
+#endif
     { }
 
     friend class ArrayBuffer;
@@ -70,10 +86,21 @@ private:
         other.m_sizeInBytes = m_sizeInBytes;
         m_data = 0;
         m_sizeInBytes = 0;
+#if defined(WTF_USE_V8)
+        // Notify the current V8 isolate that the buffer is gone.
+        if (m_deallocationObserver)
+            m_deallocationObserver->ArrayBufferDeallocated(other.m_sizeInBytes);
+        ASSERT(!other.m_deallocationObserver);
+        m_deallocationObserver = 0;
+#endif
     }
 
     void* m_data;
     unsigned m_sizeInBytes;
+
+#if defined(WTF_USE_V8)
+    ArrayBufferDeallocationObserver* m_deallocationObserver;
+#endif
 };
 
 class ArrayBuffer : public RefCounted<ArrayBuffer> {
@@ -99,6 +126,13 @@ public:
     WTF_EXPORT_PRIVATE bool transfer(ArrayBufferContents&, Vector<RefPtr<ArrayBufferView> >& neuteredViews);
     bool isNeutered() { return !m_contents.m_data; }
 
+#if defined(WTF_USE_V8)
+    void setDeallocationObserver(ArrayBufferDeallocationObserver* deallocationObserver)
+    {
+        m_contents.m_deallocationObserver = deallocationObserver;
+    }
+#endif
+
     ~ArrayBuffer() { }
 
 private:
@@ -238,6 +272,10 @@ void ArrayBufferContents::tryAllocate(unsigned numElements, unsigned elementByte
 
 ArrayBufferContents::~ArrayBufferContents()
 {
+#if defined (WTF_USE_V8)
+    if (m_deallocationObserver)
+        m_deallocationObserver->ArrayBufferDeallocated(m_sizeInBytes);
+#endif
     WTF::fastFree(m_data);
 }
 
index 32b019a..7699691 100644 (file)
@@ -1,3 +1,29 @@
+2012-08-21  Ulan Degenbaev  <ulan@chromium.org>
+
+        Call AdjustAmountOfExternalAllocatedMemory when V8ArrayBuffer constructed and destructed
+        https://bugs.webkit.org/show_bug.cgi?id=92993
+
+        Reviewed by Kenneth Russell.
+
+        Call AdjustAmountOfExternalAllocatedMemory when V8ArrayBuffer
+        is constructed and destructed so that V8's garbage collection
+        heuristics can account for the memory held by these objects.
+
+        * WebCore.gypi:
+        * bindings/v8/SerializedScriptValue.cpp:
+        * bindings/v8/custom/V8ArrayBufferCustom.cpp:
+        (WebCore::V8ArrayBufferDeallocationObserver::instance):
+        (WebCore):
+        (WebCore::V8ArrayBuffer::constructorCallback):
+        * bindings/v8/custom/V8ArrayBufferCustom.h: Added.
+        (WebCore):
+        * bindings/v8/custom/V8ArrayBufferViewCustom.cpp:
+        * bindings/v8/custom/V8ArrayBufferViewCustom.h:
+        (WebCore::constructWebGLArray):
+        * dom/MessageEvent.cpp:
+        (WebCore::MessageEvent::MessageEvent):
+        (WebCore::MessageEvent::initMessageEvent):
+
 2012-08-21  Taiju Tsuiki  <tzik@chromium.org>
 
         Web Inspector: Completion events of InspectorFileSystemAgent should be fired asynchronously.
index 37d40bb..0d0e0c6 100644 (file)
             'bindings/v8/WorldContextHandle.h',
             'bindings/v8/WrapperTypeInfo.h',
             'bindings/v8/custom/V8ArrayBufferCustom.cpp',
+            'bindings/v8/custom/V8ArrayBufferCustom.h',
             'bindings/v8/custom/V8ArrayBufferViewCustom.cpp',
             'bindings/v8/custom/V8ArrayBufferViewCustom.h',
             'bindings/v8/custom/V8AudioBufferSourceNodeCustom.cpp',
index adbeacd..672c2ab 100644 (file)
  */
 
 #include "config.h"
+#include "V8ArrayBufferCustom.h"
+
 #include <wtf/ArrayBuffer.h>
+#include <wtf/StdLibExtras.h>
 
 #include "ExceptionCode.h"
-#include "V8Binding.h"
 #include "V8ArrayBuffer.h"
+#include "V8Binding.h"
 #include "V8Proxy.h"
 
 namespace WebCore {
 
+V8ArrayBufferDeallocationObserver* V8ArrayBufferDeallocationObserver::instance()
+{
+    DEFINE_STATIC_LOCAL(V8ArrayBufferDeallocationObserver, deallocationObserver, ());
+    return &deallocationObserver;
+}
+
+
 v8::Handle<v8::Value> V8ArrayBuffer::constructorCallback(const v8::Arguments& args)
 {
     INC_STATS("DOM.ArrayBuffer.Constructor");
@@ -71,6 +81,8 @@ v8::Handle<v8::Value> V8ArrayBuffer::constructorCallback(const v8::Arguments& ar
         buffer = ArrayBuffer::create(static_cast<unsigned>(length), 1);
     if (!buffer.get())
         return throwError(RangeError, "ArrayBuffer size is not a small enough positive integer.", args.GetIsolate());
+    buffer->setDeallocationObserver(V8ArrayBufferDeallocationObserver::instance());
+    v8::V8::AdjustAmountOfExternalAllocatedMemory(buffer->byteLength());
     // Transform the holder into a wrapper object for the array.
     v8::Handle<v8::Object> wrapper = args.Holder();
     V8DOMWrapper::setDOMWrapper(wrapper, &info, buffer.get());
diff --git a/Source/WebCore/bindings/v8/custom/V8ArrayBufferCustom.h b/Source/WebCore/bindings/v8/custom/V8ArrayBufferCustom.h
new file mode 100644 (file)
index 0000000..6f0fe8c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef V8ArrayBufferCustom_h
+#define V8ArrayBufferCustom_h
+
+#include <v8.h>
+#include <wtf/ArrayBuffer.h>
+
+namespace WebCore {
+
+class V8ArrayBufferDeallocationObserver: public WTF::ArrayBufferDeallocationObserver {
+public:
+    virtual void ArrayBufferDeallocated(unsigned sizeInBytes)
+    {
+        v8::V8::AdjustAmountOfExternalAllocatedMemory(-static_cast<int>(sizeInBytes));
+    }
+    static V8ArrayBufferDeallocationObserver* instance();
+};
+
+}
+
+#endif // V8ArrayBufferCustom_h
index 93b242a..12be5b3 100644 (file)
@@ -35,6 +35,7 @@
 #include "ExceptionCode.h"
 
 #include "V8ArrayBuffer.h"
+#include "V8ArrayBufferCustom.h"
 #include "V8Binding.h"
 #include "V8Proxy.h"
 
@@ -147,6 +148,9 @@ v8::Handle<v8::Value> constructWebGLArray(const v8::Arguments& args, WrapperType
         if (!array.get())
             return throwError(RangeError, tooLargeSize, args.GetIsolate());
 
+        array->buffer()->setDeallocationObserver(V8ArrayBufferDeallocationObserver::instance());
+        v8::V8::AdjustAmountOfExternalAllocatedMemory(array->byteLength());
+
         memcpy(array->baseAddress(), source->baseAddress(), length * sizeof(ElementType));
 
         return wrapArrayBufferView(args, type, array, arrayType, true);
@@ -182,6 +186,11 @@ v8::Handle<v8::Value> constructWebGLArray(const v8::Arguments& args, WrapperType
     if (!array.get())
         return throwError(RangeError, tooLargeSize, args.GetIsolate());
 
+    if (doInstantiation) {
+        array->buffer()->setDeallocationObserver(V8ArrayBufferDeallocationObserver::instance());
+        v8::V8::AdjustAmountOfExternalAllocatedMemory(array->byteLength());
+    }
+
 
     // Transform the holder into a wrapper object for the array.
     V8DOMWrapper::setDOMWrapper(args.Holder(), type, array.get());