Tyler Hallada
513e95d697
After some testing, I'm fairly confident the issue I was seeing before isn't happening now. After re-processing ~250 plugins a few times with the same workers I was seeing significant slow-downs with large plugins. Also, if I was listening to music in the browser it would start crackling and popping. My hypothesis is that wee_alloc was causing a memory leak in the old version which was causing browsers to freak out unless I recreated the workers on every run. Though I'm not sure if that theory holds since I wasn't seeing out of control memory usage in the dev tools. Perhaps wee_alloc just wasn't good at managing memory after a certain number of allocate/deallocate cycles and was putting a huge strain on the browser. I'm hoping that reusing workers like this will also speed up processing and possibly resolve some crashing that some users were seeing.
75 lines
1.9 KiB
TypeScript
75 lines
1.9 KiB
TypeScript
import { createContext } from "react";
|
|
|
|
import {
|
|
addParsedPluginInOrder,
|
|
decrementPending,
|
|
PluginFile,
|
|
} from "../slices/plugins";
|
|
import store from "./store";
|
|
|
|
export interface Task {
|
|
skipParsing: boolean,
|
|
filename: string,
|
|
lastModified: number,
|
|
contents: Uint8Array,
|
|
}
|
|
|
|
export class WorkerPool {
|
|
public taskQueue: Task[];
|
|
public availableWorkers: Worker[];
|
|
|
|
public constructor() {
|
|
this.taskQueue = [];
|
|
this.availableWorkers = [];
|
|
}
|
|
|
|
public async init(count: number = window.navigator.hardwareConcurrency ?? 8): Promise<WorkerPool> {
|
|
this.availableWorkers = [];
|
|
for (let i = 0; i < count; i++) {
|
|
this.createWorker().then(worker => this.availableWorkers.push(worker));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
public async createWorker(): Promise<Worker> {
|
|
return new Promise((resolve) => {
|
|
const worker = new Worker(new URL("../workers/PluginsLoader.worker.ts", import.meta.url));
|
|
worker.onmessage = (evt: {
|
|
data: string | PluginFile;
|
|
}) => {
|
|
const { data } = evt;
|
|
if (typeof data === "string" && data === "ready") {
|
|
resolve(worker);
|
|
} else if (typeof data !== "string") {
|
|
store.dispatch(decrementPending(1));
|
|
store.dispatch(addParsedPluginInOrder(data));
|
|
|
|
this.availableWorkers.push(worker);
|
|
this.assignWorker()
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
public pushTask(task: Task) {
|
|
this.taskQueue.push(task);
|
|
this.assignWorker();
|
|
}
|
|
|
|
public async assignWorker() {
|
|
if (this.taskQueue.length > 0 && this.availableWorkers.length > 0) {
|
|
const task = this.taskQueue.shift()!;
|
|
const worker = this.availableWorkers.shift()!;
|
|
worker.postMessage(task, [task.contents.buffer]);
|
|
}
|
|
}
|
|
|
|
public async terminateAll() {
|
|
for (const worker of this.availableWorkers) {
|
|
worker.terminate();
|
|
}
|
|
this.availableWorkers = [];
|
|
}
|
|
}
|
|
|
|
export const WorkerPoolContext = createContext<WorkerPool | null>(null); |