From 6314b7f95e7ddef54e4d1e036cbe338ca91956ff Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 6 Nov 2025 10:45:09 -0500 Subject: [PATCH] Drop threaded serialization/deserialization --- src/js/s14e-serializer.js | 285 -------------------------------------- 1 file changed, 285 deletions(-) diff --git a/src/js/s14e-serializer.js b/src/js/s14e-serializer.js index c74a4f2e3..d57c1ca8c 100644 --- a/src/js/s14e-serializer.js +++ b/src/js/s14e-serializer.js @@ -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; - } - }; -} - /******************************************************************************/