@mieweb/wikigdrive
Version:
Google Drive to MarkDown synchronization
115 lines (114 loc) • 4.05 kB
JavaScript
import { Worker } from 'node:worker_threads';
const __dirname = globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).dirname;
export class WorkerPool {
constructor(workersCount = 4) {
Object.defineProperty(this, "workers", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "threadIdToTaskId", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "backlog", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "promises", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "taskIdCounter", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
this.workers = new Map(Array.from({ length: workersCount })
.map(() => {
const w = new Worker(__dirname + '/worker.ts');
return [w.threadId, w];
}));
// this.idle = Array.from(this.workers.keys());
this.promises = new Map();
this.threadIdToTaskId = new Map(Array.from(this.workers.keys())
.map(threadId => [threadId, 0]));
this.workers.forEach((worker, threadId) => {
worker.on('error', err => {
const taskId = this.threadIdToTaskId.get(threadId);
this.threadIdToTaskId.set(worker.threadId, 0);
this.runNext();
const promise = this.promises.get(taskId);
this.promises.delete(taskId);
if (promise) {
promise[1](err);
}
});
worker.on('exit', err => {
console.error('Worker exit', err);
});
worker.on('messageerror', err => {
const taskId = this.threadIdToTaskId.get(threadId);
this.threadIdToTaskId.set(worker.threadId, 0);
this.runNext();
const promise = this.promises.get(taskId);
this.promises.delete(taskId);
if (promise) {
promise[1](err);
}
});
worker.on('message', message => {
const taskId = this.threadIdToTaskId.get(threadId);
this.threadIdToTaskId.set(threadId, 0);
this.runNext();
const promise = this.promises.get(taskId);
this.promises.delete(taskId);
if (promise) {
const { result, err } = message;
if (err) {
promise[1](err);
}
else {
promise[0](result);
}
}
});
});
}
runNext() {
if (this.backlog.length == 0)
return;
let threadId = 0;
for (const key of this.threadIdToTaskId.keys()) {
const value = this.threadIdToTaskId.get(key);
if (value === 0) {
threadId = key;
break;
}
}
if (threadId === 0) {
return;
}
const task = this.backlog.shift();
const msg = { ...task };
this.threadIdToTaskId.set(threadId, task.taskId);
const worker = this.workers.get(threadId);
worker.postMessage(msg);
this.runNext();
}
schedule(type, payload) {
this.taskIdCounter++;
this.backlog.push({ taskId: this.taskIdCounter, type, payload });
const p = new Promise((resolve, reject) => this.promises.set(this.taskIdCounter, [resolve, reject]));
this.runNext();
return p;
}
}