This commit is contained in:
Bernd Schoolmann 2024-04-25 16:38:01 -06:00 committed by GitHub
commit 5c2a2acaed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 71 additions and 33 deletions

View File

@ -10,10 +10,12 @@ import { EncryptServiceImplementation } from "./encrypt.service.implementation";
import { getClassInitializer } from "./get-class-initializer";
// TTL (time to live) is not strictly required but avoids tying up memory resources if inactive
const workerTTL = 3 * 60000; // 3 minutes
const workerTTL = 60000; // 1 minute
const maxWorkers = 16;
const minNumberOfItemsForMultithreading = 500;
export class MultithreadEncryptServiceImplementation extends EncryptServiceImplementation {
private worker: Worker;
private workers: Worker[] = [];
private timeout: any;
private clear$ = new Subject<void>();
@ -30,46 +32,82 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple
return [];
}
this.logService.info("Starting decryption using multithreading");
this.clearTimeout();
this.worker ??= new Worker(
new URL(
/* webpackChunkName: 'encrypt-worker' */
"@bitwarden/common/platform/services/cryptography/encrypt.worker.ts",
import.meta.url,
),
let numberOfWorkers = Math.min(navigator.hardwareConcurrency, maxWorkers);
if (items.length < minNumberOfItemsForMultithreading) {
numberOfWorkers = 1;
}
this.logService.info(
"Starting decryption using multithreading with " + numberOfWorkers + " workers",
);
if (this.workers.length == 0) {
for (let i = 0; i < numberOfWorkers; i++) {
this.workers.push(
new Worker(
new URL(
/* webpackChunkName: 'encrypt-worker' */
"@bitwarden/common/platform/services/cryptography/encrypt.worker.ts",
import.meta.url,
),
),
);
}
}
const itemsPerWorker = Math.floor(items.length / this.workers.length);
const results = [];
for (const [i, worker] of this.workers.entries()) {
const start = i * itemsPerWorker;
const end = start + itemsPerWorker;
const itemsForWorker = items.slice(start, end);
// push the remaining items to the last worker
if (i == this.workers.length - 1) {
itemsForWorker.push(...items.slice(end));
}
const request = {
id: Utils.newGuid(),
items: itemsForWorker,
key: key,
};
worker.postMessage(JSON.stringify(request));
results.push(
firstValueFrom(
fromEvent(worker, "message").pipe(
filter((response: MessageEvent) => response.data?.id === request.id),
map((response) => JSON.parse(response.data.items)),
map((items) =>
items.map((jsonItem: Jsonify<T>) => {
const initializer = getClassInitializer<T>(jsonItem.initializerKey);
return initializer(jsonItem);
}),
),
takeUntil(this.clear$),
defaultIfEmpty([]),
),
),
);
}
const decryptedItems = (await Promise.all(results)).flat();
this.restartTimeout();
const request = {
id: Utils.newGuid(),
items: items,
key: key,
};
this.worker.postMessage(JSON.stringify(request));
return await firstValueFrom(
fromEvent(this.worker, "message").pipe(
filter((response: MessageEvent) => response.data?.id === request.id),
map((response) => JSON.parse(response.data.items)),
map((items) =>
items.map((jsonItem: Jsonify<T>) => {
const initializer = getClassInitializer<T>(jsonItem.initializerKey);
return initializer(jsonItem);
}),
),
takeUntil(this.clear$),
defaultIfEmpty([]),
),
);
return decryptedItems;
}
private clear() {
this.clear$.next();
this.worker?.terminate();
this.worker = null;
for (const worker of this.workers) {
worker.terminate();
}
this.workers = [];
this.clearTimeout();
}