mirror of
https://github.com/gorhill/uBlock.git
synced 2026-03-11 09:04:36 +00:00
Drop threaded serialization/deserialization
This commit is contained in:
parent
7351de08bb
commit
6314b7f95e
1 changed files with 0 additions and 285 deletions
|
|
@ -1132,34 +1132,6 @@ export const isSerialized = s =>
|
|||
export const isCompressed = s =>
|
||||
typeof s === 'string' && s.startsWith(MAGICLZ4PREFIX);
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Configuration
|
||||
*
|
||||
* */
|
||||
|
||||
const defaultConfig = {
|
||||
threadTTL: 3000,
|
||||
};
|
||||
|
||||
const validateConfig = {
|
||||
threadTTL: val => val > 0,
|
||||
};
|
||||
|
||||
const currentConfig = Object.assign({}, defaultConfig);
|
||||
|
||||
export const getConfig = ( ) => Object.assign({}, currentConfig);
|
||||
|
||||
export const setConfig = config => {
|
||||
for ( const key in Object.keys(config) ) {
|
||||
if ( Object.hasOwn(defaultConfig, key) === false ) { continue; }
|
||||
const val = config[key];
|
||||
if ( typeof val !== typeof defaultConfig[key] ) { continue; }
|
||||
if ( (validateConfig[key])(val) === false ) { continue; }
|
||||
currentConfig[key] = val;
|
||||
}
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Asynchronous APIs
|
||||
|
|
@ -1169,270 +1141,13 @@ export const setConfig = config => {
|
|||
*
|
||||
* */
|
||||
|
||||
const THREAD_AREYOUREADY = 1;
|
||||
const THREAD_IAMREADY = 2;
|
||||
const THREAD_SERIALIZE = 3;
|
||||
const THREAD_DESERIALIZE = 4;
|
||||
|
||||
class MainThread {
|
||||
constructor() {
|
||||
this.name = 'main';
|
||||
this.jobs = [];
|
||||
this.workload = 0;
|
||||
this.timer = undefined;
|
||||
this.busy = 2;
|
||||
}
|
||||
|
||||
process() {
|
||||
if ( this.jobs.length === 0 ) { return; }
|
||||
const job = this.jobs.shift();
|
||||
this.workload -= job.size;
|
||||
const result = job.what === THREAD_SERIALIZE
|
||||
? serialize(job.data, job.options)
|
||||
: deserialize(job.data);
|
||||
job.resolve(result);
|
||||
this.processAsync();
|
||||
if ( this.jobs.length === 0 ) {
|
||||
this.busy = 2;
|
||||
} else if ( this.busy > 2 ) {
|
||||
this.busy -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
processAsync() {
|
||||
if ( this.timer !== undefined ) { return; }
|
||||
if ( this.jobs.length === 0 ) { return; }
|
||||
this.timer = globalThis.requestIdleCallback(deadline => {
|
||||
this.timer = undefined;
|
||||
globalThis.queueMicrotask(( ) => {
|
||||
this.process();
|
||||
});
|
||||
if ( deadline.timeRemaining() === 0 ) {
|
||||
this.busy += 1;
|
||||
}
|
||||
}, { timeout: 5 });
|
||||
}
|
||||
|
||||
serialize(data, options) {
|
||||
return new Promise(resolve => {
|
||||
this.workload += 1;
|
||||
this.jobs.push({ what: THREAD_SERIALIZE, data, options, size: 1, resolve });
|
||||
this.processAsync();
|
||||
});
|
||||
}
|
||||
|
||||
deserialize(data, options) {
|
||||
return new Promise(resolve => {
|
||||
const size = data.length;
|
||||
this.workload += size;
|
||||
this.jobs.push({ what: THREAD_DESERIALIZE, data, options, size, resolve });
|
||||
this.processAsync();
|
||||
});
|
||||
}
|
||||
|
||||
get queueSize() {
|
||||
return this.jobs.length;
|
||||
}
|
||||
|
||||
get workSize() {
|
||||
return this.workload * this.busy;
|
||||
}
|
||||
}
|
||||
|
||||
class Thread {
|
||||
constructor(gcer) {
|
||||
this.name = 'worker';
|
||||
this.jobs = new Map();
|
||||
this.jobIdGenerator = 1;
|
||||
this.workload = 0;
|
||||
this.workerAccessTime = 0;
|
||||
this.workerTimer = undefined;
|
||||
this.gcer = gcer;
|
||||
this.workerPromise = new Promise(resolve => {
|
||||
let worker = null;
|
||||
try {
|
||||
worker = new Worker('js/s14e-serializer.js', { type: 'module' });
|
||||
worker.onmessage = ev => {
|
||||
const msg = ev.data;
|
||||
if ( isInstanceOf(msg, 'Object') === false ) { return; }
|
||||
if ( msg.what === THREAD_IAMREADY ) {
|
||||
worker.onmessage = ev => { this.onmessage(ev); };
|
||||
worker.onerror = null;
|
||||
resolve(worker);
|
||||
}
|
||||
};
|
||||
worker.onerror = ( ) => {
|
||||
worker.onmessage = worker.onerror = null;
|
||||
resolve(null);
|
||||
};
|
||||
worker.postMessage({
|
||||
what: THREAD_AREYOUREADY,
|
||||
config: currentConfig,
|
||||
});
|
||||
} catch(ex) {
|
||||
console.info(ex);
|
||||
worker.onmessage = worker.onerror = null;
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
countdownWorker() {
|
||||
if ( this.workerTimer !== undefined ) { return; }
|
||||
this.workerTimer = setTimeout(async ( ) => {
|
||||
this.workerTimer = undefined;
|
||||
if ( this.jobs.size !== 0 ) { return; }
|
||||
const idleTime = Date.now() - this.workerAccessTime;
|
||||
if ( idleTime < currentConfig.threadTTL ) {
|
||||
return this.countdownWorker();
|
||||
}
|
||||
const worker = await this.workerPromise;
|
||||
if ( this.jobs.size !== 0 ) { return; }
|
||||
this.gcer(this);
|
||||
if ( worker === null ) { return; }
|
||||
worker.onmessage = worker.onerror = null;
|
||||
worker.terminate();
|
||||
}, currentConfig.threadTTL);
|
||||
}
|
||||
|
||||
onmessage(ev) {
|
||||
this.ondone(ev.data);
|
||||
}
|
||||
|
||||
ondone(job) {
|
||||
const resolve = this.jobs.get(job.id);
|
||||
if ( resolve === undefined ) { return; }
|
||||
this.jobs.delete(job.id);
|
||||
resolve(job.result);
|
||||
this.workload -= job.size;
|
||||
if ( this.jobs.size !== 0 ) { return; }
|
||||
this.countdownWorker();
|
||||
}
|
||||
|
||||
async serialize(data, options) {
|
||||
return new Promise(resolve => {
|
||||
const id = this.jobIdGenerator++;
|
||||
this.workload += 1;
|
||||
this.jobs.set(id, resolve);
|
||||
return this.workerPromise.then(worker => {
|
||||
this.workerAccessTime = Date.now();
|
||||
if ( worker === null ) {
|
||||
this.ondone({ id, result: serialize(data, options), size: 1 });
|
||||
} else {
|
||||
worker.postMessage({ what: THREAD_SERIALIZE, id, data, options, size: 1 });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async deserialize(data, options) {
|
||||
return new Promise(resolve => {
|
||||
const id = this.jobIdGenerator++;
|
||||
const size = data.length;
|
||||
this.workload += size;
|
||||
this.jobs.set(id, resolve);
|
||||
return this.workerPromise.then(worker => {
|
||||
this.workerAccessTime = Date.now();
|
||||
if ( worker === null ) {
|
||||
this.ondone({ id, result: deserialize(data, options), size });
|
||||
} else {
|
||||
worker.postMessage({ what: THREAD_DESERIALIZE, id, data, options, size });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get queueSize() {
|
||||
return this.jobs.size;
|
||||
}
|
||||
|
||||
get workSize() {
|
||||
return this.workload;
|
||||
}
|
||||
}
|
||||
|
||||
const threads = {
|
||||
pool: [ new MainThread() ],
|
||||
thread(maxPoolSize) {
|
||||
const poolSize = this.pool.length;
|
||||
if ( poolSize !== 0 && poolSize >= maxPoolSize ) {
|
||||
if ( poolSize === 1 ) { return this.pool[0]; }
|
||||
return this.pool.reduce((a, b) => {
|
||||
//console.log(`${a.name}: q=${a.queueSize} w=${a.workSize} ${b.name}: q=${b.queueSize} w=${b.workSize}`);
|
||||
if ( b.queueSize === 0 ) { return b; }
|
||||
if ( a.queueSize === 0 ) { return a; }
|
||||
return b.workSize < a.workSize ? b : a;
|
||||
});
|
||||
}
|
||||
const thread = new Thread(thread => {
|
||||
const pos = this.pool.indexOf(thread);
|
||||
if ( pos === -1 ) { return; }
|
||||
this.pool.splice(pos, 1);
|
||||
});
|
||||
this.pool.push(thread);
|
||||
return thread;
|
||||
},
|
||||
};
|
||||
|
||||
export async function serializeAsync(data, options = {}) {
|
||||
const maxThreadCount = options.multithreaded || 0;
|
||||
if ( maxThreadCount === 0 ) {
|
||||
return serialize(data, options);
|
||||
}
|
||||
const thread = threads.thread(maxThreadCount);
|
||||
//console.log(`serializeAsync: thread=${thread.name} workload=${thread.workSize}`);
|
||||
const result = await thread.serialize(data, options);
|
||||
if ( result !== undefined ) { return result; }
|
||||
return serialize(data, options);
|
||||
}
|
||||
|
||||
export async function deserializeAsync(data, options = {}) {
|
||||
if ( isSerialized(data) === false ) { return data; }
|
||||
const maxThreadCount = options.multithreaded || 0;
|
||||
if ( maxThreadCount === 0 ) {
|
||||
return deserialize(data, options);
|
||||
}
|
||||
const thread = threads.thread(maxThreadCount);
|
||||
//console.log(`deserializeAsync: thread=${thread.name} data=${data.length} workload=${thread.workSize}`);
|
||||
const result = await thread.deserialize(data, options);
|
||||
if ( result !== undefined ) { return result; }
|
||||
return deserialize(data, options);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Worker-only code
|
||||
*
|
||||
* */
|
||||
|
||||
if ( isInstanceOf(globalThis, 'DedicatedWorkerGlobalScope') ) {
|
||||
globalThis.onmessage = ev => {
|
||||
const msg = ev.data;
|
||||
switch ( msg.what ) {
|
||||
case THREAD_AREYOUREADY:
|
||||
setConfig(msg.config);
|
||||
globalThis.postMessage({ what: THREAD_IAMREADY });
|
||||
break;
|
||||
case THREAD_SERIALIZE: {
|
||||
const result = serialize(msg.data, msg.options);
|
||||
globalThis.postMessage({ id: msg.id, size: msg.size, result });
|
||||
break;
|
||||
}
|
||||
case THREAD_DESERIALIZE: {
|
||||
let result;
|
||||
try {
|
||||
result = deserialize(msg.data);
|
||||
} catch(ex) {
|
||||
console.error(ex);
|
||||
} finally {
|
||||
globalThis.postMessage({ id: msg.id, size: msg.size, result });
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
|||
Loading…
Reference in a new issue