From c1fa5478d14b8eca6d65f651ad92d59c024d40c7 Mon Sep 17 00:00:00 2001 From: Divam Date: Tue, 14 Jan 2020 12:35:27 +0900 Subject: [PATCH 01/12] Fix Atomics.wait usage, do it in worker thread + use Int32Array + use timeout 10ms --- jsaddleJS/jsaddle.js | 32 ++++++++++-------------------- jsaddleJS/jsaddle_sendMsgWorker.js | 27 +++++++++++++++++++++++++ src/JSaddleDevice.ts | 13 +++++++----- src/worker_runner.ts | 3 ++- www/jsaddle_sendMsgWorker.js | 1 + 5 files changed, 49 insertions(+), 27 deletions(-) create mode 100644 jsaddleJS/jsaddle_sendMsgWorker.js create mode 120000 www/jsaddle_sendMsgWorker.js diff --git a/jsaddleJS/jsaddle.js b/jsaddleJS/jsaddle.js index b8bdf2f..3a3a45b 100644 --- a/jsaddleJS/jsaddle.js +++ b/jsaddleJS/jsaddle.js @@ -328,6 +328,9 @@ function jsaddleHandlerMsgs (msgs) { var jsaddleMsgSharedBuf = new SharedArrayBuffer(10*1024*1024); var jsaddleMsgBufArray = new Uint8Array(jsaddleMsgSharedBuf); var jsaddleMsgBufArray32 = new Uint32Array(jsaddleMsgSharedBuf); +// Atomics.wait need Int32 +var jsaddleMsgBufArrayInt32 = new Int32Array(jsaddleMsgSharedBuf); +var jsaddle_sendMsgWorker = new Worker('jsaddle_sendMsgWorker.js'); function sendAPI (msg) { var str = JSON.stringify(msg); @@ -338,32 +341,19 @@ function sendAPI (msg) { dataview.setUint32(0, size); const uint8 = new Uint8Array(b); uint8.set(a, 4); - // non-blocking - appendMsgToSharedBuf(uint8); -} - -async function appendMsgToSharedBuf(buf) { - var isAlreadyLocked = Atomics.compareExchange(jsaddleMsgBufArray32, 0, 0, 1); - if (isAlreadyLocked === 1) { - Atomics.wait(jsaddleMsgBufArray32, 0, 0); - appendMsgToSharedBuf(buf); - } else { - var len = buf.byteLength; - var prevLen = jsaddleMsgBufArray32[1]; - var totalLen = len + prevLen; - var startOffset = prevLen + 8; // Two 32 bit uint - var i = len; - while (i--) jsaddleMsgBufArray[startOffset + i] = buf[i]; - jsaddleMsgBufArray32[1] = totalLen; - // Release the lock - jsaddleMsgBufArray32[0] = 0; - } + jsaddle_sendMsgWorker.postMessage({ + buf: b, + jsaddleMsgBufArrayInt32: jsaddleMsgBufArrayInt32, + jsaddleMsgBufArray32: jsaddleMsgBufArray32, + jsaddleMsgBufArray: jsaddleMsgBufArray + }, [b]); } function jsaddleJsInit() { return { jsaddleListener: channel.port2, jsaddleMsgBufArray: jsaddleMsgBufArray, - jsaddleMsgBufArray32: jsaddleMsgBufArray32 + jsaddleMsgBufArray32: jsaddleMsgBufArray32, + jsaddleMsgBufArrayInt32: jsaddleMsgBufArrayInt32 }; } diff --git a/jsaddleJS/jsaddle_sendMsgWorker.js b/jsaddleJS/jsaddle_sendMsgWorker.js new file mode 100644 index 0000000..6b9dd20 --- /dev/null +++ b/jsaddleJS/jsaddle_sendMsgWorker.js @@ -0,0 +1,27 @@ +// Atomics.wait is not possible on main thread +// so this worker takes the messages from jsaddle.js and appends to the SharedArrayBuffer + +onmessage = function (msg) { + var jsaddleMsgBufArrayInt32 = msg.data.jsaddleMsgBufArrayInt32; + var jsaddleMsgBufArray32 = msg.data.jsaddleMsgBufArray32; + var jsaddleMsgBufArray = msg.data.jsaddleMsgBufArray; + const uint8 = new Uint8Array(msg.data.buf); + var appendMsgToSharedBuf = function () { + var isAlreadyLocked = Atomics.compareExchange(jsaddleMsgBufArrayInt32, 0, 0, 10); + if (isAlreadyLocked === 1) { + Atomics.wait(jsaddleMsgBufArrayInt32, 0, 0, 10); + appendMsgToSharedBuf(); + } else { + var len = uint8.length; + var prevLen = jsaddleMsgBufArray32[1]; + var totalLen = len + prevLen; + var startOffset = prevLen + 8; // Two 32 bit uint + var i = len; + while (i--) jsaddleMsgBufArray[startOffset + i] = uint8[i]; + jsaddleMsgBufArray32[1] = totalLen; + // Release the lock + jsaddleMsgBufArrayInt32[0] = 0; + } + }; + appendMsgToSharedBuf(); +} diff --git a/src/JSaddleDevice.ts b/src/JSaddleDevice.ts index 77b1ba2..d614402 100644 --- a/src/JSaddleDevice.ts +++ b/src/JSaddleDevice.ts @@ -11,9 +11,11 @@ export class JSaddleDevice implements Device { constructor( jsaddleListener: MessagePort, jsaddleMsgBufArray: Uint8Array, - jsaddleMsgBufArray32: Uint32Array) { + jsaddleMsgBufArray32: Uint32Array, + jsaddleMsgBufArrayInt32: Int32Array + ) { this._file = new JSaddleDeviceFile(this, jsaddleListener - , jsaddleMsgBufArray, jsaddleMsgBufArray32); + , jsaddleMsgBufArray, jsaddleMsgBufArray32, jsaddleMsgBufArrayInt32); } public open(flag: FileFlag): File { @@ -31,7 +33,8 @@ export class JSaddleDeviceFile extends BaseFile implements File { private _Device: JSaddleDevice, private _jsaddleListener: MessagePort, private _jsaddleMsgBufArray: Uint8Array, - private _jsaddleMsgBufArray32: Uint32Array) { + private _jsaddleMsgBufArray32: Uint32Array, + private _jsaddleMsgBufArrayInt32: Int32Array) { super(); } public getPos(): number | undefined { @@ -94,7 +97,7 @@ export class JSaddleDeviceFile extends BaseFile implements File { } public readSync(buffer: Buffer, offset: number, length: number, position: number | null): number { var bytes_read = 0; - var isAlreadyLocked = Atomics.compareExchange(this._jsaddleMsgBufArray32, 0, 0, 1); + var isAlreadyLocked = Atomics.compareExchange(this._jsaddleMsgBufArrayInt32, 0, 0, 1); if (isAlreadyLocked === 0) { var bytes_available = this._jsaddleMsgBufArray32[1]; if (bytes_available > 0) { @@ -118,7 +121,7 @@ export class JSaddleDeviceFile extends BaseFile implements File { } } // Release the lock - this._jsaddleMsgBufArray32[0] = 0; + this._jsaddleMsgBufArrayInt32[0] = 0; } return bytes_read; } diff --git a/src/worker_runner.ts b/src/worker_runner.ts index 746b56a..1a0baf6 100644 --- a/src/worker_runner.ts +++ b/src/worker_runner.ts @@ -9,7 +9,8 @@ connectParent({ onMessage: async msg1 => { const jsaddleDevice = new JSaddleDevice( msg.jsaddleVals.jsaddleListener, msg.jsaddleVals.jsaddleMsgBufArray, - msg.jsaddleVals.jsaddleMsgBufArray32); + msg.jsaddleVals.jsaddleMsgBufArray32, + msg.jsaddleVals.jsaddleMsgBufArrayInt32); const fs = await configureFileSystem({ devices: { ["/jsaddle_inout"]: jsaddleDevice } }); (await Process.instantiateProcess(fs, msg.url)).start([],[]); }}); diff --git a/www/jsaddle_sendMsgWorker.js b/www/jsaddle_sendMsgWorker.js new file mode 120000 index 0000000..36abc25 --- /dev/null +++ b/www/jsaddle_sendMsgWorker.js @@ -0,0 +1 @@ +../jsaddleJS/jsaddle_sendMsgWorker.js \ No newline at end of file From ddf134d627eb499c32a345584192c02761fd7f68 Mon Sep 17 00:00:00 2001 From: Divam Date: Wed, 15 Jan 2020 19:05:23 +0900 Subject: [PATCH 02/12] Add wait in JSaddleDevice readSync --- src/JSaddleDevice.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/JSaddleDevice.ts b/src/JSaddleDevice.ts index d614402..da86cce 100644 --- a/src/JSaddleDevice.ts +++ b/src/JSaddleDevice.ts @@ -98,7 +98,10 @@ export class JSaddleDeviceFile extends BaseFile implements File { public readSync(buffer: Buffer, offset: number, length: number, position: number | null): number { var bytes_read = 0; var isAlreadyLocked = Atomics.compareExchange(this._jsaddleMsgBufArrayInt32, 0, 0, 1); - if (isAlreadyLocked === 0) { + if (isAlreadyLocked === 1) { + Atomics.wait(this._jsaddleMsgBufArrayInt32, 0, 0, 10); + this.readSync(buffer, offset, length, position); + } else { var bytes_available = this._jsaddleMsgBufArray32[1]; if (bytes_available > 0) { if (bytes_available > length) { From 3408d03b5eac246ecfaa7edcbd194efbc24c5eaf Mon Sep 17 00:00:00 2001 From: Divam Date: Wed, 15 Jan 2020 19:06:15 +0900 Subject: [PATCH 03/12] add jsaddle helper functions --- jsaddleJS/jsaddle.js | 124 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/jsaddleJS/jsaddle.js b/jsaddleJS/jsaddle.js index 3a3a45b..22cd636 100644 --- a/jsaddleJS/jsaddle.js +++ b/jsaddleJS/jsaddle.js @@ -279,6 +279,130 @@ function jsaddleHandler(msg) { runBatch(batch); } +// ghcjs helper functions +function h$isNumber(o) { + return typeof(o) === 'number'; +} + +// returns true for null, but not for functions and host objects +function h$isObject(o) { + return typeof(o) === 'object'; +} + +function h$isString(o) { + return typeof(o) === 'string'; +} + +function h$isSymbol(o) { + return typeof(o) === 'symbol'; +} + +function h$isBoolean(o) { + return typeof(o) === 'boolean'; +} + +function h$isFunction(o) { + return typeof(o) === 'function'; +} + +function h$jsTypeOf(o) { + var t = typeof(o); + if(t === 'undefined') return 0; + if(t === 'object') return 1; + if(t === 'boolean') return 2; + if(t === 'number') return 3; + if(t === 'string') return 4; + if(t === 'symbol') return 5; + if(t === 'function') return 6; + return 7; // other, host object etc +} + +function h$jsonTypeOf(o) { + if (!(o instanceof Object)) { + if (o == null) { + return 0; + } else if (typeof o == 'number') { + if (h$isInteger(o)) { + return 1; + } else { + return 2; + } + } else if (typeof o == 'boolean') { + return 3; + } else { + return 4; + } + } else { + if (Object.prototype.toString.call(o) == '[object Array]') { + // it's an array + return 5; + } else if (!o) { + // null + return 0; + } else { + // it's an object + return 6; + } + } + +} +function h$roundUpToMultipleOf(n,m) { + var rem = n % m; + return rem === 0 ? n : n - rem + m; +} + +function h$newByteArray(len) { + var len0 = Math.max(h$roundUpToMultipleOf(len, 8), 8); + var buf = new ArrayBuffer(len0); + return { buf: buf + , len: len + , i3: new Int32Array(buf) + , u8: new Uint8Array(buf) + , u1: new Uint16Array(buf) + , f3: new Float32Array(buf) + , f6: new Float64Array(buf) + , dv: new DataView(buf) + } +} +function h$wrapBuffer(buf, unalignedOk, offset, length) { + if(!unalignedOk && offset && offset % 8 !== 0) { + throw ("h$wrapBuffer: offset not aligned:" + offset); + } + if(!buf || !(buf instanceof ArrayBuffer)) + throw "h$wrapBuffer: not an ArrayBuffer" + if(!offset) { offset = 0; } + if(!length || length < 0) { length = buf.byteLength - offset; } + return { buf: buf + , len: length + , i3: (offset%4) ? null : new Int32Array(buf, offset, length >> 2) + , u8: new Uint8Array(buf, offset, length) + , u1: (offset%2) ? null : new Uint16Array(buf, offset, length >> 1) + , f3: (offset%4) ? null : new Float32Array(buf, offset, length >> 2) + , f6: (offset%8) ? null : new Float64Array(buf, offset, length >> 3) + , dv: new DataView(buf, offset, length) + }; +} +function h$newByteArrayFromBase64String(base64) { + var bin = window.atob(base64); + var ba = h$newByteArray(bin.length); + var u8 = ba.u8; + for (var i = 0; i < bin.length; i++) { + u8[i] = bin.charCodeAt(i); + } + return ba; +} +function h$byteArrayToBase64String(off, len, ba) { + var bin = ''; + var u8 = ba.u8; + var end = off + len; + for (var i = off; i < end; i++) { + bin += String.fromCharCode(u8[i]); + } + return window.btoa(bin); +} + + +// // Communication with JSaddleDevice running in the webabi webworker // Webabi Device -> JS From c5822d174856ceee52597f0be1d3adee57bd6182 Mon Sep 17 00:00:00 2001 From: Divam Date: Wed, 15 Jan 2020 19:53:38 +0900 Subject: [PATCH 04/12] Fix copyWithin usage --- src/JSaddleDevice.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/JSaddleDevice.ts b/src/JSaddleDevice.ts index da86cce..3eb7296 100644 --- a/src/JSaddleDevice.ts +++ b/src/JSaddleDevice.ts @@ -110,11 +110,8 @@ export class JSaddleDeviceFile extends BaseFile implements File { while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 8]; // Shift the remaining contents, and set size - var target = 8; - var start = length + 8 + 1; - var len = bytes_available - length; - this._jsaddleMsgBufArray.copyWithin(target, start, len); - this._jsaddleMsgBufArray32[1] = len; + this._jsaddleMsgBufArray.copyWithin(8, length + 8, bytes_available + 8); + this._jsaddleMsgBufArray32[1] = bytes_available - length; } else { var i = bytes_available; bytes_read = bytes_available; From 03a507b37a2626f9fdaed8ee9217e01975e625a2 Mon Sep 17 00:00:00 2001 From: Divam Date: Wed, 15 Jan 2020 20:30:13 +0900 Subject: [PATCH 05/12] Fixes to algo --- jsaddleJS/jsaddle_sendMsgWorker.js | 2 +- src/JSaddleDevice.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jsaddleJS/jsaddle_sendMsgWorker.js b/jsaddleJS/jsaddle_sendMsgWorker.js index 6b9dd20..d91f852 100644 --- a/jsaddleJS/jsaddle_sendMsgWorker.js +++ b/jsaddleJS/jsaddle_sendMsgWorker.js @@ -7,7 +7,7 @@ onmessage = function (msg) { var jsaddleMsgBufArray = msg.data.jsaddleMsgBufArray; const uint8 = new Uint8Array(msg.data.buf); var appendMsgToSharedBuf = function () { - var isAlreadyLocked = Atomics.compareExchange(jsaddleMsgBufArrayInt32, 0, 0, 10); + var isAlreadyLocked = Atomics.compareExchange(jsaddleMsgBufArrayInt32, 0, 0, 1); if (isAlreadyLocked === 1) { Atomics.wait(jsaddleMsgBufArrayInt32, 0, 0, 10); appendMsgToSharedBuf(); diff --git a/src/JSaddleDevice.ts b/src/JSaddleDevice.ts index 3eb7296..9fd9d10 100644 --- a/src/JSaddleDevice.ts +++ b/src/JSaddleDevice.ts @@ -100,7 +100,7 @@ export class JSaddleDeviceFile extends BaseFile implements File { var isAlreadyLocked = Atomics.compareExchange(this._jsaddleMsgBufArrayInt32, 0, 0, 1); if (isAlreadyLocked === 1) { Atomics.wait(this._jsaddleMsgBufArrayInt32, 0, 0, 10); - this.readSync(buffer, offset, length, position); + bytes_read = this.readSync(buffer, offset, length, position); } else { var bytes_available = this._jsaddleMsgBufArray32[1]; if (bytes_available > 0) { From 5f70f7b64a1c39570b4d2c9c44db22ff5d8d3b5c Mon Sep 17 00:00:00 2001 From: Divam Date: Thu, 16 Jan 2020 14:07:57 +0900 Subject: [PATCH 06/12] Remove the recursive calls, add protection against overflow, lock for any non-zero value, and increase the wait to 50ms --- jsaddleJS/jsaddle_sendMsgWorker.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/jsaddleJS/jsaddle_sendMsgWorker.js b/jsaddleJS/jsaddle_sendMsgWorker.js index d91f852..d049b7e 100644 --- a/jsaddleJS/jsaddle_sendMsgWorker.js +++ b/jsaddleJS/jsaddle_sendMsgWorker.js @@ -1,6 +1,10 @@ // Atomics.wait is not possible on main thread // so this worker takes the messages from jsaddle.js and appends to the SharedArrayBuffer +// The payload (msg) contains 4 bytes for length + actual message +// The whole payload is written to the SharedArrayBuffer in one go +// but it might be read by the other side in pieces +// While the HS side is reading the messages it will keep the lock onmessage = function (msg) { var jsaddleMsgBufArrayInt32 = msg.data.jsaddleMsgBufArrayInt32; var jsaddleMsgBufArray32 = msg.data.jsaddleMsgBufArray32; @@ -8,20 +12,30 @@ onmessage = function (msg) { const uint8 = new Uint8Array(msg.data.buf); var appendMsgToSharedBuf = function () { var isAlreadyLocked = Atomics.compareExchange(jsaddleMsgBufArrayInt32, 0, 0, 1); - if (isAlreadyLocked === 1) { - Atomics.wait(jsaddleMsgBufArrayInt32, 0, 0, 10); - appendMsgToSharedBuf(); + if (isAlreadyLocked !== 0) { + Atomics.wait(jsaddleMsgBufArrayInt32, 0, 0, 50); + return false; } else { var len = uint8.length; var prevLen = jsaddleMsgBufArray32[1]; var totalLen = len + prevLen; + if (totalLen > 10 * 1024 * 1024) { // Protection against over filling the SharedArrayBuffer + console.log("JSaddle.js warning: SharedArrayBuffer overflow!"); + // Release the lock + jsaddleMsgBufArrayInt32[0] = 0; + return false; + } var startOffset = prevLen + 8; // Two 32 bit uint var i = len; while (i--) jsaddleMsgBufArray[startOffset + i] = uint8[i]; jsaddleMsgBufArray32[1] = totalLen; // Release the lock jsaddleMsgBufArrayInt32[0] = 0; + return true; } }; - appendMsgToSharedBuf(); + var done = false; + while (done === false) { + done = appendMsgToSharedBuf(); + }; } From 3c200fb9d4d8c408c413da32237b394a27acf1ce Mon Sep 17 00:00:00 2001 From: Divam Date: Thu, 16 Jan 2020 14:10:33 +0900 Subject: [PATCH 07/12] Implementation for reading the entire buffer in two read calls and locking the buffer while in the middle of read. --- src/JSaddleDevice.ts | 68 +++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/src/JSaddleDevice.ts b/src/JSaddleDevice.ts index 9fd9d10..1e20434 100644 --- a/src/JSaddleDevice.ts +++ b/src/JSaddleDevice.ts @@ -97,31 +97,61 @@ export class JSaddleDeviceFile extends BaseFile implements File { } public readSync(buffer: Buffer, offset: number, length: number, position: number | null): number { var bytes_read = 0; - var isAlreadyLocked = Atomics.compareExchange(this._jsaddleMsgBufArrayInt32, 0, 0, 1); - if (isAlreadyLocked === 1) { + var lockValue = Atomics.compareExchange(this._jsaddleMsgBufArrayInt32, 0, 0, 2); + if (lockValue === 1) { // Locked by appendMsgToSharedBuf Atomics.wait(this._jsaddleMsgBufArrayInt32, 0, 0, 10); bytes_read = this.readSync(buffer, offset, length, position); } else { - var bytes_available = this._jsaddleMsgBufArray32[1]; - if (bytes_available > 0) { - if (bytes_available > length) { - let i : number = length; - bytes_read = i; - while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 8]; + var payloadSize = this._jsaddleMsgBufArray32[1]; + if (payloadSize > 0) { + if (lockValue === 3) { // continue append of data + if (payloadSize > length) { + // This block of code is never executed + // ie the length is always equal to payloadSize + let i : number = length; + bytes_read = i; + while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 8]; - // Shift the remaining contents, and set size - this._jsaddleMsgBufArray.copyWithin(8, length + 8, bytes_available + 8); - this._jsaddleMsgBufArray32[1] = bytes_available - length; - } else { - var i = bytes_available; - bytes_read = bytes_available; - while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 8]; - // Set remaining bytes to 0 - this._jsaddleMsgBufArray32[1] = 0; + // Shift the remaining contents, and set size + this._jsaddleMsgBufArray.copyWithin(8, length + 8, payloadSize + 8); + this._jsaddleMsgBufArray32[1] = payloadSize - length; + // Keep the lock + this._jsaddleMsgBufArrayInt32[0] = 3; + } else { + var i = payloadSize; + bytes_read = i; + while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 8]; + // Set remaining bytes to 0 + this._jsaddleMsgBufArray32[1] = 0; + // Release the lock + this._jsaddleMsgBufArrayInt32[0] = 0; + } + } else { // New read request, include the payloadSize in first 4 bytes + if ((payloadSize + 4) > length) { + let i : number = length; + bytes_read = length; + while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 4]; + + // Shift the remaining contents, and set size + this._jsaddleMsgBufArray.copyWithin(8, length + 4, payloadSize + 8); + this._jsaddleMsgBufArray32[1] = payloadSize - (length - 4); + // Keep the lock + this._jsaddleMsgBufArrayInt32[0] = 3; + } else { + // console.log("4>"); + var i = payloadSize + 4; + bytes_read = i; + while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 4]; + // Set remaining bytes to 0 + this._jsaddleMsgBufArray32[1] = 0; + // Release the lock + this._jsaddleMsgBufArrayInt32[0] = 0; + } } + } else { + // Release the lock + this._jsaddleMsgBufArrayInt32[0] = 0; } - // Release the lock - this._jsaddleMsgBufArrayInt32[0] = 0; } return bytes_read; } From 5c3e3358ca3097ea56876189b3a379fb86e8683c Mon Sep 17 00:00:00 2001 From: Divam Date: Fri, 17 Jan 2020 09:56:10 +0900 Subject: [PATCH 08/12] Do mmap2 even when addr /= currentSize --- src/process.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/process.ts b/src/process.ts index 754de3a..8cc467a 100644 --- a/src/process.ts +++ b/src/process.ts @@ -536,8 +536,7 @@ export class Process { mmap2(addr: number, len: number, prot: number, flags: number, fd: number, offset: number): number { // Ignore prot and flags var currentSize = this.memoryEnd; - if ((fd === -1) && (offset === 0) - && ((addr === 0) || (addr === currentSize))) { + if ((fd === -1) && (offset === 0)) { var newSize = this.brk(currentSize + len); return currentSize; } else { From b5dbf99defa9ddaf0bf1a837b220a91f2889da88 Mon Sep 17 00:00:00 2001 From: Divam Date: Fri, 17 Jan 2020 11:45:51 +0900 Subject: [PATCH 09/12] Add some comments to jsaddle.js --- jsaddleJS/jsaddle.js | 17 ++++++++++------- jsaddleJS/jsaddle_sendMsgWorker.js | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/jsaddleJS/jsaddle.js b/jsaddleJS/jsaddle.js index 22cd636..1cf82c3 100644 --- a/jsaddleJS/jsaddle.js +++ b/jsaddleJS/jsaddle.js @@ -1,3 +1,7 @@ +// JSaddle JS code +// The code is copied from jsaddle/src/Language/Javascript/JSaddle/Run/Files.hs + +// @@@@ START of JSaddle JS code @@@@ var dec = new TextDecoder(); var enc = new TextEncoder(); @@ -401,12 +405,12 @@ function h$byteArrayToBase64String(off, len, ba) { return window.btoa(bin); } +// @@@@ END of JSaddle JS code @@@@ -// // Communication with JSaddleDevice running in the webabi webworker // Webabi Device -> JS -// channel is used to receive messages for each SYS_Write call +// MessageChannel is used to receive messages for each SYS_Write call // This is a non-blocking call on the webabi side // var channel = new MessageChannel(); @@ -439,16 +443,15 @@ function jsaddleHandlerMsgs (msgs) { // JS -> Webabi Device // SharedArrayBuffer is used to communicate back to JSaddleDevice in wasm side. -// Since the jsaddle-wasm will do a SYS_read whenever it is free -// append all the messages in this buffer. +// The jsaddle-wasm will do a SYS_read to read the data. // -// First UInt (32 bits), hold a lock to the read/write of this shared buffer +// The first Int (32 bits) hold a lock to the read/write of this shared buffer // and this value should be read/written with atomic operations. -// Second UInt (32 bits), indicate size of payload currently in this buffer +// The second UInt (32 bits) indicates the total size of payload currently in the buffer // After that buffer contains the payload // Note: the payload can contain multiple encoded messages -// There for each message is prepended with its own size. +// Each message is prepended with its own size. var jsaddleMsgSharedBuf = new SharedArrayBuffer(10*1024*1024); var jsaddleMsgBufArray = new Uint8Array(jsaddleMsgSharedBuf); var jsaddleMsgBufArray32 = new Uint32Array(jsaddleMsgSharedBuf); diff --git a/jsaddleJS/jsaddle_sendMsgWorker.js b/jsaddleJS/jsaddle_sendMsgWorker.js index d049b7e..25ee85f 100644 --- a/jsaddleJS/jsaddle_sendMsgWorker.js +++ b/jsaddleJS/jsaddle_sendMsgWorker.js @@ -4,7 +4,7 @@ // The payload (msg) contains 4 bytes for length + actual message // The whole payload is written to the SharedArrayBuffer in one go // but it might be read by the other side in pieces -// While the HS side is reading the messages it will keep the lock +// While the HS side is reading the messages, it will keep the lock (the value of 0 index will be non-zero) onmessage = function (msg) { var jsaddleMsgBufArrayInt32 = msg.data.jsaddleMsgBufArrayInt32; var jsaddleMsgBufArray32 = msg.data.jsaddleMsgBufArray32; From 5cefa2e2d4bdb3690c51925066e589fecc5330d7 Mon Sep 17 00:00:00 2001 From: Divam Date: Fri, 17 Jan 2020 12:07:34 +0900 Subject: [PATCH 10/12] add notify, and refactor lock release code --- jsaddleJS/jsaddle_sendMsgWorker.js | 1 + src/JSaddleDevice.ts | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/jsaddleJS/jsaddle_sendMsgWorker.js b/jsaddleJS/jsaddle_sendMsgWorker.js index 25ee85f..bc3b010 100644 --- a/jsaddleJS/jsaddle_sendMsgWorker.js +++ b/jsaddleJS/jsaddle_sendMsgWorker.js @@ -31,6 +31,7 @@ onmessage = function (msg) { jsaddleMsgBufArray32[1] = totalLen; // Release the lock jsaddleMsgBufArrayInt32[0] = 0; + Atomics.notify(jsaddleMsgBufArrayInt32, 0); return true; } }; diff --git a/src/JSaddleDevice.ts b/src/JSaddleDevice.ts index 1e20434..7d414fc 100644 --- a/src/JSaddleDevice.ts +++ b/src/JSaddleDevice.ts @@ -99,9 +99,10 @@ export class JSaddleDeviceFile extends BaseFile implements File { var bytes_read = 0; var lockValue = Atomics.compareExchange(this._jsaddleMsgBufArrayInt32, 0, 0, 2); if (lockValue === 1) { // Locked by appendMsgToSharedBuf - Atomics.wait(this._jsaddleMsgBufArrayInt32, 0, 0, 10); + Atomics.wait(this._jsaddleMsgBufArrayInt32, 0, 0, 50); bytes_read = this.readSync(buffer, offset, length, position); } else { + var releaseLock = true; var payloadSize = this._jsaddleMsgBufArray32[1]; if (payloadSize > 0) { if (lockValue === 3) { // continue append of data @@ -115,16 +116,13 @@ export class JSaddleDeviceFile extends BaseFile implements File { // Shift the remaining contents, and set size this._jsaddleMsgBufArray.copyWithin(8, length + 8, payloadSize + 8); this._jsaddleMsgBufArray32[1] = payloadSize - length; - // Keep the lock - this._jsaddleMsgBufArrayInt32[0] = 3; + releaseLock = false; } else { var i = payloadSize; bytes_read = i; while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 8]; // Set remaining bytes to 0 this._jsaddleMsgBufArray32[1] = 0; - // Release the lock - this._jsaddleMsgBufArrayInt32[0] = 0; } } else { // New read request, include the payloadSize in first 4 bytes if ((payloadSize + 4) > length) { @@ -135,8 +133,7 @@ export class JSaddleDeviceFile extends BaseFile implements File { // Shift the remaining contents, and set size this._jsaddleMsgBufArray.copyWithin(8, length + 4, payloadSize + 8); this._jsaddleMsgBufArray32[1] = payloadSize - (length - 4); - // Keep the lock - this._jsaddleMsgBufArrayInt32[0] = 3; + releaseLock = false; } else { // console.log("4>"); var i = payloadSize + 4; @@ -144,13 +141,17 @@ export class JSaddleDeviceFile extends BaseFile implements File { while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 4]; // Set remaining bytes to 0 this._jsaddleMsgBufArray32[1] = 0; - // Release the lock - this._jsaddleMsgBufArrayInt32[0] = 0; } } - } else { + } + if (releaseLock) { // Release the lock this._jsaddleMsgBufArrayInt32[0] = 0; + // @ts-ignore + Atomics.notify(this._jsaddleMsgBufArrayInt32, 0); + } else { + // Keep the lock + this._jsaddleMsgBufArrayInt32[0] = 3; } } return bytes_read; From 900a4949477b9920b71aaf80428f234a9d61af14 Mon Sep 17 00:00:00 2001 From: Divam Date: Fri, 17 Jan 2020 13:16:54 +0900 Subject: [PATCH 11/12] Refactor readSync api --- src/JSaddleDevice.ts | 53 ++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/src/JSaddleDevice.ts b/src/JSaddleDevice.ts index 7d414fc..e405c1e 100644 --- a/src/JSaddleDevice.ts +++ b/src/JSaddleDevice.ts @@ -105,44 +105,25 @@ export class JSaddleDeviceFile extends BaseFile implements File { var releaseLock = true; var payloadSize = this._jsaddleMsgBufArray32[1]; if (payloadSize > 0) { + var startCopyFrom = 4; + var prependSizeBytes = 4; if (lockValue === 3) { // continue append of data - if (payloadSize > length) { - // This block of code is never executed - // ie the length is always equal to payloadSize - let i : number = length; - bytes_read = i; - while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 8]; - - // Shift the remaining contents, and set size - this._jsaddleMsgBufArray.copyWithin(8, length + 8, payloadSize + 8); - this._jsaddleMsgBufArray32[1] = payloadSize - length; - releaseLock = false; - } else { - var i = payloadSize; - bytes_read = i; - while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 8]; - // Set remaining bytes to 0 - this._jsaddleMsgBufArray32[1] = 0; - } - } else { // New read request, include the payloadSize in first 4 bytes - if ((payloadSize + 4) > length) { - let i : number = length; - bytes_read = length; - while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 4]; + startCopyFrom = 8; + prependSizeBytes = 0; + } + if ((prependSizeBytes + payloadSize) > length) { + bytes_read = length; + releaseLock = false; + } else { + bytes_read = prependSizeBytes + payloadSize; + } + buffer.set(this._jsaddleMsgBufArray.subarray(startCopyFrom, startCopyFrom + bytes_read), offset); - // Shift the remaining contents, and set size - this._jsaddleMsgBufArray.copyWithin(8, length + 4, payloadSize + 8); - this._jsaddleMsgBufArray32[1] = payloadSize - (length - 4); - releaseLock = false; - } else { - // console.log("4>"); - var i = payloadSize + 4; - bytes_read = i; - while (i--) buffer[offset + i] = this._jsaddleMsgBufArray[i + 4]; - // Set remaining bytes to 0 - this._jsaddleMsgBufArray32[1] = 0; - } + // Shift the remaining contents, and set size + if ((prependSizeBytes + payloadSize) > length) { + this._jsaddleMsgBufArray.copyWithin(8, startCopyFrom + length, payloadSize + 8); } + this._jsaddleMsgBufArray32[1] = (prependSizeBytes + payloadSize) - bytes_read; } if (releaseLock) { // Release the lock @@ -150,7 +131,7 @@ export class JSaddleDeviceFile extends BaseFile implements File { // @ts-ignore Atomics.notify(this._jsaddleMsgBufArrayInt32, 0); } else { - // Keep the lock + // Keep the lock, and continue append of data on next readSync call this._jsaddleMsgBufArrayInt32[0] = 3; } } From 265f80f631112c6e677e55165c397fb2410c0cf3 Mon Sep 17 00:00:00 2001 From: Divam Date: Sat, 18 Jan 2020 10:56:48 +0900 Subject: [PATCH 12/12] Update package-lock.json --- package-lock.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4a89bbf..9c88655 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1577,7 +1577,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -1992,7 +1993,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2048,6 +2050,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2091,12 +2094,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } },