Move base64 conversions off of the main thread
This commit is contained in:
parent
911bc63c67
commit
02fbea96c0
7 changed files with 167 additions and 5 deletions
|
@ -12,6 +12,7 @@ test/views/*.js
|
||||||
# Generated files
|
# Generated files
|
||||||
js/components.js
|
js/components.js
|
||||||
js/libtextsecure.js
|
js/libtextsecure.js
|
||||||
|
js/util_worker.js
|
||||||
js/libsignal-protocol-worker.js
|
js/libsignal-protocol-worker.js
|
||||||
libtextsecure/components.js
|
libtextsecure/components.js
|
||||||
libtextsecure/test/test.js
|
libtextsecure/test/test.js
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -17,6 +17,7 @@ sql/
|
||||||
|
|
||||||
# generated files
|
# generated files
|
||||||
js/components.js
|
js/components.js
|
||||||
|
js/util_worker.js
|
||||||
js/libtextsecure.js
|
js/libtextsecure.js
|
||||||
libtextsecure/components.js
|
libtextsecure/components.js
|
||||||
libtextsecure/test/test.js
|
libtextsecure/test/test.js
|
||||||
|
|
|
@ -6,6 +6,7 @@ config/local-*.json
|
||||||
config/local.json
|
config/local.json
|
||||||
dist/**
|
dist/**
|
||||||
js/components.js
|
js/components.js
|
||||||
|
js/util_worker.js
|
||||||
js/libtextsecure.js
|
js/libtextsecure.js
|
||||||
libtextsecure/components.js
|
libtextsecure/components.js
|
||||||
libtextsecure/test/test.js
|
libtextsecure/test/test.js
|
||||||
|
|
|
@ -33,6 +33,14 @@ module.exports = grunt => {
|
||||||
src: components,
|
src: components,
|
||||||
dest: 'js/components.js',
|
dest: 'js/components.js',
|
||||||
},
|
},
|
||||||
|
util_worker: {
|
||||||
|
src: [
|
||||||
|
'components/bytebuffer/dist/ByteBufferAB.js',
|
||||||
|
'components/long/dist/Long.js',
|
||||||
|
'js/util_worker_tasks.js',
|
||||||
|
],
|
||||||
|
dest: 'js/util_worker.js',
|
||||||
|
},
|
||||||
libtextsecurecomponents: {
|
libtextsecurecomponents: {
|
||||||
src: libtextsecurecomponents,
|
src: libtextsecurecomponents,
|
||||||
dest: 'libtextsecure/components.js',
|
dest: 'libtextsecure/components.js',
|
||||||
|
|
|
@ -166,7 +166,7 @@ ipcRenderer.on(
|
||||||
const job = _getJob(jobId);
|
const job = _getJob(jobId);
|
||||||
if (!job) {
|
if (!job) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Received job reply to job ${jobId}, but did not have it in our registry!`
|
`Received SQL channel reply to job ${jobId}, but did not have it in our registry!`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,9 @@ ipcRenderer.on(
|
||||||
|
|
||||||
if (errorForDisplay) {
|
if (errorForDisplay) {
|
||||||
return reject(
|
return reject(
|
||||||
new Error(`Error calling channel ${fnName}: ${errorForDisplay}`)
|
new Error(
|
||||||
|
`Error received from SQL channel job ${jobId} (${fnName}): ${errorForDisplay}`
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +198,8 @@ function makeChannel(fnName) {
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() => reject(new Error(`Request to ${fnName} timed out`)),
|
() =>
|
||||||
|
reject(new Error(`SQL channel job ${jobId} (${fnName}) timed out`)),
|
||||||
DATABASE_UPDATE_TIMEOUT
|
DATABASE_UPDATE_TIMEOUT
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
44
js/util_worker_tasks.js
Normal file
44
js/util_worker_tasks.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/* global dcodeIO */
|
||||||
|
/* eslint-disable strict */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const functions = {
|
||||||
|
stringToArrayBufferBase64,
|
||||||
|
arrayBufferToStringBase64,
|
||||||
|
};
|
||||||
|
|
||||||
|
onmessage = async e => {
|
||||||
|
const [jobId, fnName, ...args] = e.data;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fn = functions[fnName];
|
||||||
|
if (!fn) {
|
||||||
|
throw new Error(`Worker: job ${jobId} did not find function ${fnName}`);
|
||||||
|
}
|
||||||
|
const result = await fn(...args);
|
||||||
|
postMessage([jobId, null, result]);
|
||||||
|
} catch (error) {
|
||||||
|
const errorForDisplay = prepareErrorForPostMessage(error);
|
||||||
|
postMessage([jobId, errorForDisplay]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function prepareErrorForPostMessage(error) {
|
||||||
|
if (!error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.stack) {
|
||||||
|
return error.stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToArrayBufferBase64(string) {
|
||||||
|
return dcodeIO.ByteBuffer.wrap(string, 'base64').toArrayBuffer();
|
||||||
|
}
|
||||||
|
function arrayBufferToStringBase64(arrayBuffer) {
|
||||||
|
return dcodeIO.ByteBuffer.wrap(arrayBuffer).toString('base64');
|
||||||
|
}
|
|
@ -9,9 +9,112 @@
|
||||||
/* global _: false */
|
/* global _: false */
|
||||||
/* global ContactBuffer: false */
|
/* global ContactBuffer: false */
|
||||||
/* global GroupBuffer: false */
|
/* global GroupBuffer: false */
|
||||||
|
/* global Worker: false */
|
||||||
|
|
||||||
/* eslint-disable more/no-then */
|
/* eslint-disable more/no-then */
|
||||||
|
|
||||||
|
const WORKER_TIMEOUT = 60 * 1000; // one minute
|
||||||
|
|
||||||
|
const _utilWorker = new Worker('js/util_worker.js');
|
||||||
|
const _jobs = Object.create(null);
|
||||||
|
const _DEBUG = false;
|
||||||
|
let _jobCounter = 0;
|
||||||
|
|
||||||
|
function _makeJob(fnName) {
|
||||||
|
_jobCounter += 1;
|
||||||
|
const id = _jobCounter;
|
||||||
|
|
||||||
|
if (_DEBUG) {
|
||||||
|
window.log.info(`Worker job ${id} (${fnName}) started`);
|
||||||
|
}
|
||||||
|
_jobs[id] = {
|
||||||
|
fnName,
|
||||||
|
start: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _updateJob(id, data) {
|
||||||
|
const { resolve, reject } = data;
|
||||||
|
const { fnName, start } = _jobs[id];
|
||||||
|
|
||||||
|
_jobs[id] = {
|
||||||
|
..._jobs[id],
|
||||||
|
...data,
|
||||||
|
resolve: value => {
|
||||||
|
_removeJob(id);
|
||||||
|
const end = Date.now();
|
||||||
|
window.log.info(
|
||||||
|
`Worker job ${id} (${fnName}) succeeded in ${end - start}ms`
|
||||||
|
);
|
||||||
|
return resolve(value);
|
||||||
|
},
|
||||||
|
reject: error => {
|
||||||
|
_removeJob(id);
|
||||||
|
const end = Date.now();
|
||||||
|
window.log.info(
|
||||||
|
`Worker job ${id} (${fnName}) failed in ${end - start}ms`
|
||||||
|
);
|
||||||
|
return reject(error);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function _removeJob(id) {
|
||||||
|
if (_DEBUG) {
|
||||||
|
_jobs[id].complete = true;
|
||||||
|
} else {
|
||||||
|
delete _jobs[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getJob(id) {
|
||||||
|
return _jobs[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callWorker(fnName, ...args) {
|
||||||
|
const jobId = _makeJob(fnName);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
_utilWorker.postMessage([jobId, fnName, ...args]);
|
||||||
|
|
||||||
|
_updateJob(jobId, {
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
args: _DEBUG ? args : null,
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(
|
||||||
|
() => reject(new Error(`Worker job ${jobId} (${fnName}) timed out`)),
|
||||||
|
WORKER_TIMEOUT
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_utilWorker.onmessage = e => {
|
||||||
|
const [jobId, errorForDisplay, result] = e.data;
|
||||||
|
|
||||||
|
const job = _getJob(jobId);
|
||||||
|
if (!job) {
|
||||||
|
throw new Error(
|
||||||
|
`Received worker reply to job ${jobId}, but did not have it in our registry!`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { resolve, reject, fnName } = job;
|
||||||
|
|
||||||
|
if (errorForDisplay) {
|
||||||
|
return reject(
|
||||||
|
new Error(
|
||||||
|
`Error received from worker job ${jobId} (${fnName}): ${errorForDisplay}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(result);
|
||||||
|
};
|
||||||
|
|
||||||
function MessageReceiver(username, password, signalingKey, options = {}) {
|
function MessageReceiver(username, password, signalingKey, options = {}) {
|
||||||
this.count = 0;
|
this.count = 0;
|
||||||
|
|
||||||
|
@ -35,10 +138,11 @@ MessageReceiver.stringToArrayBuffer = string =>
|
||||||
Promise.resolve(dcodeIO.ByteBuffer.wrap(string, 'binary').toArrayBuffer());
|
Promise.resolve(dcodeIO.ByteBuffer.wrap(string, 'binary').toArrayBuffer());
|
||||||
MessageReceiver.arrayBufferToString = arrayBuffer =>
|
MessageReceiver.arrayBufferToString = arrayBuffer =>
|
||||||
Promise.resolve(dcodeIO.ByteBuffer.wrap(arrayBuffer).toString('binary'));
|
Promise.resolve(dcodeIO.ByteBuffer.wrap(arrayBuffer).toString('binary'));
|
||||||
|
|
||||||
MessageReceiver.stringToArrayBufferBase64 = string =>
|
MessageReceiver.stringToArrayBufferBase64 = string =>
|
||||||
Promise.resolve(dcodeIO.ByteBuffer.wrap(string, 'base64').toArrayBuffer());
|
callWorker('stringToArrayBufferBase64', string);
|
||||||
MessageReceiver.arrayBufferToStringBase64 = arrayBuffer =>
|
MessageReceiver.arrayBufferToStringBase64 = arrayBuffer =>
|
||||||
Promise.resolve(dcodeIO.ByteBuffer.wrap(arrayBuffer).toString('base64'));
|
callWorker('arrayBufferToStringBase64', arrayBuffer);
|
||||||
|
|
||||||
MessageReceiver.prototype = new textsecure.EventTarget();
|
MessageReceiver.prototype = new textsecure.EventTarget();
|
||||||
MessageReceiver.prototype.extend({
|
MessageReceiver.prototype.extend({
|
||||||
|
|
Loading…
Reference in a new issue