Call AdjustAmountOfExternalAllocatedMemory when V8ArrayBuffer constructed and destructed
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: http://svn.webkit.org/repository/webkit/trunk@126196 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/ManualTests/typed-array-memory.html b/ManualTests/typed-array-memory.html
new file mode 100644
index 0000000..1d62429
--- /dev/null
+++ b/ManualTests/typed-array-memory.html
@@ -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>