From 7256472b4fcab4f8161a69e96a0fca1dd922830c Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 16:56:02 -0800 Subject: [PATCH 01/22] report time to complete fs tests --- tests/fstests.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/fstests.js b/tests/fstests.js index 22457745..2aa0330e 100644 --- a/tests/fstests.js +++ b/tests/fstests.js @@ -1,6 +1,6 @@ 'use strict'; -var passed = 0, failed = 0; +var passed = 0, failed = 0, then = performance.now(); function is(a, b, msg) { if (a == b) { ++passed; @@ -30,7 +30,8 @@ var fd; function next() { if (tests.length == 0) { ok(true, "TESTS COMPLETED"); - console.log("DONE: " + passed + " PASS, " + failed + " FAIL"); + console.log("DONE: " + passed + " PASS, " + failed + " FAIL, " + + (Math.round(performance.now() - then)) + " TIME"); } else { var test = tests.shift(); test(); From d131a01539e824f808c186463d72b9bbc18a5777 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 16:56:37 -0800 Subject: [PATCH 02/22] add file connection perf test --- .../file/TestFileConnectionPerf.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/com/ibm/oti/connection/file/TestFileConnectionPerf.java diff --git a/tests/com/ibm/oti/connection/file/TestFileConnectionPerf.java b/tests/com/ibm/oti/connection/file/TestFileConnectionPerf.java new file mode 100644 index 00000000..256bf544 --- /dev/null +++ b/tests/com/ibm/oti/connection/file/TestFileConnectionPerf.java @@ -0,0 +1,45 @@ +package com.ibm.oti.connection.file; + +import javax.microedition.io.*; +import javax.microedition.io.file.*; +import java.util.Enumeration; +import java.util.Vector; +import java.util.Hashtable; +import java.io.*; + +import gnu.testlet.TestHarness; +import gnu.testlet.Testlet; + +public class TestFileConnectionPerf implements Testlet { + public void test(TestHarness th) { + try { + String dirPath = System.getProperty("fileconn.dir.private"); + + String str = "I am the very model of a modern major general."; + byte[] bytes = str.getBytes(); + + FileConnection file = (FileConnection)Connector.open(dirPath + "test.txt"); + file.create(); + + long then = System.currentTimeMillis(); + + OutputStream out = file.openOutputStream(); + for (int i = 0; i < 1000; i++) { + out.write(bytes); + out.flush(); + } + + System.out.println("Time to write/flush output: " + (System.currentTimeMillis() - then) + "ms"); + + out.close(); + System.out.println("Time to close output: " + (System.currentTimeMillis() - then) + "ms"); + file.delete(); + System.out.println("Time to delete file: " + (System.currentTimeMillis() - then) + "ms"); + file.close(); + System.out.println("Time to close file: " + (System.currentTimeMillis() - then) + "ms"); + } catch (Exception e) { + th.fail("Unexpected exception: " + e); + e.printStackTrace(); + } + } +} From 3ceeead6456e6d0075a50db2a904f18b9eaa01e9 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 16:59:08 -0800 Subject: [PATCH 03/22] implement fs memory cache; make FCOutputStream.closeImpl/syncImpl synchronous --- libs/fs.js | 63 ++++++++++++++++++++++++++++++++++++++---------------- midp/fs.js | 12 ++++------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index 29c258c9..5504ebf1 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -1,6 +1,38 @@ 'use strict'; var fs = (function() { + var realAsyncStorage = asyncStorage; + var Store = function() { + this.map = new Map(); + }; + Store.prototype.getItem = function(key, cb) { + if (this.map.has(key)) { + var value = this.map.get(key); + window.setZeroTimeout(function() { cb(value) }); + } else { + realAsyncStorage.getItem(key, (function(value) { + this.map.set(key, value); + cb(value); + }).bind(this)); + } + }; + Store.prototype.setItem = function(key, value, cb) { + this.map.set(key, value); + window.setZeroTimeout(function() { (cb || function() {})() }); + realAsyncStorage.setItem(key, value); + }; + Store.prototype.removeItem = function(key, cb) { + this.map.delete(key); + window.setZeroTimeout(function() { cb() }); + realAsyncStorage.removeItem(key); + }; + Store.prototype.clear = function(cb) { + this.map.clear(); + realAsyncStorage.clear(); + window.setZeroTimeout(function() { if (cb) cb() }); + } + asyncStorage = new Store(); + var FileBuffer = function(array) { this.array = array; this.contentSize = array.byteLength; @@ -114,18 +146,11 @@ var fs = (function() { function close(fd, cb) { if (fd >= 0 && openedFiles[fd]) { - flush(fd, function() { - // Replace descriptor object with null value instead of removing it from - // the array so we don't change the indexes of the other objects. - openedFiles.splice(fd, 1, null); - if (cb) { - cb(); - } - }); - } else { - if (cb) { - cb(); - } + flush(fd, function() {}); + openedFiles.splice(fd, 1, null); + } + if (cb) { + setZeroTimeout(cb); } } @@ -193,17 +218,19 @@ var fs = (function() { } function flush(fd, cb) { + var openedFile = openedFiles[fd]; + // Bail early if the file has not been modified. - if (!openedFiles[fd].dirty) { + if (!openedFile.dirty) { cb(); return; } - var blob = new Blob([openedFiles[fd].buffer.getContent()]); - asyncStorage.setItem(openedFiles[fd].path, blob, function() { - openedFiles[fd].dirty = false; - if (openedFiles[fd].stat) { - setStat(openedFiles[fd].path, openedFiles[fd].stat, cb); + var blob = new Blob([openedFile.buffer.getContent()]); + asyncStorage.setItem(openedFile.path, blob, function() { + openedFile.dirty = false; + if (openedFile.stat) { + setStat(openedFile.path, openedFile.stat, cb); } else { cb(); } diff --git a/midp/fs.js b/midp/fs.js index f9a7fa91..757250ef 100644 --- a/midp/fs.js +++ b/midp/fs.js @@ -500,10 +500,8 @@ Native.create("com/ibm/oti/connection/file/FCInputStream.closeImpl.(I)V", functi }); Native.create("com/ibm/oti/connection/file/FCOutputStream.closeImpl.(I)V", function(fd) { - return new Promise(function(resolve, reject) { - fs.close(fd, resolve); - }); -}, true); + fs.close(fd); +}); Native.create("com/ibm/oti/connection/file/FCOutputStream.openImpl.([B)I", function(jPath) { var path = util.decodeUtf8(jPath); @@ -559,10 +557,8 @@ Native.create("com/ibm/oti/connection/file/FCOutputStream.openOffsetImpl.([BJ)I" }, true); Native.create("com/ibm/oti/connection/file/FCOutputStream.syncImpl.(I)V", function(fd) { - return new Promise(function(resolve, reject) { - fs.flush(fd, resolve); - }); -}, true); + fs.flush(fd, function() {}); +}); Native.create("com/ibm/oti/connection/file/FCOutputStream.writeByteImpl.(II)V", function(val, fd) { var buf = new Uint8Array(1); From 99c999daefa2ff473aea4214150bffd2f10be2ea Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 18:09:50 -0800 Subject: [PATCH 04/22] log file fs usage --- libs/fs.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/libs/fs.js b/libs/fs.js index 5504ebf1..b4cbe3df 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -124,6 +124,7 @@ var fs = (function() { function open(path, cb) { path = normalizePath(path); +console.log("fs open " + path); asyncStorage.getItem(path, function(blob) { if (blob == null || !(blob instanceof Blob)) { @@ -146,6 +147,7 @@ var fs = (function() { function close(fd, cb) { if (fd >= 0 && openedFiles[fd]) { +console.log("fs close " + openedFiles[fd].path); flush(fd, function() {}); openedFiles.splice(fd, 1, null); } @@ -158,6 +160,7 @@ var fs = (function() { if (!openedFiles[fd]) { return null; } +console.log("fs read " + openedFiles[fd].path); var buffer = openedFiles[fd].buffer; @@ -178,6 +181,8 @@ var fs = (function() { } function write(fd, data, from) { +console.log("fs write " + openedFiles[fd].path); + if (typeof from == "undefined") { from = openedFiles[fd].position; } @@ -218,6 +223,8 @@ var fs = (function() { } function flush(fd, cb) { +console.log("fs flush " + openedFiles[fd].path); + var openedFile = openedFiles[fd]; // Bail early if the file has not been modified. @@ -239,6 +246,7 @@ var fs = (function() { function list(path, cb) { path = normalizePath(path); +console.log("fs list " + path); asyncStorage.getItem(path, function(files) { if (files == null || files instanceof Blob) { @@ -251,6 +259,7 @@ var fs = (function() { function exists(path, cb) { path = normalizePath(path); +console.log("fs exists " + path); stat(path, function(stat) { cb(stat ? true : false); @@ -259,6 +268,7 @@ var fs = (function() { function truncate(path, cb) { path = normalizePath(path); +console.log("fs truncate " + path); stat(path, function(stat) { if (stat && !stat.isDir) { @@ -273,6 +283,8 @@ var fs = (function() { } function ftruncate(fd, size) { +console.log("fs ftruncate " + openedFiles[fd].path); + var file = openedFiles[fd]; if (size != file.buffer.contentSize) { file.buffer.setSize(size); @@ -283,6 +295,7 @@ var fs = (function() { function remove(path, cb) { path = normalizePath(path); +console.log("fs remove " + path); if (openedFiles.findIndex(function(file) { return file && file.path === path; }) != -1) { setZeroTimeout(function() { cb(false); }); @@ -339,6 +352,7 @@ var fs = (function() { function create(path, blob, cb) { path = normalizePath(path); +console.log("fs create " + path); createInternal(path, blob, function(created) { if (created) { @@ -353,6 +367,7 @@ var fs = (function() { function mkdir(path, cb) { path = normalizePath(path); +console.log("fs mkdir " + path); createInternal(path, [], function(created) { if (created) { @@ -366,6 +381,8 @@ var fs = (function() { } function mkdirp(path, cb) { +console.log("fs mkdirp " + path); + if (path[0] !== "/") { console.error("mkdirp called on relative path: " + path); cb(false); @@ -409,6 +426,7 @@ var fs = (function() { function size(path, cb) { path = normalizePath(path); +console.log("fs size " + path); if (fileStats[path] && typeof fileStats[path].size != "undefined") { cb(fileStats[path].size); @@ -432,6 +450,7 @@ var fs = (function() { function rename(oldPath, newPath, cb) { oldPath = normalizePath(oldPath); newPath = normalizePath(newPath); +console.log("fs rename " + oldPath + " -> " + newPath); if (openedFiles.findIndex(function(file) { return file && file.path === oldPath; }) != -1) { setZeroTimeout(function() { cb(false); }); @@ -467,17 +486,22 @@ var fs = (function() { } function setStat(path, stat, cb) { +console.log("fs setStat " + path); + fileStats[path] = stat; asyncStorage.setItem("!" + path, stat, cb); } function removeStat(path, cb) { +console.log("fs removeStat " + path); + delete fileStats[path]; asyncStorage.removeItem("!" + path, cb); } function stat(path, cb) { path = normalizePath(path); +console.log("fs stat " + path); var stat = fileStats[path]; if (stat) { From d736b9ae2e3e3e5d35f9f8b2265560b4d2a72749 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 18:25:26 -0800 Subject: [PATCH 05/22] rename Store to MemoryStore for clarity --- libs/fs.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index b4cbe3df..0d566f3f 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -2,10 +2,10 @@ var fs = (function() { var realAsyncStorage = asyncStorage; - var Store = function() { + var MemoryStore = function() { this.map = new Map(); }; - Store.prototype.getItem = function(key, cb) { + MemoryStore.prototype.getItem = function(key, cb) { if (this.map.has(key)) { var value = this.map.get(key); window.setZeroTimeout(function() { cb(value) }); @@ -16,22 +16,22 @@ var fs = (function() { }).bind(this)); } }; - Store.prototype.setItem = function(key, value, cb) { + MemoryStore.prototype.setItem = function(key, value, cb) { this.map.set(key, value); window.setZeroTimeout(function() { (cb || function() {})() }); realAsyncStorage.setItem(key, value); }; - Store.prototype.removeItem = function(key, cb) { + MemoryStore.prototype.removeItem = function(key, cb) { this.map.delete(key); window.setZeroTimeout(function() { cb() }); realAsyncStorage.removeItem(key); }; - Store.prototype.clear = function(cb) { + MemoryStore.prototype.clear = function(cb) { this.map.clear(); realAsyncStorage.clear(); window.setZeroTimeout(function() { if (cb) cb() }); } - asyncStorage = new Store(); + asyncStorage = new MemoryStore(); var FileBuffer = function(array) { this.array = array; From c9d9b5d7a6b2079f43ac1af2b4b992541303ff19 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 18:39:45 -0800 Subject: [PATCH 06/22] call asyncStorage indirectly --- libs/fs.js | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index 0d566f3f..b9358cc1 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -1,7 +1,6 @@ 'use strict'; var fs = (function() { - var realAsyncStorage = asyncStorage; var MemoryStore = function() { this.map = new Map(); }; @@ -10,7 +9,7 @@ var fs = (function() { var value = this.map.get(key); window.setZeroTimeout(function() { cb(value) }); } else { - realAsyncStorage.getItem(key, (function(value) { + asyncStorage.getItem(key, (function(value) { this.map.set(key, value); cb(value); }).bind(this)); @@ -19,19 +18,19 @@ var fs = (function() { MemoryStore.prototype.setItem = function(key, value, cb) { this.map.set(key, value); window.setZeroTimeout(function() { (cb || function() {})() }); - realAsyncStorage.setItem(key, value); + asyncStorage.setItem(key, value); }; MemoryStore.prototype.removeItem = function(key, cb) { this.map.delete(key); window.setZeroTimeout(function() { cb() }); - realAsyncStorage.removeItem(key); + asyncStorage.removeItem(key); }; MemoryStore.prototype.clear = function(cb) { this.map.clear(); - realAsyncStorage.clear(); + asyncStorage.clear(); window.setZeroTimeout(function() { if (cb) cb() }); } - asyncStorage = new MemoryStore(); + var store = new MemoryStore(); var FileBuffer = function(array) { this.array = array; @@ -108,11 +107,11 @@ var fs = (function() { } function init(cb) { - asyncStorage.getItem("/", function(data) { + store.getItem("/", function(data) { if (data) { cb(); } else { - asyncStorage.setItem("/", [], function() { + store.setItem("/", [], function() { setStat("/", { mtime: Date.now(), isDir: true }, cb); }); } @@ -126,7 +125,7 @@ var fs = (function() { path = normalizePath(path); console.log("fs open " + path); - asyncStorage.getItem(path, function(blob) { + store.getItem(path, function(blob) { if (blob == null || !(blob instanceof Blob)) { cb(-1); } else { @@ -234,7 +233,7 @@ console.log("fs flush " + openedFiles[fd].path); } var blob = new Blob([openedFile.buffer.getContent()]); - asyncStorage.setItem(openedFile.path, blob, function() { + store.setItem(openedFile.path, blob, function() { openedFile.dirty = false; if (openedFile.stat) { setStat(openedFile.path, openedFile.stat, cb); @@ -248,7 +247,7 @@ console.log("fs flush " + openedFiles[fd].path); path = normalizePath(path); console.log("fs list " + path); - asyncStorage.getItem(path, function(files) { + store.getItem(path, function(files) { if (files == null || files instanceof Blob) { cb(null); } else { @@ -272,7 +271,7 @@ console.log("fs truncate " + path); stat(path, function(stat) { if (stat && !stat.isDir) { - asyncStorage.setItem(path, new Blob(), function() { + store.setItem(path, new Blob(), function() { setStat(path, { mtime: Date.now(), isDir: false, size: 0 }); cb(true); }); @@ -320,8 +319,8 @@ console.log("fs remove " + path); } files.splice(index, 1); - asyncStorage.setItem(dir, files, function() { - asyncStorage.removeItem(path, function() { + store.setItem(dir, files, function() { + store.removeItem(path, function() { removeStat(path, function() { cb(true); }); @@ -342,8 +341,8 @@ console.log("fs remove " + path); } files.push(name); - asyncStorage.setItem(dir, files, function() { - asyncStorage.setItem(path, data, function() { + store.setItem(dir, files, function() { + store.setItem(path, data, function() { cb(true); }); }); @@ -433,7 +432,7 @@ console.log("fs size " + path); return; } - asyncStorage.getItem(path, function(blob) { + store.getItem(path, function(blob) { if (blob == null || !(blob instanceof Blob)) { cb(-1); } else { @@ -463,7 +462,7 @@ console.log("fs rename " + oldPath + " -> " + newPath); return; } - asyncStorage.getItem(oldPath, function(data) { + store.getItem(oldPath, function(data) { if (data == null) { cb(false); return; @@ -489,14 +488,14 @@ console.log("fs rename " + oldPath + " -> " + newPath); console.log("fs setStat " + path); fileStats[path] = stat; - asyncStorage.setItem("!" + path, stat, cb); + store.setItem("!" + path, stat, cb); } function removeStat(path, cb) { console.log("fs removeStat " + path); delete fileStats[path]; - asyncStorage.removeItem("!" + path, cb); + store.removeItem("!" + path, cb); } function stat(path, cb) { @@ -515,7 +514,7 @@ console.log("fs stat " + path); return; } - asyncStorage.getItem("!" + path, function(stat) { + store.getItem("!" + path, function(stat) { if (stat) { fileStats[path] = stat; } From 739e7b3cc769d8426c36daf6f5e3cd425c452505 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 18:43:34 -0800 Subject: [PATCH 07/22] disable fs logging by default --- libs/fs.js | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index b9358cc1..967d4f98 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -1,5 +1,7 @@ 'use strict'; +var DEBUG_FS = false; + var fs = (function() { var MemoryStore = function() { this.map = new Map(); @@ -123,7 +125,7 @@ var fs = (function() { function open(path, cb) { path = normalizePath(path); -console.log("fs open " + path); + if (DEBUG_FS) { console.log("fs open " + path); } store.getItem(path, function(blob) { if (blob == null || !(blob instanceof Blob)) { @@ -146,7 +148,7 @@ console.log("fs open " + path); function close(fd, cb) { if (fd >= 0 && openedFiles[fd]) { -console.log("fs close " + openedFiles[fd].path); + if (DEBUG_FS) { console.log("fs close " + openedFiles[fd].path); } flush(fd, function() {}); openedFiles.splice(fd, 1, null); } @@ -159,7 +161,7 @@ console.log("fs close " + openedFiles[fd].path); if (!openedFiles[fd]) { return null; } -console.log("fs read " + openedFiles[fd].path); + if (DEBUG_FS) { console.log("fs read " + openedFiles[fd].path); } var buffer = openedFiles[fd].buffer; @@ -180,7 +182,7 @@ console.log("fs read " + openedFiles[fd].path); } function write(fd, data, from) { -console.log("fs write " + openedFiles[fd].path); + if (DEBUG_FS) { console.log("fs write " + openedFiles[fd].path); } if (typeof from == "undefined") { from = openedFiles[fd].position; @@ -222,7 +224,7 @@ console.log("fs write " + openedFiles[fd].path); } function flush(fd, cb) { -console.log("fs flush " + openedFiles[fd].path); + if (DEBUG_FS) { console.log("fs flush " + openedFiles[fd].path); } var openedFile = openedFiles[fd]; @@ -245,7 +247,7 @@ console.log("fs flush " + openedFiles[fd].path); function list(path, cb) { path = normalizePath(path); -console.log("fs list " + path); + if (DEBUG_FS) { console.log("fs list " + path); } store.getItem(path, function(files) { if (files == null || files instanceof Blob) { @@ -258,7 +260,7 @@ console.log("fs list " + path); function exists(path, cb) { path = normalizePath(path); -console.log("fs exists " + path); + if (DEBUG_FS) { console.log("fs exists " + path); } stat(path, function(stat) { cb(stat ? true : false); @@ -267,7 +269,7 @@ console.log("fs exists " + path); function truncate(path, cb) { path = normalizePath(path); -console.log("fs truncate " + path); + if (DEBUG_FS) { console.log("fs truncate " + path); } stat(path, function(stat) { if (stat && !stat.isDir) { @@ -282,7 +284,7 @@ console.log("fs truncate " + path); } function ftruncate(fd, size) { -console.log("fs ftruncate " + openedFiles[fd].path); + if (DEBUG_FS) { console.log("fs ftruncate " + openedFiles[fd].path); } var file = openedFiles[fd]; if (size != file.buffer.contentSize) { @@ -294,7 +296,7 @@ console.log("fs ftruncate " + openedFiles[fd].path); function remove(path, cb) { path = normalizePath(path); -console.log("fs remove " + path); + if (DEBUG_FS) { console.log("fs remove " + path); } if (openedFiles.findIndex(function(file) { return file && file.path === path; }) != -1) { setZeroTimeout(function() { cb(false); }); @@ -351,7 +353,7 @@ console.log("fs remove " + path); function create(path, blob, cb) { path = normalizePath(path); -console.log("fs create " + path); + if (DEBUG_FS) { console.log("fs create " + path); } createInternal(path, blob, function(created) { if (created) { @@ -366,7 +368,7 @@ console.log("fs create " + path); function mkdir(path, cb) { path = normalizePath(path); -console.log("fs mkdir " + path); + if (DEBUG_FS) { console.log("fs mkdir " + path); } createInternal(path, [], function(created) { if (created) { @@ -380,7 +382,7 @@ console.log("fs mkdir " + path); } function mkdirp(path, cb) { -console.log("fs mkdirp " + path); + if (DEBUG_FS) { console.log("fs mkdirp " + path); } if (path[0] !== "/") { console.error("mkdirp called on relative path: " + path); @@ -425,7 +427,7 @@ console.log("fs mkdirp " + path); function size(path, cb) { path = normalizePath(path); -console.log("fs size " + path); + if (DEBUG_FS) { console.log("fs size " + path); } if (fileStats[path] && typeof fileStats[path].size != "undefined") { cb(fileStats[path].size); @@ -449,7 +451,7 @@ console.log("fs size " + path); function rename(oldPath, newPath, cb) { oldPath = normalizePath(oldPath); newPath = normalizePath(newPath); -console.log("fs rename " + oldPath + " -> " + newPath); + if (DEBUG_FS) { console.log("fs rename " + oldPath + " -> " + newPath); } if (openedFiles.findIndex(function(file) { return file && file.path === oldPath; }) != -1) { setZeroTimeout(function() { cb(false); }); @@ -485,14 +487,14 @@ console.log("fs rename " + oldPath + " -> " + newPath); } function setStat(path, stat, cb) { -console.log("fs setStat " + path); + if (DEBUG_FS) { console.log("fs setStat " + path); } fileStats[path] = stat; store.setItem("!" + path, stat, cb); } function removeStat(path, cb) { -console.log("fs removeStat " + path); + if (DEBUG_FS) { console.log("fs removeStat " + path); } delete fileStats[path]; store.removeItem("!" + path, cb); @@ -500,7 +502,7 @@ console.log("fs removeStat " + path); function stat(path, cb) { path = normalizePath(path); -console.log("fs stat " + path); + if (DEBUG_FS) { console.log("fs stat " + path); } var stat = fileStats[path]; if (stat) { From e25ac3c22e3f9fde28a4f319d9a4bdd6cca99398 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 18:50:08 -0800 Subject: [PATCH 08/22] consolidate all asyncStorage usage in fs --- libs/fs.js | 5 +++++ main.js | 2 +- tests/fstests.js | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index 967d4f98..e8676837 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -524,6 +524,10 @@ var fs = (function() { }); } + function clear(cb) { + store.clear(cb); + } + return { dirname: dirname, init: init, @@ -546,5 +550,6 @@ var fs = (function() { size: size, rename: rename, stat: stat, + clear: clear, }; })(); diff --git a/main.js b/main.js index a6034208..eb620326 100644 --- a/main.js +++ b/main.js @@ -152,7 +152,7 @@ function toggle(button) { window.onload = function() { document.getElementById("clearstorage").onclick = function() { - asyncStorage.clear(); + fs.clear(); }; document.getElementById("trace").onclick = function() { VM.DEBUG = !VM.DEBUG; diff --git a/tests/fstests.js b/tests/fstests.js index 2aa0330e..0df49bcf 100644 --- a/tests/fstests.js +++ b/tests/fstests.js @@ -809,7 +809,7 @@ tests.push(function() { }); }); -asyncStorage.clear(function() { +fs.clear(function() { fs.init(function() { next(); }); From 49b0b3a304dfdfbd270e24d17c847dee5c3d1841 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 19:52:48 -0800 Subject: [PATCH 09/22] unrefactor IDB access --- libs/async_storage.js | 179 ------------------------------------------ libs/fs.js | 70 ++++++++++++++--- main.js | 39 +++++---- tests/fstests.js | 4 +- 4 files changed, 85 insertions(+), 207 deletions(-) delete mode 100644 libs/async_storage.js diff --git a/libs/async_storage.js b/libs/async_storage.js deleted file mode 100644 index 2282ae9a..00000000 --- a/libs/async_storage.js +++ /dev/null @@ -1,179 +0,0 @@ -'use strict'; - -/** - * This module defines an asynchronous version of the localStorage API, backed by - * an IndexedDB database. It creates a global asyncStorage object that has - * methods like the localStorage object. - * - * To store a value use setItem: - * - * asyncStorage.setItem('key', 'value'); - * - * If you want confirmation that the value has been stored, pass a callback - * function as the third argument: - * - * asyncStorage.setItem('key', 'newvalue', function() { - * console.log('new value stored'); - * }); - * - * To read a value, call getItem(), but note that you must supply a callback - * function that the value will be passed to asynchronously: - * - * asyncStorage.getItem('key', function(value) { - * console.log('The value of key is:', value); - * }); - * - * Note that unlike localStorage, asyncStorage does not allow you to store and - * retrieve values by setting and querying properties directly. You cannot just - * write asyncStorage.key; you have to explicitly call setItem() or getItem(). - * - * removeItem(), clear(), length(), and key() are like the same-named methods of - * localStorage, but, like getItem() and setItem() they take a callback - * argument. - * - * The asynchronous nature of getItem() makes it tricky to retrieve multiple - * values. But unlike localStorage, asyncStorage does not require the values you - * store to be strings. So if you need to save multiple values and want to - * retrieve them together, in a single asynchronous operation, just group the - * values into a single object. The properties of this object may not include - * DOM elements, but they may include things like Blobs and typed arrays. - */ - -var asyncStorage = (function() { - var indexedDB = window.indexedDB || window.webkitIndexedDB || - window.mozIndexedDB || window.msIndexedDB; - - var DBNAME = 'asyncStorage'; - var DBVERSION = 1; - var STORENAME = 'keyvaluepairs'; - var db = null; - - function withDatabase(f) { - if (db) { - f(); - } else { - var openreq = indexedDB.open(DBNAME, DBVERSION); - openreq.onerror = function withStoreOnError() { - console.error('asyncStorage: can\'t open database:', - openreq.error.name); - }; - openreq.onupgradeneeded = function withStoreOnUpgradeNeeded() { - // First time setup: create an empty object store - openreq.result.createObjectStore(STORENAME); - }; - openreq.onsuccess = function withStoreOnSuccess() { - db = openreq.result; - f(); - }; - } - } - - function withStore(type, callback, oncomplete) { - withDatabase(function() { - var transaction = db.transaction(STORENAME, type); - if (oncomplete) { - transaction.oncomplete = oncomplete; - } - callback(transaction.objectStore(STORENAME)); - }); - } - - function getItem(key, callback) { - var req; - withStore('readonly', function getItemBody(store) { - req = store.get(key); - req.onerror = function getItemOnError() { - console.error('Error in asyncStorage.getItem(): ', req.error.name); - }; - }, function onComplete() { - var value = req.result; - if (value === undefined) { - value = null; - } - callback(value); - }); - } - - function setItem(key, value, callback) { - withStore('readwrite', function setItemBody(store) { - var req = store.put(value, key); - req.onerror = function setItemOnError() { - console.error('Error in asyncStorage.setItem(): ', req.error.name); - }; - }, callback); - } - - function removeItem(key, callback) { - withStore('readwrite', function removeItemBody(store) { - var req = store.delete(key); - req.onerror = function removeItemOnError() { - console.error('Error in asyncStorage.removeItem(): ', req.error.name); - }; - }, callback); - } - - function clear(callback) { - withStore('readwrite', function clearBody(store) { - var req = store.clear(); - req.onerror = function clearOnError() { - console.error('Error in asyncStorage.clear(): ', req.error.name); - }; - }, callback); - } - - function length(callback) { - var req; - withStore('readonly', function lengthBody(store) { - req = store.count(); - req.onerror = function lengthOnError() { - console.error('Error in asyncStorage.length(): ', req.error.name); - }; - }, function onComplete() { - callback(req.result); - }); - } - - function key(n, callback) { - if (n < 0) { - callback(null); - return; - } - - var req; - withStore('readonly', function keyBody(store) { - var advanced = false; - req = store.openCursor(); - req.onsuccess = function keyOnSuccess() { - var cursor = req.result; - if (!cursor) { - // this means there weren't enough keys - return; - } - if (n === 0 || advanced) { - // Either 1) we have the first key, return it if that's what they - // wanted, or 2) we've got the nth key. - return; - } - - // Otherwise, ask the cursor to skip ahead n records - advanced = true; - cursor.advance(n); - }; - req.onerror = function keyOnError() { - console.error('Error in asyncStorage.key(): ', req.error.name); - }; - }, function onComplete() { - var cursor = req.result; - callback(cursor ? cursor.key : null); - }); - } - - return { - getItem: getItem, - setItem: setItem, - removeItem: removeItem, - clear: clear, - length: length, - key: key - }; -})(); diff --git a/libs/fs.js b/libs/fs.js index e8676837..33529366 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -3,35 +3,72 @@ var DEBUG_FS = false; var fs = (function() { + var DBNAME = 'asyncStorage'; + var DBVERSION = 1; + var STORENAME = 'keyvaluepairs'; + var db = null; + var MemoryStore = function() { this.map = new Map(); }; + MemoryStore.prototype.getItem = function(key, cb) { if (this.map.has(key)) { var value = this.map.get(key); window.setZeroTimeout(function() { cb(value) }); } else { - asyncStorage.getItem(key, (function(value) { + var transaction = db.transaction(STORENAME, "readonly"); + var objectStore = transaction.objectStore(STORENAME); + var req = objectStore.get(key); + req.onerror = function() { + console.error("Error getting " + key + ": " + req.error.name); + }; + transaction.oncomplete = (function() { + var value = req.result; + if (value === undefined) { + value = null; + } this.map.set(key, value); cb(value); - }).bind(this)); + }).bind(this); } }; + MemoryStore.prototype.setItem = function(key, value, cb) { this.map.set(key, value); window.setZeroTimeout(function() { (cb || function() {})() }); - asyncStorage.setItem(key, value); + + var transaction = db.transaction(STORENAME, "readwrite"); + var objectStore = transaction.objectStore(STORENAME); + var req = objectStore.put(value, key); + req.onerror = function() { + console.error("Error putting " + key + ": " + req.error.name); + }; }; + MemoryStore.prototype.removeItem = function(key, cb) { this.map.delete(key); window.setZeroTimeout(function() { cb() }); - asyncStorage.removeItem(key); + + var transaction = db.transaction(STORENAME, "readwrite"); + var objectStore = transaction.objectStore(STORENAME); + var req = objectStore.delete(key); + req.onerror = function() { + console.error("Error deleting " + key + ": " + req.error.name); + }; }; - MemoryStore.prototype.clear = function(cb) { + + MemoryStore.prototype.clear = function() { this.map.clear(); - asyncStorage.clear(); - window.setZeroTimeout(function() { if (cb) cb() }); + + var transaction = db.transaction(STORENAME, "readwrite"); + var objectStore = transaction.objectStore(STORENAME); + var req = objectStore.clear(); + req.onerror = function() { + console.error("Error clearing store: " + req.error.name); + }; } + var store = new MemoryStore(); var FileBuffer = function(array) { @@ -108,7 +145,7 @@ var fs = (function() { return path.slice(path.lastIndexOf("/") + 1); } - function init(cb) { + function initRootDir(cb) { store.getItem("/", function(data) { if (data) { cb(); @@ -120,6 +157,20 @@ var fs = (function() { }); } + function init(cb) { + var openreq = indexedDB.open(DBNAME, DBVERSION); + openreq.onerror = function() { + console.error("error opening database: " + openreq.error.name); + }; + openreq.onupgradeneeded = function() { + openreq.result.createObjectStore(STORENAME); + }; + openreq.onsuccess = function() { + db = openreq.result; + initRootDir(cb); + }; + } + var openedFiles = [null, null, null]; var fileStats = {}; @@ -525,7 +576,8 @@ var fs = (function() { } function clear(cb) { - store.clear(cb); + store.clear(); + initRootDir(cb); } return { diff --git a/main.js b/main.js index eb620326..40095359 100644 --- a/main.js +++ b/main.js @@ -63,7 +63,7 @@ if (urlParams.pushConn && urlParams.pushMidlet) { var initFS = new Promise(function(resolve, reject) { fs.init(resolve); }).then(function() { - return Promise.all([ + var fsPromises = [ new Promise(function(resolve, reject) { fs.mkdir("/Persistent", resolve); }), @@ -81,7 +81,27 @@ var initFS = new Promise(function(resolve, reject) { } }); }), - ]); + ]; + + if (MIDP.midletClassName == "RunTests") { + fsPromises.push( + new Promise(function(resolve, reject) { + fs.exists("/_test.ks", function(exists) { + if (exists) { + resolve(); + } else { + load("certs/_test.ks", "blob").then(function(data) { + fs.create("/_test.ks", data, function() { + resolve(); + }); + }); + } + }); + }) + ); + } + + return Promise.all(fsPromises); }); // Mobile info gets accessed a lot, so we cache it on startup. @@ -120,21 +140,6 @@ if (MIDP.midletClassName == "RunTests") { loadingPromises.push(loadScript("tests/native.js"), loadScript("tests/override.js"), loadScript("tests/mozactivitymock.js")); - loadingPromises.push( - new Promise(function(resolve, reject) { - fs.exists("/_test.ks", function(exists) { - if (exists) { - resolve(); - } else { - load("certs/_test.ks", "blob").then(function(data) { - fs.create("/_test.ks", data, function() { - resolve(); - }); - }); - } - }); - }) - ); } Promise.all(loadingPromises).then(function() { diff --git a/tests/fstests.js b/tests/fstests.js index 0df49bcf..290ae86c 100644 --- a/tests/fstests.js +++ b/tests/fstests.js @@ -809,8 +809,8 @@ tests.push(function() { }); }); -fs.clear(function() { - fs.init(function() { +fs.init(function() { + fs.clear(function() { next(); }); }); From 70833ddabd39772d3838538bc69d23b60d3834a6 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 19:57:32 -0800 Subject: [PATCH 10/22] make removeItem synchronous --- libs/fs.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index 33529366..72243487 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -46,9 +46,8 @@ var fs = (function() { }; }; - MemoryStore.prototype.removeItem = function(key, cb) { + MemoryStore.prototype.removeItem = function(key) { this.map.delete(key); - window.setZeroTimeout(function() { cb() }); var transaction = db.transaction(STORENAME, "readwrite"); var objectStore = transaction.objectStore(STORENAME); @@ -373,11 +372,9 @@ var fs = (function() { files.splice(index, 1); store.setItem(dir, files, function() { - store.removeItem(path, function() { - removeStat(path, function() { - cb(true); - }); - }); + store.removeItem(path); + removeStat(path); + cb(true); }); }); }); @@ -544,11 +541,11 @@ var fs = (function() { store.setItem("!" + path, stat, cb); } - function removeStat(path, cb) { + function removeStat(path) { if (DEBUG_FS) { console.log("fs removeStat " + path); } delete fileStats[path]; - store.removeItem("!" + path, cb); + store.removeItem("!" + path); } function stat(path, cb) { From 3c5f07aa78ddd6d8c32eb687bee8da66a1f6e6bd Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 20:12:00 -0800 Subject: [PATCH 11/22] remove reference to async_storage.js --- main.html | 1 - 1 file changed, 1 deletion(-) diff --git a/main.html b/main.html index 080d49de..8c1b5bd1 100644 --- a/main.html +++ b/main.html @@ -33,7 +33,6 @@ - From a24c4df236c124763e8dcfee05dbf7cf9b951fc5 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Sun, 23 Nov 2014 20:25:06 -0800 Subject: [PATCH 12/22] make setItem synchronous --- libs/fs.js | 72 +++++++++++++++++++++++------------------------------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index 72243487..eb8aa163 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -34,9 +34,8 @@ var fs = (function() { } }; - MemoryStore.prototype.setItem = function(key, value, cb) { + MemoryStore.prototype.setItem = function(key, value) { this.map.set(key, value); - window.setZeroTimeout(function() { (cb || function() {})() }); var transaction = db.transaction(STORENAME, "readwrite"); var objectStore = transaction.objectStore(STORENAME); @@ -149,9 +148,9 @@ var fs = (function() { if (data) { cb(); } else { - store.setItem("/", [], function() { - setStat("/", { mtime: Date.now(), isDir: true }, cb); - }); + store.setItem("/", []); + setStat("/", { mtime: Date.now(), isDir: true }); + cb(); } }); } @@ -166,7 +165,7 @@ var fs = (function() { }; openreq.onsuccess = function() { db = openreq.result; - initRootDir(cb); + initRootDir(cb || function() {}); }; } @@ -285,14 +284,12 @@ var fs = (function() { } var blob = new Blob([openedFile.buffer.getContent()]); - store.setItem(openedFile.path, blob, function() { - openedFile.dirty = false; - if (openedFile.stat) { - setStat(openedFile.path, openedFile.stat, cb); - } else { - cb(); - } - }); + store.setItem(openedFile.path, blob); + openedFile.dirty = false; + if (openedFile.stat) { + setStat(openedFile.path, openedFile.stat); + } + cb(); } function list(path, cb) { @@ -323,10 +320,9 @@ var fs = (function() { stat(path, function(stat) { if (stat && !stat.isDir) { - store.setItem(path, new Blob(), function() { - setStat(path, { mtime: Date.now(), isDir: false, size: 0 }); - cb(true); - }); + store.setItem(path, new Blob()); + setStat(path, { mtime: Date.now(), isDir: false, size: 0 }); + cb(true); } else { cb(false); } @@ -371,11 +367,13 @@ var fs = (function() { } files.splice(index, 1); - store.setItem(dir, files, function() { - store.removeItem(path); - removeStat(path); - cb(true); - }); + store.setItem(dir, files); + store.removeItem(path); + removeStat(path); + // Calling this synchronously changes the number of passing tests, + // although it doesn't change the number of failures. + // XXX Figure out why. + window.setZeroTimeout(function() { cb(true) }); }); }); } @@ -391,11 +389,9 @@ var fs = (function() { } files.push(name); - store.setItem(dir, files, function() { - store.setItem(path, data, function() { - cb(true); - }); - }); + store.setItem(dir, files); + store.setItem(path, data); + cb(true); }); } @@ -405,12 +401,9 @@ var fs = (function() { createInternal(path, blob, function(created) { if (created) { - setStat(path, { mtime: Date.now(), isDir: false, size: blob.size }, function() { - cb(created); - }); - } else { - cb(created); + setStat(path, { mtime: Date.now(), isDir: false, size: blob.size }); } + cb(created); }); } @@ -420,12 +413,9 @@ var fs = (function() { createInternal(path, [], function(created) { if (created) { - setStat(path, { mtime: Date.now(), isDir: true }, function() { - cb(created); - }); - } else { - cb(created); + setStat(path, { mtime: Date.now(), isDir: true }); } + cb(created); }); } @@ -534,11 +524,11 @@ var fs = (function() { }); } - function setStat(path, stat, cb) { + function setStat(path, stat) { if (DEBUG_FS) { console.log("fs setStat " + path); } fileStats[path] = stat; - store.setItem("!" + path, stat, cb); + store.setItem("!" + path, stat); } function removeStat(path) { @@ -574,7 +564,7 @@ var fs = (function() { function clear(cb) { store.clear(); - initRootDir(cb); + initRootDir(cb || function() {}); } return { From 01399432a6ae37584a95ad28ffc6fb93e40dbf54 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Mon, 24 Nov 2014 01:49:56 -0800 Subject: [PATCH 13/22] make close and flush synchronous --- libs/fs.js | 11 +++----- midp/fs.js | 39 ++++++++++------------------ tests/fstests.js | 67 ++++++++++++++++++++++++------------------------ 3 files changed, 50 insertions(+), 67 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index eb8aa163..40275cd7 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -195,15 +195,12 @@ var fs = (function() { }); } - function close(fd, cb) { + function close(fd) { if (fd >= 0 && openedFiles[fd]) { if (DEBUG_FS) { console.log("fs close " + openedFiles[fd].path); } - flush(fd, function() {}); + flush(fd); openedFiles.splice(fd, 1, null); } - if (cb) { - setZeroTimeout(cb); - } } function read(fd, from, to) { @@ -272,14 +269,13 @@ var fs = (function() { return openedFiles[fd].buffer.contentSize; } - function flush(fd, cb) { + function flush(fd) { if (DEBUG_FS) { console.log("fs flush " + openedFiles[fd].path); } var openedFile = openedFiles[fd]; // Bail early if the file has not been modified. if (!openedFile.dirty) { - cb(); return; } @@ -289,7 +285,6 @@ var fs = (function() { if (openedFile.stat) { setStat(openedFile.path, openedFile.stat); } - cb(); } function list(path, cb) { diff --git a/midp/fs.js b/midp/fs.js index 757250ef..c47b663a 100644 --- a/midp/fs.js +++ b/midp/fs.js @@ -109,25 +109,17 @@ Native.create("com/sun/midp/rms/RecordStoreFile.writeBytes.(I[BII)V", function(h }); Native.create("com/sun/midp/rms/RecordStoreFile.commitWrite.(I)V", function(handle) { - return new Promise(function(resolve, reject) { - fs.flush(handle, resolve); - }); -}, true); + fs.flush(handle); +}); Native.create("com/sun/midp/rms/RecordStoreFile.closeFile.(I)V", function(handle) { - return new Promise(function(resolve, reject) { - fs.close(handle, resolve); - }); -}, true); + fs.close(handle); +}); Native.create("com/sun/midp/rms/RecordStoreFile.truncateFile.(II)V", function(handle, size) { - return new Promise(function(resolve, reject) { - fs.flush(handle, function() { - fs.ftruncate(handle, size); - resolve(); - }); - }); -}, true); + fs.flush(handle); + fs.ftruncate(handle, size); +}); MIDP.RecordStoreCache = []; @@ -442,7 +434,8 @@ Native.create("com/ibm/oti/connection/file/Connection.truncateImpl.([BJ)V", func } fs.ftruncate(fd, newLength.toNumber()); - fs.close(fd, resolve); + fs.close(fd); + resolve(); }); }); }, true); @@ -557,7 +550,7 @@ Native.create("com/ibm/oti/connection/file/FCOutputStream.openOffsetImpl.([BJ)I" }, true); Native.create("com/ibm/oti/connection/file/FCOutputStream.syncImpl.(I)V", function(fd) { - fs.flush(fd, function() {}); + fs.flush(fd); }); Native.create("com/ibm/oti/connection/file/FCOutputStream.writeByteImpl.(II)V", function(val, fd) { @@ -626,10 +619,8 @@ function(handle, buffer, offset, length) { }); Native.create("com/sun/midp/io/j2me/storage/RandomAccessStream.commitWrite.(I)V", function(handle) { - return new Promise(function(resolve, reject) { - fs.flush(handle, resolve); - }); -}, true); + fs.flush(handle); +}); Native.create("com/sun/midp/io/j2me/storage/RandomAccessStream.position.(II)V", function(handle, position) { fs.setpos(handle, position); @@ -646,10 +637,8 @@ Native.create("com/sun/midp/io/j2me/storage/RandomAccessStream.sizeOf.(I)I", fun }); Native.create("com/sun/midp/io/j2me/storage/RandomAccessStream.close.(I)V", function(handle) { - return new Promise(function(resolve, reject) { - fs.close(handle, resolve); - }); -}, true); + fs.close(handle); +}); Native.create("javax/microedition/io/file/FileSystemRegistry.getRootsImpl.()[Ljava/lang/String;", function() { var array = util.newArray("[Ljava/lang/String;", 1); diff --git a/tests/fstests.js b/tests/fstests.js index 290ae86c..b2dc5af1 100644 --- a/tests/fstests.js +++ b/tests/fstests.js @@ -68,15 +68,18 @@ tests.push(function() { }); tests.push(function() { - fs.close(-1, next); + fs.close(-1); + next(); }); tests.push(function() { - fs.close(0, next); + fs.close(0); + next(); }); tests.push(function() { - fs.close(1, next); + fs.close(1); + next(); }); tests.push(function() { @@ -332,7 +335,8 @@ tests.push(function() { }); tests.push(function() { - fs.close(3, next); + fs.close(3); + next(); }); tests.push(function() { @@ -501,10 +505,9 @@ tests.push(function() { }); tests.push(function() { - fs.flush(fd, function() { - ok(true, "file data flushed"); - next(); - }); + fs.flush(fd); + ok(true, "file data flushed"); + next(); }); tests.push(function() { @@ -550,7 +553,8 @@ tests.push(function() { }); tests.push(function() { - fs.close(fd, next); + fs.close(fd); + next(); }); tests.push(function() { @@ -677,12 +681,11 @@ tests.push(function() { tests.push(function() { window.setTimeout(function() { - fs.flush(fd, function() { - fs.stat("/tmp/stat.txt", function(stat) { - is(stat.mtime, lastTime, "flush on just opened file doesn't update mtime"); - lastTime = stat.mtime; - next(); - }); + fs.flush(fd); + fs.stat("/tmp/stat.txt", function(stat) { + is(stat.mtime, lastTime, "flush on just opened file doesn't update mtime"); + lastTime = stat.mtime; + next(); }); }, 1); }); @@ -701,24 +704,22 @@ tests.push(function() { tests.push(function() { window.setTimeout(function() { fs.write(fd, new TextEncoder().encode("sc")); - fs.flush(fd, function() { - fs.stat("/tmp/stat.txt", function(stat) { - ok(stat.mtime > lastTime, "write and then flush updates mtime"); - lastTime = stat.mtime; - next(); - }); + fs.flush(fd); + fs.stat("/tmp/stat.txt", function(stat) { + ok(stat.mtime > lastTime, "write and then flush updates mtime"); + lastTime = stat.mtime; + next(); }); }, 1); }); tests.push(function() { window.setTimeout(function() { - fs.flush(fd, function() { - fs.stat("/tmp/stat.txt", function(stat) { - is(stat.mtime, lastTime, "flush on non-dirty file doesn't change mtime"); - lastTime = stat.mtime; - next(); - }); + fs.flush(fd); + fs.stat("/tmp/stat.txt", function(stat) { + is(stat.mtime, lastTime, "flush on non-dirty file doesn't change mtime"); + lastTime = stat.mtime; + next(); }); }, 1); }); @@ -758,13 +759,11 @@ tests.push(function() { tests.push(function() { window.setTimeout(function() { - fs.flush(fd, function() { - fs.close(fd, function() { - fs.stat("/tmp/stat.txt", function(stat) { - is(stat.mtime, lastTime, "close doesn't update mtime"); - next(); - }); - }); + fs.flush(fd); + fs.close(fd); + fs.stat("/tmp/stat.txt", function(stat) { + is(stat.mtime, lastTime, "close doesn't update mtime"); + next(); }); }, 1); }); From 2fb98157b5b2b9e4517709d485127c98725ee77e Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Mon, 24 Nov 2014 03:09:48 -0800 Subject: [PATCH 14/22] remove player listener so it doesn't receive indeterminate number of updates --- libs/fs.js | 5 +---- .../TestFileConnectionPerf.java => TestPerf.java} | 14 +++----------- tests/automation.js | 2 +- tests/javax/microedition/media/TestMediaImage.java | 2 ++ 4 files changed, 7 insertions(+), 16 deletions(-) rename tests/{com/ibm/oti/connection/file/TestFileConnectionPerf.java => TestPerf.java} (78%) diff --git a/libs/fs.js b/libs/fs.js index 40275cd7..1b9194ed 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -365,10 +365,7 @@ var fs = (function() { store.setItem(dir, files); store.removeItem(path); removeStat(path); - // Calling this synchronously changes the number of passing tests, - // although it doesn't change the number of failures. - // XXX Figure out why. - window.setZeroTimeout(function() { cb(true) }); + cb(true); }); }); } diff --git a/tests/com/ibm/oti/connection/file/TestFileConnectionPerf.java b/tests/TestPerf.java similarity index 78% rename from tests/com/ibm/oti/connection/file/TestFileConnectionPerf.java rename to tests/TestPerf.java index 256bf544..300fabe0 100644 --- a/tests/com/ibm/oti/connection/file/TestFileConnectionPerf.java +++ b/tests/TestPerf.java @@ -1,17 +1,9 @@ -package com.ibm.oti.connection.file; - import javax.microedition.io.*; import javax.microedition.io.file.*; -import java.util.Enumeration; -import java.util.Vector; -import java.util.Hashtable; import java.io.*; -import gnu.testlet.TestHarness; -import gnu.testlet.Testlet; - -public class TestFileConnectionPerf implements Testlet { - public void test(TestHarness th) { +public class TestPerf { + public static void main(String args[]) { try { String dirPath = System.getProperty("fileconn.dir.private"); @@ -38,7 +30,7 @@ public class TestFileConnectionPerf implements Testlet { file.close(); System.out.println("Time to close file: " + (System.currentTimeMillis() - then) + "ms"); } catch (Exception e) { - th.fail("Unexpected exception: " + e); + System.out.println("Unexpected exception: " + e); e.printStackTrace(); } } diff --git a/tests/automation.js b/tests/automation.js index 3614602d..c9467914 100644 --- a/tests/automation.js +++ b/tests/automation.js @@ -41,7 +41,7 @@ var gfxTests = [ ]; var expectedUnitTestResults = [ - { name: "pass", number: 71106 }, + { name: "pass", number: 71104 }, { name: "fail", number: 0 }, { name: "known fail", number: 179 }, { name: "unknown pass", number: 0 } diff --git a/tests/javax/microedition/media/TestMediaImage.java b/tests/javax/microedition/media/TestMediaImage.java index b8b3919f..a618fd8a 100644 --- a/tests/javax/microedition/media/TestMediaImage.java +++ b/tests/javax/microedition/media/TestMediaImage.java @@ -61,6 +61,8 @@ public class TestMediaImage implements Testlet, PlayerListener { player.start(); + player.removePlayerListener(this); + file.delete(); file.close(); } catch (Exception e) { From f3790feb9117928ee6e4d9f09c819337fd956850 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Mon, 24 Nov 2014 03:16:05 -0800 Subject: [PATCH 15/22] rename MemoryStore to Store now that it also stores to IDB --- libs/fs.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index 1b9194ed..947109eb 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -8,11 +8,11 @@ var fs = (function() { var STORENAME = 'keyvaluepairs'; var db = null; - var MemoryStore = function() { + var Store = function() { this.map = new Map(); }; - MemoryStore.prototype.getItem = function(key, cb) { + Store.prototype.getItem = function(key, cb) { if (this.map.has(key)) { var value = this.map.get(key); window.setZeroTimeout(function() { cb(value) }); @@ -34,7 +34,7 @@ var fs = (function() { } }; - MemoryStore.prototype.setItem = function(key, value) { + Store.prototype.setItem = function(key, value) { this.map.set(key, value); var transaction = db.transaction(STORENAME, "readwrite"); @@ -45,7 +45,7 @@ var fs = (function() { }; }; - MemoryStore.prototype.removeItem = function(key) { + Store.prototype.removeItem = function(key) { this.map.delete(key); var transaction = db.transaction(STORENAME, "readwrite"); @@ -56,7 +56,7 @@ var fs = (function() { }; }; - MemoryStore.prototype.clear = function() { + Store.prototype.clear = function() { this.map.clear(); var transaction = db.transaction(STORENAME, "readwrite"); @@ -67,7 +67,7 @@ var fs = (function() { }; } - var store = new MemoryStore(); + var store = new Store(); var FileBuffer = function(array) { this.array = array; From 73fb522e6750b11aa98c5a85ac0aa6606e21144a Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Mon, 24 Nov 2014 03:16:14 -0800 Subject: [PATCH 16/22] explain TestMediaImage change --- tests/javax/microedition/media/TestMediaImage.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/javax/microedition/media/TestMediaImage.java b/tests/javax/microedition/media/TestMediaImage.java index a618fd8a..f51432df 100644 --- a/tests/javax/microedition/media/TestMediaImage.java +++ b/tests/javax/microedition/media/TestMediaImage.java @@ -61,6 +61,10 @@ public class TestMediaImage implements Testlet, PlayerListener { player.start(); + // We have to remove the listener now that we're done testing. + // Otherwise, it could receive additional calls to playerUpdate, + // including after we've returned, and the harness has tallied + // our total number of passed tests! player.removePlayerListener(this); file.delete(); From 6b53ae406691937be7a4c5e0f44ad30ac576301c Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Mon, 24 Nov 2014 03:39:00 -0800 Subject: [PATCH 17/22] make TestPerf output clearer; show effect of many IDB writes --- tests/TestPerf.java | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/tests/TestPerf.java b/tests/TestPerf.java index 300fabe0..ae79ac2a 100644 --- a/tests/TestPerf.java +++ b/tests/TestPerf.java @@ -6,29 +6,54 @@ public class TestPerf { public static void main(String args[]) { try { String dirPath = System.getProperty("fileconn.dir.private"); + long then; String str = "I am the very model of a modern major general."; byte[] bytes = str.getBytes(); + then = System.currentTimeMillis(); FileConnection file = (FileConnection)Connector.open(dirPath + "test.txt"); + System.out.println("Time to open file: " + (System.currentTimeMillis() - then) + "ms"); + + then = System.currentTimeMillis(); file.create(); + System.out.println("Time to create file: " + (System.currentTimeMillis() - then) + "ms"); - long then = System.currentTimeMillis(); - + then = System.currentTimeMillis(); OutputStream out = file.openOutputStream(); for (int i = 0; i < 1000; i++) { out.write(bytes); out.flush(); } + System.out.println("Time to write/flush to output stream: " + (System.currentTimeMillis() - then) + "ms"); - System.out.println("Time to write/flush output: " + (System.currentTimeMillis() - then) + "ms"); - + then = System.currentTimeMillis(); out.close(); - System.out.println("Time to close output: " + (System.currentTimeMillis() - then) + "ms"); + System.out.println("Time to close output stream: " + (System.currentTimeMillis() - then) + "ms"); + + then = System.currentTimeMillis(); file.delete(); System.out.println("Time to delete file: " + (System.currentTimeMillis() - then) + "ms"); + + then = System.currentTimeMillis(); file.close(); System.out.println("Time to close file: " + (System.currentTimeMillis() - then) + "ms"); + + then = System.currentTimeMillis(); + file = (FileConnection)Connector.open(dirPath + "test.txt"); + System.out.println("Time to reopen file: " + (System.currentTimeMillis() - then) + "ms"); + + then = System.currentTimeMillis(); + file = (FileConnection)Connector.open(dirPath + "test2.txt"); + file.create(); + out = file.openOutputStream(); + out.write(bytes); + out.flush(); + out.close(); + file.delete(); + file.close(); + System.out.println("Time to access another file: " + (System.currentTimeMillis() - then) + "ms"); + } catch (Exception e) { System.out.println("Unexpected exception: " + e); e.printStackTrace(); From a2691f121d5b3df89b827d9a53f9f3c2b88041a4 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Mon, 24 Nov 2014 16:34:30 -0800 Subject: [PATCH 18/22] encapsulate Store constants/initialization --- libs/fs.js | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index 947109eb..1a6b1954 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -3,13 +3,27 @@ var DEBUG_FS = false; var fs = (function() { - var DBNAME = 'asyncStorage'; - var DBVERSION = 1; - var STORENAME = 'keyvaluepairs'; - var db = null; - var Store = function() { this.map = new Map(); + this.db = null; + }; + + Store.DBNAME = "asyncStorage"; + Store.DBVERSION = 1; + Store.DBSTORENAME = "keyvaluepairs"; + + Store.prototype.init = function(cb) { + var openreq = indexedDB.open(Store.DBNAME, Store.DBVERSION); + openreq.onerror = function() { + console.error("error opening database: " + openreq.error.name); + }; + openreq.onupgradeneeded = function() { + openreq.result.createObjectStore(Store.DBSTORENAME); + }; + openreq.onsuccess = (function() { + this.db = openreq.result; + cb(); + }).bind(this); }; Store.prototype.getItem = function(key, cb) { @@ -17,8 +31,8 @@ var fs = (function() { var value = this.map.get(key); window.setZeroTimeout(function() { cb(value) }); } else { - var transaction = db.transaction(STORENAME, "readonly"); - var objectStore = transaction.objectStore(STORENAME); + var transaction = this.db.transaction(Store.DBSTORENAME, "readonly"); + var objectStore = transaction.objectStore(Store.DBSTORENAME); var req = objectStore.get(key); req.onerror = function() { console.error("Error getting " + key + ": " + req.error.name); @@ -37,8 +51,8 @@ var fs = (function() { Store.prototype.setItem = function(key, value) { this.map.set(key, value); - var transaction = db.transaction(STORENAME, "readwrite"); - var objectStore = transaction.objectStore(STORENAME); + var transaction = this.db.transaction(Store.DBSTORENAME, "readwrite"); + var objectStore = transaction.objectStore(Store.DBSTORENAME); var req = objectStore.put(value, key); req.onerror = function() { console.error("Error putting " + key + ": " + req.error.name); @@ -48,8 +62,8 @@ var fs = (function() { Store.prototype.removeItem = function(key) { this.map.delete(key); - var transaction = db.transaction(STORENAME, "readwrite"); - var objectStore = transaction.objectStore(STORENAME); + var transaction = this.db.transaction(Store.DBSTORENAME, "readwrite"); + var objectStore = transaction.objectStore(Store.DBSTORENAME); var req = objectStore.delete(key); req.onerror = function() { console.error("Error deleting " + key + ": " + req.error.name); @@ -59,8 +73,8 @@ var fs = (function() { Store.prototype.clear = function() { this.map.clear(); - var transaction = db.transaction(STORENAME, "readwrite"); - var objectStore = transaction.objectStore(STORENAME); + var transaction = this.db.transaction(Store.DBSTORENAME, "readwrite"); + var objectStore = transaction.objectStore(Store.DBSTORENAME); var req = objectStore.clear(); req.onerror = function() { console.error("Error clearing store: " + req.error.name); @@ -156,17 +170,9 @@ var fs = (function() { } function init(cb) { - var openreq = indexedDB.open(DBNAME, DBVERSION); - openreq.onerror = function() { - console.error("error opening database: " + openreq.error.name); - }; - openreq.onupgradeneeded = function() { - openreq.result.createObjectStore(STORENAME); - }; - openreq.onsuccess = function() { - db = openreq.result; + store.init(function() { initRootDir(cb || function() {}); - }; + }); } var openedFiles = [null, null, null]; From 54fcadd39c4db349934aaa4f1ed6ed1b5a2078a1 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Tue, 25 Nov 2014 11:28:29 -0800 Subject: [PATCH 19/22] remove redundant fileStats cache --- libs/fs.js | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index 1a6b1954..283eea5d 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -176,7 +176,6 @@ var fs = (function() { } var openedFiles = [null, null, null]; - var fileStats = {}; function open(path, cb) { path = normalizePath(path); @@ -256,7 +255,6 @@ var fs = (function() { file.position = from + data.byteLength; file.stat = { mtime: Date.now(), isDir: false, size: buffer.contentSize }; file.dirty = true; - fileStats[file.path] = file.stat; } function getpos(fd) { @@ -337,7 +335,7 @@ var fs = (function() { if (size != file.buffer.contentSize) { file.buffer.setSize(size); file.dirty = true; - fileStats[file.path] = file.stat = { mtime: Date.now(), isDir: false, size: size }; + file.stat = { mtime: Date.now(), isDir: false, size: size }; } } @@ -465,18 +463,10 @@ var fs = (function() { path = normalizePath(path); if (DEBUG_FS) { console.log("fs size " + path); } - if (fileStats[path] && typeof fileStats[path].size != "undefined") { - cb(fileStats[path].size); - return; - } - store.getItem(path, function(blob) { if (blob == null || !(blob instanceof Blob)) { cb(-1); } else { - if (fileStats[path]) { - fileStats[path].size = blob.size; - } cb(blob.size); } }); @@ -525,14 +515,12 @@ var fs = (function() { function setStat(path, stat) { if (DEBUG_FS) { console.log("fs setStat " + path); } - fileStats[path] = stat; store.setItem("!" + path, stat); } function removeStat(path) { if (DEBUG_FS) { console.log("fs removeStat " + path); } - delete fileStats[path]; store.removeItem("!" + path); } @@ -540,24 +528,13 @@ var fs = (function() { path = normalizePath(path); if (DEBUG_FS) { console.log("fs stat " + path); } - var stat = fileStats[path]; - if (stat) { - setZeroTimeout(function() { cb(stat); }); - return; - } - var file = openedFiles.find(function (file) { return file && file.stat && file.path === path }); if (file) { setZeroTimeout(function() { cb(file.stat); }); return; } - store.getItem("!" + path, function(stat) { - if (stat) { - fileStats[path] = stat; - } - cb(stat); - }); + store.getItem("!" + path, cb); } function clear(cb) { From afbe0f106624390662450381661d8feda1975c0c Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Tue, 25 Nov 2014 11:49:42 -0800 Subject: [PATCH 20/22] expect correct size of unflushed file --- tests/fstests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fstests.js b/tests/fstests.js index b2dc5af1..8a2aacf9 100644 --- a/tests/fstests.js +++ b/tests/fstests.js @@ -499,7 +499,7 @@ tests.push(function() { tests.push(function() { fs.size("/tmp/tmp.txt", function(size) { - is(size, 12, "unflushed file's size is 12"); + is(size, 0, "unflushed file's size is 0"); next(); }); }); From a926538dcda5444d77e82f5de1546a16b5c8d156 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Tue, 25 Nov 2014 17:07:33 -0800 Subject: [PATCH 21/22] remove last reference to removed async_storage.js --- tests/fstests.html | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/fstests.html b/tests/fstests.html index 2e252a40..32fbb66e 100644 --- a/tests/fstests.html +++ b/tests/fstests.html @@ -4,7 +4,6 @@ -