first_commit
This commit is contained in:
24
GTA_P_V2/node_modules/tinypool/LICENSE
generated
vendored
Normal file
24
GTA_P_V2/node_modules/tinypool/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 James M Snell and the Piscina contributors
|
||||
|
||||
Piscina contributors listed at https://github.com/jasnell/piscina#the-team and
|
||||
in the README file.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
20
GTA_P_V2/node_modules/tinypool/README.md
generated
vendored
Normal file
20
GTA_P_V2/node_modules/tinypool/README.md
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# Tinypool - the node.js worker pool 🧵
|
||||
|
||||
> Piscina: A fast, efficient Node.js Worker Thread Pool implementation
|
||||
|
||||
Tinypool is a fork of piscina. What we try to achieve in this library, is to eliminate some dependencies and features that our target users don't need (currently, our main user will be Vitest). Tinypool's install size (38KB) can then be smaller than Piscina's install size (6MB when Tinypool was created, Piscina has since reduced it's size to ~800KB). If you need features like [utilization](https://github.com/piscinajs/piscina#property-utilization-readonly) or OS-specific thread priority setting, [Piscina](https://github.com/piscinajs/piscina) is a better choice for you. We think that Piscina is an amazing library, and we may try to upstream some of the dependencies optimization in this fork.
|
||||
|
||||
- ✅ Smaller install size, 38KB
|
||||
- ✅ Minimal
|
||||
- ✅ No dependencies
|
||||
- ✅ Physical cores instead of Logical cores with [physical-cpu-count](https://www.npmjs.com/package/physical-cpu-count)
|
||||
- ✅ Supports `worker_threads` and `child_process`
|
||||
- ❌ No utilization
|
||||
- ❌ No OS-specific thread priority setting
|
||||
|
||||
- Written in TypeScript, and ESM support only. For Node.js 18.x and higher.
|
||||
|
||||
_In case you need more tiny libraries like tinypool or tinyspy, please consider submitting an [RFC](https://github.com/tinylibs/rfcs)_
|
||||
|
||||
## Docs
|
||||
Read **[full docs](https://github.com/tinylibs/tinypool#readme)** on GitHub.
|
||||
28
GTA_P_V2/node_modules/tinypool/dist/common-Qw-RoVFD.js
generated
vendored
Normal file
28
GTA_P_V2/node_modules/tinypool/dist/common-Qw-RoVFD.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
//#region src/common.ts
|
||||
const kMovable = Symbol("Tinypool.kMovable");
|
||||
const kTransferable = Symbol.for("Tinypool.transferable");
|
||||
const kValue = Symbol.for("Tinypool.valueOf");
|
||||
const kQueueOptions = Symbol.for("Tinypool.queueOptions");
|
||||
function isTransferable(value) {
|
||||
return value != null && typeof value === "object" && kTransferable in value && kValue in value;
|
||||
}
|
||||
function isMovable(value) {
|
||||
return isTransferable(value) && value[kMovable] === true;
|
||||
}
|
||||
function markMovable(value) {
|
||||
Object.defineProperty(value, kMovable, {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: true
|
||||
});
|
||||
}
|
||||
function isTaskQueue(value) {
|
||||
return typeof value === "object" && value !== null && "size" in value && typeof value.shift === "function" && typeof value.remove === "function" && typeof value.push === "function";
|
||||
}
|
||||
const kRequestCountField = 0;
|
||||
const kResponseCountField = 1;
|
||||
const kFieldCount = 2;
|
||||
|
||||
//#endregion
|
||||
export { isMovable, isTaskQueue, isTransferable, kFieldCount, kQueueOptions, kRequestCountField, kResponseCountField, kTransferable, kValue, markMovable };
|
||||
1
GTA_P_V2/node_modules/tinypool/dist/entry/process.d.ts
generated
vendored
Normal file
1
GTA_P_V2/node_modules/tinypool/dist/entry/process.d.ts
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export { };
|
||||
72
GTA_P_V2/node_modules/tinypool/dist/entry/process.js
generated
vendored
Normal file
72
GTA_P_V2/node_modules/tinypool/dist/entry/process.js
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
import { stderr, stdout } from "../utils-De75vAgL.js";
|
||||
import { getHandler, throwInNextTick } from "../utils-B--2TaWv.js";
|
||||
|
||||
//#region src/entry/process.ts
|
||||
process.__tinypool_state__ = {
|
||||
isChildProcess: true,
|
||||
isTinypoolWorker: true,
|
||||
workerData: null,
|
||||
workerId: Number(process.env.TINYPOOL_WORKER_ID)
|
||||
};
|
||||
const memoryUsage = process.memoryUsage.bind(process);
|
||||
const send = process.send.bind(process);
|
||||
process.on("message", (message) => {
|
||||
if (!message || !message.__tinypool_worker_message__) return;
|
||||
if (message.source === "pool") {
|
||||
const { filename, name } = message;
|
||||
(async function() {
|
||||
if (filename !== null) await getHandler(filename, name);
|
||||
send({
|
||||
ready: true,
|
||||
source: "pool",
|
||||
__tinypool_worker_message__: true
|
||||
}, () => {});
|
||||
})().catch(throwInNextTick);
|
||||
return;
|
||||
}
|
||||
if (message.source === "port") {
|
||||
onMessage(message).catch(throwInNextTick);
|
||||
return;
|
||||
}
|
||||
throw new Error(`Unexpected TinypoolWorkerMessage ${JSON.stringify(message)}`);
|
||||
});
|
||||
async function onMessage(message) {
|
||||
const { taskId, task, filename, name } = message;
|
||||
let response;
|
||||
try {
|
||||
const handler = await getHandler(filename, name);
|
||||
if (handler === null) throw new Error(`No handler function "${name}" exported from "${filename}"`);
|
||||
const result = await handler(task);
|
||||
response = {
|
||||
source: "port",
|
||||
__tinypool_worker_message__: true,
|
||||
taskId,
|
||||
result,
|
||||
error: null,
|
||||
usedMemory: memoryUsage().heapUsed
|
||||
};
|
||||
if (stdout()?.writableLength > 0) await new Promise((resolve) => process.stdout.write("", resolve));
|
||||
if (stderr()?.writableLength > 0) await new Promise((resolve) => process.stderr.write("", resolve));
|
||||
} catch (error) {
|
||||
response = {
|
||||
source: "port",
|
||||
__tinypool_worker_message__: true,
|
||||
taskId,
|
||||
result: null,
|
||||
error: serializeError(error),
|
||||
usedMemory: memoryUsage().heapUsed
|
||||
};
|
||||
}
|
||||
send(response);
|
||||
}
|
||||
function serializeError(error) {
|
||||
if (error instanceof Error) return {
|
||||
...error,
|
||||
name: error.name,
|
||||
stack: error.stack,
|
||||
message: error.message
|
||||
};
|
||||
return String(error);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
7
GTA_P_V2/node_modules/tinypool/dist/entry/utils.d.ts
generated
vendored
Normal file
7
GTA_P_V2/node_modules/tinypool/dist/entry/utils.d.ts
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
//#region src/entry/utils.d.ts
|
||||
type Handler = Function;
|
||||
declare function getHandler(filename: string, name: string): Promise<Handler | null>;
|
||||
declare function throwInNextTick(error: Error): void;
|
||||
|
||||
//#endregion
|
||||
export { getHandler, throwInNextTick };
|
||||
3
GTA_P_V2/node_modules/tinypool/dist/entry/utils.js
generated
vendored
Normal file
3
GTA_P_V2/node_modules/tinypool/dist/entry/utils.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { getHandler, throwInNextTick } from "../utils-B--2TaWv.js";
|
||||
|
||||
export { getHandler, throwInNextTick };
|
||||
1
GTA_P_V2/node_modules/tinypool/dist/entry/worker.d.ts
generated
vendored
Normal file
1
GTA_P_V2/node_modules/tinypool/dist/entry/worker.d.ts
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export { };
|
||||
76
GTA_P_V2/node_modules/tinypool/dist/entry/worker.js
generated
vendored
Normal file
76
GTA_P_V2/node_modules/tinypool/dist/entry/worker.js
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
import { isMovable, kRequestCountField, kResponseCountField, kTransferable, kValue } from "../common-Qw-RoVFD.js";
|
||||
import { stderr, stdout } from "../utils-De75vAgL.js";
|
||||
import { getHandler, throwInNextTick } from "../utils-B--2TaWv.js";
|
||||
import { parentPort, receiveMessageOnPort, workerData } from "node:worker_threads";
|
||||
|
||||
//#region src/entry/worker.ts
|
||||
const [tinypoolPrivateData, workerData$1] = workerData;
|
||||
process.__tinypool_state__ = {
|
||||
isWorkerThread: true,
|
||||
isTinypoolWorker: true,
|
||||
workerData: workerData$1,
|
||||
workerId: tinypoolPrivateData.workerId
|
||||
};
|
||||
const memoryUsage = process.memoryUsage.bind(process);
|
||||
let useAtomics = process.env.PISCINA_DISABLE_ATOMICS !== "1";
|
||||
parentPort.on("message", (message) => {
|
||||
useAtomics = process.env.PISCINA_DISABLE_ATOMICS === "1" ? false : message.useAtomics;
|
||||
const { port, sharedBuffer, filename, name } = message;
|
||||
(async function() {
|
||||
if (filename !== null) await getHandler(filename, name);
|
||||
const readyMessage = { ready: true };
|
||||
parentPort.postMessage(readyMessage);
|
||||
port.start();
|
||||
port.on("message", onMessage.bind(null, port, sharedBuffer));
|
||||
atomicsWaitLoop(port, sharedBuffer);
|
||||
})().catch(throwInNextTick);
|
||||
});
|
||||
let currentTasks = 0;
|
||||
let lastSeenRequestCount = 0;
|
||||
function atomicsWaitLoop(port, sharedBuffer) {
|
||||
if (!useAtomics) return;
|
||||
while (currentTasks === 0) {
|
||||
Atomics.wait(sharedBuffer, kRequestCountField, lastSeenRequestCount);
|
||||
lastSeenRequestCount = Atomics.load(sharedBuffer, kRequestCountField);
|
||||
let entry;
|
||||
while ((entry = receiveMessageOnPort(port)) !== void 0) onMessage(port, sharedBuffer, entry.message);
|
||||
}
|
||||
}
|
||||
function onMessage(port, sharedBuffer, message) {
|
||||
currentTasks++;
|
||||
const { taskId, task, filename, name } = message;
|
||||
(async function() {
|
||||
let response;
|
||||
let transferList = [];
|
||||
try {
|
||||
const handler = await getHandler(filename, name);
|
||||
if (handler === null) throw new Error(`No handler function "${name}" exported from "${filename}"`);
|
||||
let result = await handler(task);
|
||||
if (isMovable(result)) {
|
||||
transferList = transferList.concat(result[kTransferable]);
|
||||
result = result[kValue];
|
||||
}
|
||||
response = {
|
||||
taskId,
|
||||
result,
|
||||
error: null,
|
||||
usedMemory: memoryUsage().heapUsed
|
||||
};
|
||||
if (stdout()?.writableLength > 0) await new Promise((resolve) => process.stdout.write("", resolve));
|
||||
if (stderr()?.writableLength > 0) await new Promise((resolve) => process.stderr.write("", resolve));
|
||||
} catch (error) {
|
||||
response = {
|
||||
taskId,
|
||||
result: null,
|
||||
error,
|
||||
usedMemory: memoryUsage().heapUsed
|
||||
};
|
||||
}
|
||||
currentTasks--;
|
||||
port.postMessage(response, transferList);
|
||||
Atomics.add(sharedBuffer, kResponseCountField, 1);
|
||||
atomicsWaitLoop(port, sharedBuffer);
|
||||
})().catch(throwInNextTick);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
195
GTA_P_V2/node_modules/tinypool/dist/index.d.ts
generated
vendored
Normal file
195
GTA_P_V2/node_modules/tinypool/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
/// <reference types="node" />
|
||||
import { MessagePort, TransferListItem } from "node:worker_threads";
|
||||
import { EventEmitterAsyncResource } from "node:events";
|
||||
|
||||
//#region src/common.d.ts
|
||||
/** Channel for communicating between main thread and workers */
|
||||
/** Channel for communicating between main thread and workers */
|
||||
interface TinypoolChannel {
|
||||
/** Workers subscribing to messages */
|
||||
onMessage?: (callback: (message: any) => void) => void;
|
||||
/** Called with worker's messages */
|
||||
postMessage?: (message: any) => void;
|
||||
/** Called when channel can be closed */
|
||||
onClose?: () => void;
|
||||
}
|
||||
interface TinypoolWorker {
|
||||
runtime: string;
|
||||
initialize(options: {
|
||||
env?: Record<string, string>;
|
||||
argv?: string[];
|
||||
execArgv?: string[];
|
||||
resourceLimits?: any;
|
||||
workerData: TinypoolData;
|
||||
trackUnmanagedFds?: boolean;
|
||||
}): void;
|
||||
terminate(): Promise<any>;
|
||||
postMessage(message: any, transferListItem?: TransferListItem[]): void;
|
||||
setChannel?: (channel: TinypoolChannel) => void;
|
||||
on(event: string, listener: (...args: any[]) => void): void;
|
||||
once(event: string, listener: (...args: any[]) => void): void;
|
||||
emit(event: string, ...data: any[]): void;
|
||||
ref?: () => void;
|
||||
unref?: () => void;
|
||||
threadId: number;
|
||||
}
|
||||
/**
|
||||
* Tinypool's internal messaging between main thread and workers.
|
||||
* - Utilizers can use `__tinypool_worker_message__` property to identify
|
||||
* these messages and ignore them.
|
||||
*/
|
||||
interface TinypoolWorkerMessage<T extends 'port' | 'pool' = 'port' | 'pool'> {
|
||||
__tinypool_worker_message__: true;
|
||||
source: T;
|
||||
}
|
||||
interface StartupMessage {
|
||||
filename: string | null;
|
||||
name: string;
|
||||
port: MessagePort;
|
||||
sharedBuffer: Int32Array;
|
||||
useAtomics: boolean;
|
||||
}
|
||||
interface RequestMessage {
|
||||
taskId: number;
|
||||
task: any;
|
||||
filename: string;
|
||||
name: string;
|
||||
}
|
||||
interface ReadyMessage {
|
||||
ready: true;
|
||||
}
|
||||
interface ResponseMessage {
|
||||
taskId: number;
|
||||
result: any;
|
||||
error: unknown | null;
|
||||
usedMemory: number;
|
||||
}
|
||||
interface TinypoolPrivateData {
|
||||
workerId: number;
|
||||
}
|
||||
type TinypoolData = [TinypoolPrivateData, any];
|
||||
declare const kTransferable: unique symbol;
|
||||
declare const kValue: unique symbol;
|
||||
declare const kQueueOptions: unique symbol;
|
||||
declare function isTransferable(value: any): boolean;
|
||||
declare function isMovable(value: any): boolean;
|
||||
declare function markMovable(value: object): void;
|
||||
interface Transferable {
|
||||
readonly [kTransferable]: object;
|
||||
readonly [kValue]: object;
|
||||
}
|
||||
interface Task {
|
||||
readonly [kQueueOptions]: object | null;
|
||||
cancel(): void;
|
||||
}
|
||||
interface TaskQueue {
|
||||
readonly size: number;
|
||||
shift(): Task | null;
|
||||
remove(task: Task): void;
|
||||
push(task: Task): void;
|
||||
cancel(): void;
|
||||
}
|
||||
declare function isTaskQueue(value: any): boolean;
|
||||
declare const kRequestCountField = 0;
|
||||
declare const kResponseCountField = 1;
|
||||
declare const kFieldCount = 2; //#endregion
|
||||
//#region src/index.d.ts
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface Process {
|
||||
__tinypool_state__: {
|
||||
isTinypoolWorker: boolean;
|
||||
isWorkerThread?: boolean;
|
||||
isChildProcess?: boolean;
|
||||
workerData: any;
|
||||
workerId: number;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
interface AbortSignalEventTargetAddOptions {
|
||||
once: boolean;
|
||||
}
|
||||
interface AbortSignalEventTarget {
|
||||
addEventListener: (name: 'abort', listener: () => void, options?: AbortSignalEventTargetAddOptions) => void;
|
||||
removeEventListener: (name: 'abort', listener: () => void) => void;
|
||||
aborted?: boolean;
|
||||
}
|
||||
interface AbortSignalEventEmitter {
|
||||
off: (name: 'abort', listener: () => void) => void;
|
||||
once: (name: 'abort', listener: () => void) => void;
|
||||
}
|
||||
type AbortSignalAny = AbortSignalEventTarget | AbortSignalEventEmitter;
|
||||
type ResourceLimits = Worker extends {
|
||||
resourceLimits?: infer T;
|
||||
} ? T : object;
|
||||
interface Options {
|
||||
filename?: string | null;
|
||||
runtime?: 'worker_threads' | 'child_process';
|
||||
name?: string;
|
||||
minThreads?: number;
|
||||
maxThreads?: number;
|
||||
idleTimeout?: number;
|
||||
terminateTimeout?: number;
|
||||
maxQueue?: number | 'auto';
|
||||
concurrentTasksPerWorker?: number;
|
||||
useAtomics?: boolean;
|
||||
resourceLimits?: ResourceLimits;
|
||||
maxMemoryLimitBeforeRecycle?: number;
|
||||
argv?: string[];
|
||||
execArgv?: string[];
|
||||
env?: Record<string, string>;
|
||||
workerData?: any;
|
||||
taskQueue?: TaskQueue;
|
||||
trackUnmanagedFds?: boolean;
|
||||
isolateWorkers?: boolean;
|
||||
teardown?: string;
|
||||
}
|
||||
interface FilledOptions extends Options {
|
||||
filename: string | null;
|
||||
name: string;
|
||||
runtime: NonNullable<Options['runtime']>;
|
||||
minThreads: number;
|
||||
maxThreads: number;
|
||||
idleTimeout: number;
|
||||
maxQueue: number;
|
||||
concurrentTasksPerWorker: number;
|
||||
useAtomics: boolean;
|
||||
taskQueue: TaskQueue;
|
||||
}
|
||||
interface RunOptions {
|
||||
transferList?: TransferList;
|
||||
channel?: TinypoolChannel;
|
||||
filename?: string | null;
|
||||
signal?: AbortSignalAny | null;
|
||||
name?: string | null;
|
||||
runtime?: Options['runtime'];
|
||||
}
|
||||
type TransferList = MessagePort extends {
|
||||
postMessage(value: any, transferList: infer T): any;
|
||||
} ? T : never;
|
||||
type TransferListItem$1 = TransferList extends (infer T)[] ? T : never;
|
||||
declare class Tinypool extends EventEmitterAsyncResource {
|
||||
#private;
|
||||
constructor(options?: Options);
|
||||
run(task: any, options?: RunOptions): Promise<any>;
|
||||
destroy(): Promise<void>;
|
||||
get options(): FilledOptions;
|
||||
get threads(): TinypoolWorker[];
|
||||
get queueSize(): number;
|
||||
cancelPendingTasks(): void;
|
||||
recycleWorkers(options?: Pick<Options, 'runtime'>): Promise<void>;
|
||||
get completed(): number;
|
||||
get duration(): number;
|
||||
static get isWorkerThread(): boolean;
|
||||
static get workerData(): any;
|
||||
static get version(): string;
|
||||
static move(val: Transferable | TransferListItem$1 | ArrayBufferView | ArrayBuffer | MessagePort): MessagePort | ArrayBuffer | Transferable | ArrayBufferView;
|
||||
static get transferableSymbol(): symbol;
|
||||
static get valueSymbol(): symbol;
|
||||
static get queueOptionsSymbol(): symbol;
|
||||
}
|
||||
declare const _workerId: number;
|
||||
|
||||
//#endregion
|
||||
export { Options, ReadyMessage, RequestMessage, ResponseMessage, StartupMessage, Task, TaskQueue, Tinypool, TinypoolChannel, TinypoolData, TinypoolPrivateData, TinypoolWorker, TinypoolWorkerMessage, Transferable, Tinypool as default, isMovable, isTaskQueue, isTransferable, kFieldCount, kQueueOptions, kRequestCountField, kResponseCountField, kTransferable, kValue, markMovable, _workerId as workerId };
|
||||
820
GTA_P_V2/node_modules/tinypool/dist/index.js
generated
vendored
Normal file
820
GTA_P_V2/node_modules/tinypool/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,820 @@
|
||||
import { isMovable, isTaskQueue, isTransferable, kFieldCount, kQueueOptions, kRequestCountField, kResponseCountField, kTransferable, kValue, markMovable } from "./common-Qw-RoVFD.js";
|
||||
import { MessageChannel, MessagePort, Worker, receiveMessageOnPort } from "node:worker_threads";
|
||||
import { EventEmitterAsyncResource, once } from "node:events";
|
||||
import { AsyncResource } from "node:async_hooks";
|
||||
import { URL, fileURLToPath } from "node:url";
|
||||
import { join } from "node:path";
|
||||
import { inspect, types } from "node:util";
|
||||
import assert from "node:assert";
|
||||
import { performance } from "node:perf_hooks";
|
||||
import { readFileSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import childProcess, { fork } from "node:child_process";
|
||||
|
||||
//#region src/physicalCpuCount.ts
|
||||
function exec(command) {
|
||||
const output = childProcess.execSync(command, {
|
||||
encoding: "utf8",
|
||||
stdio: [
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
});
|
||||
return output;
|
||||
}
|
||||
let amount;
|
||||
try {
|
||||
const platform = os.platform();
|
||||
if (platform === "linux") {
|
||||
const output1 = exec("cat /proc/cpuinfo | grep \"physical id\" | sort |uniq | wc -l");
|
||||
const output2 = exec("cat /proc/cpuinfo | grep \"core id\" | sort | uniq | wc -l");
|
||||
const physicalCpuAmount = parseInt(output1.trim(), 10);
|
||||
const physicalCoreAmount = parseInt(output2.trim(), 10);
|
||||
amount = physicalCpuAmount * physicalCoreAmount;
|
||||
} else if (platform === "darwin") {
|
||||
const output = exec("sysctl -n hw.physicalcpu_max");
|
||||
amount = parseInt(output.trim(), 10);
|
||||
} else if (platform === "win32") throw new Error();
|
||||
else {
|
||||
const cores = os.cpus().filter(function(cpu, index) {
|
||||
const hasHyperthreading = cpu.model.includes("Intel");
|
||||
const isOdd = index % 2 === 1;
|
||||
return !hasHyperthreading || isOdd;
|
||||
});
|
||||
amount = cores.length;
|
||||
}
|
||||
} catch {
|
||||
amount = os.cpus().length;
|
||||
}
|
||||
if (amount === 0) amount = os.cpus().length;
|
||||
|
||||
//#endregion
|
||||
//#region src/runtime/thread-worker.ts
|
||||
var ThreadWorker = class {
|
||||
name = "ThreadWorker";
|
||||
runtime = "worker_threads";
|
||||
initialize(options) {
|
||||
this.thread = new Worker(fileURLToPath(import.meta.url + "/../entry/worker.js"), options);
|
||||
this.threadId = this.thread.threadId;
|
||||
}
|
||||
async terminate() {
|
||||
const output = await this.thread.terminate();
|
||||
this.channel?.onClose?.();
|
||||
return output;
|
||||
}
|
||||
postMessage(message, transferListItem) {
|
||||
return this.thread.postMessage(message, transferListItem);
|
||||
}
|
||||
on(event, callback) {
|
||||
return this.thread.on(event, callback);
|
||||
}
|
||||
once(event, callback) {
|
||||
return this.thread.once(event, callback);
|
||||
}
|
||||
emit(event, ...data) {
|
||||
return this.thread.emit(event, ...data);
|
||||
}
|
||||
ref() {
|
||||
return this.thread.ref();
|
||||
}
|
||||
unref() {
|
||||
return this.thread.unref();
|
||||
}
|
||||
setChannel(channel) {
|
||||
if (channel.onMessage) throw new Error("{ runtime: 'worker_threads' } doesn't support channel.onMessage. Use transferListItem for listening to messages instead.");
|
||||
if (channel.postMessage) throw new Error("{ runtime: 'worker_threads' } doesn't support channel.postMessage. Use transferListItem for sending to messages instead.");
|
||||
if (this.channel && this.channel !== channel) this.channel.onClose?.();
|
||||
this.channel = channel;
|
||||
}
|
||||
};
|
||||
|
||||
//#endregion
|
||||
//#region src/runtime/process-worker.ts
|
||||
const __tinypool_worker_message__ = true;
|
||||
const SIGKILL_TIMEOUT = 1e3;
|
||||
var ProcessWorker = class {
|
||||
name = "ProcessWorker";
|
||||
runtime = "child_process";
|
||||
isTerminating = false;
|
||||
initialize(options) {
|
||||
this.process = fork(fileURLToPath(import.meta.url + "/../entry/process.js"), options.argv, {
|
||||
...options,
|
||||
stdio: "pipe",
|
||||
env: {
|
||||
...options.env,
|
||||
TINYPOOL_WORKER_ID: options.workerData[0].workerId.toString()
|
||||
}
|
||||
});
|
||||
process.stdout.setMaxListeners(1 + process.stdout.getMaxListeners());
|
||||
process.stderr.setMaxListeners(1 + process.stderr.getMaxListeners());
|
||||
this.process.stdout?.pipe(process.stdout);
|
||||
this.process.stderr?.pipe(process.stderr);
|
||||
this.threadId = this.process.pid;
|
||||
this.process.on("exit", this.onUnexpectedExit);
|
||||
this.waitForExit = new Promise((r) => this.process.on("exit", r));
|
||||
}
|
||||
onUnexpectedExit = () => {
|
||||
this.process.emit("error", new Error("Worker exited unexpectedly"));
|
||||
};
|
||||
async terminate() {
|
||||
this.isTerminating = true;
|
||||
this.process.off("exit", this.onUnexpectedExit);
|
||||
const sigkillTimeout = setTimeout(() => this.process.kill("SIGKILL"), SIGKILL_TIMEOUT);
|
||||
this.process.kill();
|
||||
await this.waitForExit;
|
||||
this.process.stdout?.unpipe(process.stdout);
|
||||
this.process.stderr?.unpipe(process.stderr);
|
||||
this.port?.close();
|
||||
this.channel?.onClose?.();
|
||||
clearTimeout(sigkillTimeout);
|
||||
}
|
||||
setChannel(channel) {
|
||||
if (this.channel && this.channel !== channel) this.channel.onClose?.();
|
||||
this.channel = channel;
|
||||
this.channel.onMessage?.((message) => {
|
||||
this.send(message);
|
||||
});
|
||||
}
|
||||
send(message) {
|
||||
if (!this.isTerminating) this.process.send(message);
|
||||
}
|
||||
postMessage(message, transferListItem) {
|
||||
transferListItem?.forEach((item) => {
|
||||
if (item instanceof MessagePort) {
|
||||
this.port = item;
|
||||
this.port.start();
|
||||
}
|
||||
});
|
||||
if (this.port) this.port.on("message", (message$1) => this.send({
|
||||
...message$1,
|
||||
source: "port",
|
||||
__tinypool_worker_message__
|
||||
}));
|
||||
return this.send({
|
||||
...message,
|
||||
source: "pool",
|
||||
__tinypool_worker_message__
|
||||
});
|
||||
}
|
||||
on(event, callback) {
|
||||
return this.process.on(event, (data) => {
|
||||
if (event === "error") return callback(data);
|
||||
if (!data || !data.__tinypool_worker_message__) return this.channel?.postMessage?.(data);
|
||||
if (data.source === "pool") callback(data);
|
||||
else if (data.source === "port") this.port.postMessage(data);
|
||||
});
|
||||
}
|
||||
once(event, callback) {
|
||||
return this.process.once(event, callback);
|
||||
}
|
||||
emit(event, ...data) {
|
||||
return this.process.emit(event, ...data);
|
||||
}
|
||||
ref() {
|
||||
return this.process.ref();
|
||||
}
|
||||
unref() {
|
||||
this.port?.unref();
|
||||
this.process.channel?.unref?.();
|
||||
if (hasUnref(this.process.stdout)) this.process.stdout.unref();
|
||||
if (hasUnref(this.process.stderr)) this.process.stderr.unref();
|
||||
return this.process.unref();
|
||||
}
|
||||
};
|
||||
function hasUnref(stream) {
|
||||
return stream != null && "unref" in stream && typeof stream.unref === "function";
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region src/index.ts
|
||||
const cpuCount = amount;
|
||||
function onabort(abortSignal, listener) {
|
||||
if ("addEventListener" in abortSignal) abortSignal.addEventListener("abort", listener, { once: true });
|
||||
else abortSignal.once("abort", listener);
|
||||
}
|
||||
var AbortError = class extends Error {
|
||||
constructor() {
|
||||
super("The task has been aborted");
|
||||
}
|
||||
get name() {
|
||||
return "AbortError";
|
||||
}
|
||||
};
|
||||
var CancelError = class extends Error {
|
||||
constructor() {
|
||||
super("The task has been cancelled");
|
||||
}
|
||||
get name() {
|
||||
return "CancelError";
|
||||
}
|
||||
};
|
||||
var ArrayTaskQueue = class {
|
||||
tasks = [];
|
||||
get size() {
|
||||
return this.tasks.length;
|
||||
}
|
||||
shift() {
|
||||
return this.tasks.shift();
|
||||
}
|
||||
push(task) {
|
||||
this.tasks.push(task);
|
||||
}
|
||||
remove(task) {
|
||||
const index = this.tasks.indexOf(task);
|
||||
assert.notStrictEqual(index, -1);
|
||||
this.tasks.splice(index, 1);
|
||||
}
|
||||
cancel() {
|
||||
while (this.tasks.length > 0) {
|
||||
const task = this.tasks.pop();
|
||||
task?.cancel();
|
||||
}
|
||||
}
|
||||
};
|
||||
const kDefaultOptions = {
|
||||
filename: null,
|
||||
name: "default",
|
||||
runtime: "worker_threads",
|
||||
minThreads: Math.max(cpuCount / 2, 1),
|
||||
maxThreads: cpuCount,
|
||||
idleTimeout: 0,
|
||||
maxQueue: Infinity,
|
||||
concurrentTasksPerWorker: 1,
|
||||
useAtomics: true,
|
||||
taskQueue: new ArrayTaskQueue(),
|
||||
trackUnmanagedFds: true
|
||||
};
|
||||
const kDefaultRunOptions = {
|
||||
transferList: void 0,
|
||||
filename: null,
|
||||
signal: null,
|
||||
name: null
|
||||
};
|
||||
var DirectlyTransferable = class {
|
||||
#value;
|
||||
constructor(value) {
|
||||
this.#value = value;
|
||||
}
|
||||
get [kTransferable]() {
|
||||
return this.#value;
|
||||
}
|
||||
get [kValue]() {
|
||||
return this.#value;
|
||||
}
|
||||
};
|
||||
var ArrayBufferViewTransferable = class {
|
||||
#view;
|
||||
constructor(view) {
|
||||
this.#view = view;
|
||||
}
|
||||
get [kTransferable]() {
|
||||
return this.#view.buffer;
|
||||
}
|
||||
get [kValue]() {
|
||||
return this.#view;
|
||||
}
|
||||
};
|
||||
let taskIdCounter = 0;
|
||||
function maybeFileURLToPath(filename) {
|
||||
return filename.startsWith("file:") ? fileURLToPath(new URL(filename)) : filename;
|
||||
}
|
||||
var TaskInfo = class extends AsyncResource {
|
||||
abortListener = null;
|
||||
workerInfo = null;
|
||||
constructor(task, transferList, filename, name, callback, abortSignal, triggerAsyncId, channel) {
|
||||
super("Tinypool.Task", {
|
||||
requireManualDestroy: true,
|
||||
triggerAsyncId
|
||||
});
|
||||
this.callback = callback;
|
||||
this.task = task;
|
||||
this.transferList = transferList;
|
||||
this.cancel = () => this.callback(new CancelError(), null);
|
||||
this.channel = channel;
|
||||
if (isMovable(task)) {
|
||||
/* istanbul ignore if */
|
||||
if (this.transferList == null) this.transferList = [];
|
||||
this.transferList = this.transferList.concat(task[kTransferable]);
|
||||
this.task = task[kValue];
|
||||
}
|
||||
this.filename = filename;
|
||||
this.name = name;
|
||||
this.taskId = taskIdCounter++;
|
||||
this.abortSignal = abortSignal;
|
||||
this.created = performance.now();
|
||||
this.started = 0;
|
||||
}
|
||||
releaseTask() {
|
||||
const ret = this.task;
|
||||
this.task = null;
|
||||
return ret;
|
||||
}
|
||||
done(err, result) {
|
||||
this.emitDestroy();
|
||||
this.runInAsyncScope(this.callback, null, err, result);
|
||||
if (this.abortSignal && this.abortListener) if ("removeEventListener" in this.abortSignal && this.abortListener) this.abortSignal.removeEventListener("abort", this.abortListener);
|
||||
else this.abortSignal.off("abort", this.abortListener);
|
||||
}
|
||||
get [kQueueOptions]() {
|
||||
return kQueueOptions in this.task ? this.task[kQueueOptions] : null;
|
||||
}
|
||||
};
|
||||
var AsynchronouslyCreatedResource = class {
|
||||
onreadyListeners = [];
|
||||
markAsReady() {
|
||||
const listeners = this.onreadyListeners;
|
||||
assert(listeners !== null);
|
||||
this.onreadyListeners = null;
|
||||
for (const listener of listeners) listener();
|
||||
}
|
||||
isReady() {
|
||||
return this.onreadyListeners === null;
|
||||
}
|
||||
onReady(fn) {
|
||||
if (this.onreadyListeners === null) {
|
||||
fn();
|
||||
return;
|
||||
}
|
||||
this.onreadyListeners.push(fn);
|
||||
}
|
||||
};
|
||||
var AsynchronouslyCreatedResourcePool = class {
|
||||
pendingItems = new Set();
|
||||
readyItems = new Set();
|
||||
constructor(maximumUsage) {
|
||||
this.maximumUsage = maximumUsage;
|
||||
this.onAvailableListeners = [];
|
||||
}
|
||||
add(item) {
|
||||
this.pendingItems.add(item);
|
||||
item.onReady(() => {
|
||||
/* istanbul ignore else */
|
||||
if (this.pendingItems.has(item)) {
|
||||
this.pendingItems.delete(item);
|
||||
this.readyItems.add(item);
|
||||
this.maybeAvailable(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
delete(item) {
|
||||
this.pendingItems.delete(item);
|
||||
this.readyItems.delete(item);
|
||||
}
|
||||
findAvailable() {
|
||||
let minUsage = this.maximumUsage;
|
||||
let candidate = null;
|
||||
for (const item of this.readyItems) {
|
||||
const usage = item.currentUsage();
|
||||
if (usage === 0) return item;
|
||||
if (usage < minUsage) {
|
||||
candidate = item;
|
||||
minUsage = usage;
|
||||
}
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
*[Symbol.iterator]() {
|
||||
yield* this.pendingItems;
|
||||
yield* this.readyItems;
|
||||
}
|
||||
get size() {
|
||||
return this.pendingItems.size + this.readyItems.size;
|
||||
}
|
||||
maybeAvailable(item) {
|
||||
/* istanbul ignore else */
|
||||
if (item.currentUsage() < this.maximumUsage) for (const listener of this.onAvailableListeners) listener(item);
|
||||
}
|
||||
onAvailable(fn) {
|
||||
this.onAvailableListeners.push(fn);
|
||||
}
|
||||
};
|
||||
const Errors = {
|
||||
ThreadTermination: () => new Error("Terminating worker thread"),
|
||||
FilenameNotProvided: () => new Error("filename must be provided to run() or in options object"),
|
||||
TaskQueueAtLimit: () => new Error("Task queue is at limit"),
|
||||
NoTaskQueueAvailable: () => new Error("No task queue available and all Workers are busy")
|
||||
};
|
||||
var WorkerInfo = class extends AsynchronouslyCreatedResource {
|
||||
idleTimeout = null;
|
||||
lastSeenResponseCount = 0;
|
||||
constructor(worker, port, workerId, freeWorkerId, onMessage, filename, teardown) {
|
||||
super();
|
||||
this.worker = worker;
|
||||
this.workerId = workerId;
|
||||
this.freeWorkerId = freeWorkerId;
|
||||
this.teardown = teardown;
|
||||
this.filename = filename;
|
||||
this.port = port;
|
||||
this.port.on("message", (message) => this._handleResponse(message));
|
||||
this.onMessage = onMessage;
|
||||
this.taskInfos = new Map();
|
||||
this.sharedBuffer = new Int32Array(new SharedArrayBuffer(kFieldCount * Int32Array.BYTES_PER_ELEMENT));
|
||||
}
|
||||
async destroy(timeout) {
|
||||
let resolve;
|
||||
let reject;
|
||||
const ret = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
if (this.teardown && this.filename) {
|
||||
const { teardown, filename } = this;
|
||||
await new Promise((resolve$1, reject$1) => {
|
||||
this.postTask(new TaskInfo({}, [], filename, teardown, (error, result) => error ? reject$1(error) : resolve$1(result), null, 1, void 0));
|
||||
});
|
||||
}
|
||||
const timer = timeout ? setTimeout(() => reject(new Error("Failed to terminate worker")), timeout) : null;
|
||||
this.worker.terminate().then(() => {
|
||||
if (timer !== null) clearTimeout(timer);
|
||||
this.port.close();
|
||||
this.clearIdleTimeout();
|
||||
for (const taskInfo of this.taskInfos.values()) taskInfo.done(Errors.ThreadTermination());
|
||||
this.taskInfos.clear();
|
||||
resolve();
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
clearIdleTimeout() {
|
||||
if (this.idleTimeout !== null) {
|
||||
clearTimeout(this.idleTimeout);
|
||||
this.idleTimeout = null;
|
||||
}
|
||||
}
|
||||
ref() {
|
||||
this.port.ref();
|
||||
return this;
|
||||
}
|
||||
unref() {
|
||||
this.port.unref();
|
||||
return this;
|
||||
}
|
||||
_handleResponse(message) {
|
||||
this.usedMemory = message.usedMemory;
|
||||
this.onMessage(message);
|
||||
if (this.taskInfos.size === 0) this.unref();
|
||||
}
|
||||
postTask(taskInfo) {
|
||||
assert(!this.taskInfos.has(taskInfo.taskId));
|
||||
const message = {
|
||||
task: taskInfo.releaseTask(),
|
||||
taskId: taskInfo.taskId,
|
||||
filename: taskInfo.filename,
|
||||
name: taskInfo.name
|
||||
};
|
||||
try {
|
||||
if (taskInfo.channel) this.worker.setChannel?.(taskInfo.channel);
|
||||
this.port.postMessage(message, taskInfo.transferList);
|
||||
} catch (err) {
|
||||
taskInfo.done(err);
|
||||
return;
|
||||
}
|
||||
taskInfo.workerInfo = this;
|
||||
this.taskInfos.set(taskInfo.taskId, taskInfo);
|
||||
this.ref();
|
||||
this.clearIdleTimeout();
|
||||
Atomics.add(this.sharedBuffer, kRequestCountField, 1);
|
||||
Atomics.notify(this.sharedBuffer, kRequestCountField, 1);
|
||||
}
|
||||
processPendingMessages() {
|
||||
const actualResponseCount = Atomics.load(this.sharedBuffer, kResponseCountField);
|
||||
if (actualResponseCount !== this.lastSeenResponseCount) {
|
||||
this.lastSeenResponseCount = actualResponseCount;
|
||||
let entry;
|
||||
while ((entry = receiveMessageOnPort(this.port)) !== void 0) this._handleResponse(entry.message);
|
||||
}
|
||||
}
|
||||
isRunningAbortableTask() {
|
||||
if (this.taskInfos.size !== 1) return false;
|
||||
const [first] = this.taskInfos;
|
||||
const [, task] = first || [];
|
||||
return task?.abortSignal !== null;
|
||||
}
|
||||
currentUsage() {
|
||||
if (this.isRunningAbortableTask()) return Infinity;
|
||||
return this.taskInfos.size;
|
||||
}
|
||||
};
|
||||
var ThreadPool = class {
|
||||
skipQueue = [];
|
||||
completed = 0;
|
||||
start = performance.now();
|
||||
inProcessPendingMessages = false;
|
||||
startingUp = false;
|
||||
workerFailsDuringBootstrap = false;
|
||||
constructor(publicInterface, options) {
|
||||
this.publicInterface = publicInterface;
|
||||
this.taskQueue = options.taskQueue || new ArrayTaskQueue();
|
||||
const filename = options.filename ? maybeFileURLToPath(options.filename) : null;
|
||||
this.options = {
|
||||
...kDefaultOptions,
|
||||
...options,
|
||||
filename,
|
||||
maxQueue: 0
|
||||
};
|
||||
if (options.maxThreads !== void 0 && this.options.minThreads >= options.maxThreads) this.options.minThreads = options.maxThreads;
|
||||
if (options.minThreads !== void 0 && this.options.maxThreads <= options.minThreads) this.options.maxThreads = options.minThreads;
|
||||
if (options.maxQueue === "auto") this.options.maxQueue = this.options.maxThreads ** 2;
|
||||
else this.options.maxQueue = options.maxQueue ?? kDefaultOptions.maxQueue;
|
||||
this.workerIds = new Map(new Array(this.options.maxThreads).fill(0).map((_, i) => [i + 1, true]));
|
||||
this.workers = new AsynchronouslyCreatedResourcePool(this.options.concurrentTasksPerWorker);
|
||||
this.workers.onAvailable((w) => this._onWorkerAvailable(w));
|
||||
this.startingUp = true;
|
||||
this._ensureMinimumWorkers();
|
||||
this.startingUp = false;
|
||||
}
|
||||
_ensureEnoughWorkersForTaskQueue() {
|
||||
while (this.workers.size < this.taskQueue.size && this.workers.size < this.options.maxThreads) this._addNewWorker();
|
||||
}
|
||||
_ensureMaximumWorkers() {
|
||||
while (this.workers.size < this.options.maxThreads) this._addNewWorker();
|
||||
}
|
||||
_ensureMinimumWorkers() {
|
||||
while (this.workers.size < this.options.minThreads) this._addNewWorker();
|
||||
}
|
||||
_addNewWorker() {
|
||||
const workerIds = this.workerIds;
|
||||
let workerId;
|
||||
workerIds.forEach((isIdAvailable, _workerId$1) => {
|
||||
if (isIdAvailable && !workerId) {
|
||||
workerId = _workerId$1;
|
||||
workerIds.set(_workerId$1, false);
|
||||
}
|
||||
});
|
||||
const tinypoolPrivateData = { workerId };
|
||||
const worker = this.options.runtime === "child_process" ? new ProcessWorker() : new ThreadWorker();
|
||||
worker.initialize({
|
||||
env: this.options.env,
|
||||
argv: this.options.argv,
|
||||
execArgv: this.options.execArgv,
|
||||
resourceLimits: this.options.resourceLimits,
|
||||
workerData: [tinypoolPrivateData, this.options.workerData],
|
||||
trackUnmanagedFds: this.options.trackUnmanagedFds
|
||||
});
|
||||
const onMessage = (message$1) => {
|
||||
const { taskId, result } = message$1;
|
||||
const taskInfo = workerInfo.taskInfos.get(taskId);
|
||||
workerInfo.taskInfos.delete(taskId);
|
||||
if (!this.shouldRecycleWorker(taskInfo)) this.workers.maybeAvailable(workerInfo);
|
||||
/* istanbul ignore if */
|
||||
if (taskInfo === void 0) {
|
||||
const err = new Error(`Unexpected message from Worker: ${inspect(message$1)}`);
|
||||
this.publicInterface.emit("error", err);
|
||||
} else taskInfo.done(message$1.error, result);
|
||||
this._processPendingMessages();
|
||||
};
|
||||
const { port1, port2 } = new MessageChannel();
|
||||
const workerInfo = new WorkerInfo(worker, port1, workerId, () => workerIds.set(workerId, true), onMessage, this.options.filename, this.options.teardown);
|
||||
if (this.startingUp) workerInfo.markAsReady();
|
||||
const message = {
|
||||
filename: this.options.filename,
|
||||
name: this.options.name,
|
||||
port: port2,
|
||||
sharedBuffer: workerInfo.sharedBuffer,
|
||||
useAtomics: this.options.useAtomics
|
||||
};
|
||||
worker.postMessage(message, [port2]);
|
||||
worker.on("message", (message$1) => {
|
||||
if (message$1.ready === true) {
|
||||
port1.start();
|
||||
if (workerInfo.currentUsage() === 0) workerInfo.unref();
|
||||
if (!workerInfo.isReady()) workerInfo.markAsReady();
|
||||
return;
|
||||
}
|
||||
worker.emit("error", new Error(`Unexpected message on Worker: ${inspect(message$1)}`));
|
||||
});
|
||||
worker.on("error", (err) => {
|
||||
worker.ref = () => {};
|
||||
const taskInfos = [...workerInfo.taskInfos.values()];
|
||||
workerInfo.taskInfos.clear();
|
||||
this._removeWorker(workerInfo);
|
||||
if (workerInfo.isReady() && !this.workerFailsDuringBootstrap) this._ensureMinimumWorkers();
|
||||
else this.workerFailsDuringBootstrap = true;
|
||||
if (taskInfos.length > 0) for (const taskInfo of taskInfos) taskInfo.done(err, null);
|
||||
else this.publicInterface.emit("error", err);
|
||||
});
|
||||
worker.unref();
|
||||
port1.on("close", () => {
|
||||
worker.ref();
|
||||
});
|
||||
this.workers.add(workerInfo);
|
||||
}
|
||||
_processPendingMessages() {
|
||||
if (this.inProcessPendingMessages || !this.options.useAtomics) return;
|
||||
this.inProcessPendingMessages = true;
|
||||
try {
|
||||
for (const workerInfo of this.workers) workerInfo.processPendingMessages();
|
||||
} finally {
|
||||
this.inProcessPendingMessages = false;
|
||||
}
|
||||
}
|
||||
_removeWorker(workerInfo) {
|
||||
workerInfo.freeWorkerId();
|
||||
this.workers.delete(workerInfo);
|
||||
return workerInfo.destroy(this.options.terminateTimeout);
|
||||
}
|
||||
_onWorkerAvailable(workerInfo) {
|
||||
while ((this.taskQueue.size > 0 || this.skipQueue.length > 0) && workerInfo.currentUsage() < this.options.concurrentTasksPerWorker) {
|
||||
const taskInfo = this.skipQueue.shift() || this.taskQueue.shift();
|
||||
if (taskInfo.abortSignal && workerInfo.taskInfos.size > 0) {
|
||||
this.skipQueue.push(taskInfo);
|
||||
break;
|
||||
}
|
||||
const now = performance.now();
|
||||
taskInfo.started = now;
|
||||
workerInfo.postTask(taskInfo);
|
||||
this._maybeDrain();
|
||||
return;
|
||||
}
|
||||
if (workerInfo.taskInfos.size === 0 && this.workers.size > this.options.minThreads) workerInfo.idleTimeout = setTimeout(() => {
|
||||
assert.strictEqual(workerInfo.taskInfos.size, 0);
|
||||
if (this.workers.size > this.options.minThreads) this._removeWorker(workerInfo);
|
||||
}, this.options.idleTimeout).unref();
|
||||
}
|
||||
runTask(task, options) {
|
||||
let { filename, name } = options;
|
||||
const { transferList = [], signal = null, channel } = options;
|
||||
if (filename == null) filename = this.options.filename;
|
||||
if (name == null) name = this.options.name;
|
||||
if (typeof filename !== "string") return Promise.reject(Errors.FilenameNotProvided());
|
||||
filename = maybeFileURLToPath(filename);
|
||||
let resolve;
|
||||
let reject;
|
||||
const ret = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
const taskInfo = new TaskInfo(task, transferList, filename, name, (err, result) => {
|
||||
this.completed++;
|
||||
if (err !== null) reject(err);
|
||||
if (this.shouldRecycleWorker(taskInfo)) this._removeWorker(taskInfo.workerInfo).then(() => this._ensureMinimumWorkers()).then(() => this._ensureEnoughWorkersForTaskQueue()).then(() => resolve(result)).catch(reject);
|
||||
else resolve(result);
|
||||
}, signal, this.publicInterface.asyncResource.asyncId(), channel);
|
||||
if (signal !== null) {
|
||||
if (signal.aborted) return Promise.reject(new AbortError());
|
||||
taskInfo.abortListener = () => {
|
||||
reject(new AbortError());
|
||||
if (taskInfo.workerInfo !== null) {
|
||||
this._removeWorker(taskInfo.workerInfo);
|
||||
this._ensureMinimumWorkers();
|
||||
} else this.taskQueue.remove(taskInfo);
|
||||
};
|
||||
onabort(signal, taskInfo.abortListener);
|
||||
}
|
||||
if (this.taskQueue.size > 0) {
|
||||
const totalCapacity = this.options.maxQueue + this.pendingCapacity();
|
||||
if (this.taskQueue.size >= totalCapacity) if (this.options.maxQueue === 0) return Promise.reject(Errors.NoTaskQueueAvailable());
|
||||
else return Promise.reject(Errors.TaskQueueAtLimit());
|
||||
else {
|
||||
if (this.workers.size < this.options.maxThreads) this._addNewWorker();
|
||||
this.taskQueue.push(taskInfo);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
let workerInfo = this.workers.findAvailable();
|
||||
if (workerInfo !== null && workerInfo.currentUsage() > 0 && signal) workerInfo = null;
|
||||
let waitingForNewWorker = false;
|
||||
if ((workerInfo === null || workerInfo.currentUsage() > 0) && this.workers.size < this.options.maxThreads) {
|
||||
this._addNewWorker();
|
||||
waitingForNewWorker = true;
|
||||
}
|
||||
if (workerInfo === null) {
|
||||
if (this.options.maxQueue <= 0 && !waitingForNewWorker) return Promise.reject(Errors.NoTaskQueueAvailable());
|
||||
else this.taskQueue.push(taskInfo);
|
||||
return ret;
|
||||
}
|
||||
const now = performance.now();
|
||||
taskInfo.started = now;
|
||||
workerInfo.postTask(taskInfo);
|
||||
this._maybeDrain();
|
||||
return ret;
|
||||
}
|
||||
shouldRecycleWorker(taskInfo) {
|
||||
if (taskInfo?.workerInfo?.shouldRecycle) return true;
|
||||
if (this.options.isolateWorkers && taskInfo?.workerInfo) return true;
|
||||
if (!this.options.isolateWorkers && this.options.maxMemoryLimitBeforeRecycle !== void 0 && (taskInfo?.workerInfo?.usedMemory || 0) > this.options.maxMemoryLimitBeforeRecycle) return true;
|
||||
return false;
|
||||
}
|
||||
pendingCapacity() {
|
||||
return this.workers.pendingItems.size * this.options.concurrentTasksPerWorker;
|
||||
}
|
||||
_maybeDrain() {
|
||||
if (this.taskQueue.size === 0 && this.skipQueue.length === 0) this.publicInterface.emit("drain");
|
||||
}
|
||||
async destroy() {
|
||||
while (this.skipQueue.length > 0) {
|
||||
const taskInfo = this.skipQueue.shift();
|
||||
taskInfo.done(new Error("Terminating worker thread"));
|
||||
}
|
||||
while (this.taskQueue.size > 0) {
|
||||
const taskInfo = this.taskQueue.shift();
|
||||
taskInfo.done(new Error("Terminating worker thread"));
|
||||
}
|
||||
const exitEvents = [];
|
||||
while (this.workers.size > 0) {
|
||||
const [workerInfo] = this.workers;
|
||||
exitEvents.push(once(workerInfo.worker, "exit"));
|
||||
this._removeWorker(workerInfo);
|
||||
}
|
||||
await Promise.all(exitEvents);
|
||||
}
|
||||
async recycleWorkers(options = {}) {
|
||||
const runtimeChanged = options?.runtime && options.runtime !== this.options.runtime;
|
||||
if (options?.runtime) this.options.runtime = options.runtime;
|
||||
if (this.options.isolateWorkers && !runtimeChanged) return;
|
||||
const exitEvents = [];
|
||||
Array.from(this.workers).filter((workerInfo) => {
|
||||
if (workerInfo.currentUsage() === 0) {
|
||||
exitEvents.push(once(workerInfo.worker, "exit"));
|
||||
this._removeWorker(workerInfo);
|
||||
} else workerInfo.shouldRecycle = true;
|
||||
});
|
||||
await Promise.all(exitEvents);
|
||||
this._ensureMinimumWorkers();
|
||||
}
|
||||
};
|
||||
var Tinypool = class extends EventEmitterAsyncResource {
|
||||
#pool;
|
||||
constructor(options = {}) {
|
||||
if (options.minThreads !== void 0 && options.minThreads > 0 && options.minThreads < 1) options.minThreads = Math.max(1, Math.floor(options.minThreads * cpuCount));
|
||||
if (options.maxThreads !== void 0 && options.maxThreads > 0 && options.maxThreads < 1) options.maxThreads = Math.max(1, Math.floor(options.maxThreads * cpuCount));
|
||||
super({
|
||||
...options,
|
||||
name: "Tinypool"
|
||||
});
|
||||
if (options.minThreads !== void 0 && options.maxThreads !== void 0 && options.minThreads > options.maxThreads) throw new RangeError("options.minThreads and options.maxThreads must not conflict");
|
||||
this.#pool = new ThreadPool(this, options);
|
||||
}
|
||||
run(task, options = kDefaultRunOptions) {
|
||||
const { transferList, filename, name, signal, runtime, channel } = options;
|
||||
return this.#pool.runTask(task, {
|
||||
transferList,
|
||||
filename,
|
||||
name,
|
||||
signal,
|
||||
runtime,
|
||||
channel
|
||||
});
|
||||
}
|
||||
async destroy() {
|
||||
await this.#pool.destroy();
|
||||
this.emitDestroy();
|
||||
}
|
||||
get options() {
|
||||
return this.#pool.options;
|
||||
}
|
||||
get threads() {
|
||||
const ret = [];
|
||||
for (const workerInfo of this.#pool.workers) ret.push(workerInfo.worker);
|
||||
return ret;
|
||||
}
|
||||
get queueSize() {
|
||||
const pool = this.#pool;
|
||||
return Math.max(pool.taskQueue.size - pool.pendingCapacity(), 0);
|
||||
}
|
||||
cancelPendingTasks() {
|
||||
const pool = this.#pool;
|
||||
pool.taskQueue.cancel();
|
||||
}
|
||||
async recycleWorkers(options = {}) {
|
||||
await this.#pool.recycleWorkers(options);
|
||||
}
|
||||
get completed() {
|
||||
return this.#pool.completed;
|
||||
}
|
||||
get duration() {
|
||||
return performance.now() - this.#pool.start;
|
||||
}
|
||||
static get isWorkerThread() {
|
||||
return process.__tinypool_state__?.isWorkerThread || false;
|
||||
}
|
||||
static get workerData() {
|
||||
return process.__tinypool_state__?.workerData || void 0;
|
||||
}
|
||||
static get version() {
|
||||
const { version } = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
||||
return version;
|
||||
}
|
||||
static move(val) {
|
||||
if (val != null && typeof val === "object" && typeof val !== "function") {
|
||||
if (!isTransferable(val)) if (types.isArrayBufferView(val)) val = new ArrayBufferViewTransferable(val);
|
||||
else val = new DirectlyTransferable(val);
|
||||
markMovable(val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
static get transferableSymbol() {
|
||||
return kTransferable;
|
||||
}
|
||||
static get valueSymbol() {
|
||||
return kValue;
|
||||
}
|
||||
static get queueOptionsSymbol() {
|
||||
return kQueueOptions;
|
||||
}
|
||||
};
|
||||
const _workerId = process.__tinypool_state__?.workerId;
|
||||
var src_default = Tinypool;
|
||||
|
||||
//#endregion
|
||||
export { Tinypool, src_default as default, isMovable, isTaskQueue, isTransferable, kFieldCount, kQueueOptions, kRequestCountField, kResponseCountField, kTransferable, kValue, markMovable, _workerId as workerId };
|
||||
38
GTA_P_V2/node_modules/tinypool/dist/utils-B--2TaWv.js
generated
vendored
Normal file
38
GTA_P_V2/node_modules/tinypool/dist/utils-B--2TaWv.js
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
//#region src/entry/utils.ts
|
||||
let importESMCached;
|
||||
function getImportESM() {
|
||||
if (importESMCached === void 0) importESMCached = new Function("specifier", "return import(specifier)");
|
||||
return importESMCached;
|
||||
}
|
||||
const handlerCache = new Map();
|
||||
async function getHandler(filename, name) {
|
||||
let handler = handlerCache.get(`${filename}/${name}`);
|
||||
if (handler !== void 0) return handler;
|
||||
try {
|
||||
const handlerModule = await import(filename);
|
||||
handler = typeof handlerModule.default !== "function" && handlerModule.default || handlerModule;
|
||||
if (typeof handler !== "function") handler = await handler[name];
|
||||
} catch {}
|
||||
if (typeof handler !== "function") {
|
||||
handler = await getImportESM()(pathToFileURL(filename).href);
|
||||
if (typeof handler !== "function") handler = await handler[name];
|
||||
}
|
||||
if (typeof handler !== "function") return null;
|
||||
if (handlerCache.size > 1e3) {
|
||||
const [handler$1] = handlerCache;
|
||||
const key = handler$1[0];
|
||||
handlerCache.delete(key);
|
||||
}
|
||||
handlerCache.set(`${filename}/${name}`, handler);
|
||||
return handler;
|
||||
}
|
||||
function throwInNextTick(error) {
|
||||
process.nextTick(() => {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
//#endregion
|
||||
export { getHandler, throwInNextTick };
|
||||
10
GTA_P_V2/node_modules/tinypool/dist/utils-De75vAgL.js
generated
vendored
Normal file
10
GTA_P_V2/node_modules/tinypool/dist/utils-De75vAgL.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
//#region src/utils.ts
|
||||
function stdout() {
|
||||
return console._stdout || process.stdout || void 0;
|
||||
}
|
||||
function stderr() {
|
||||
return console._stderr || process.stderr || void 0;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
export { stderr, stdout };
|
||||
42
GTA_P_V2/node_modules/tinypool/package.json
generated
vendored
Normal file
42
GTA_P_V2/node_modules/tinypool/package.json
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "tinypool",
|
||||
"type": "module",
|
||||
"version": "1.1.1",
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"description": "A minimal and tiny Node.js Worker Thread Pool implementation, a fork of piscina, but with fewer features",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/tinylibs/tinypool#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tinylibs/tinypool.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/tinylibs/tinypool/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"fast",
|
||||
"worker threads",
|
||||
"thread pool"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"vitest>tinypool": "link:./"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user