mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
build(spf): build26 from 45504a2b
This commit is contained in:
Vendored
+191
@@ -0,0 +1,191 @@
|
||||
import { anyAbortSignal } from "@videojs/utils/events";
|
||||
import { generateId } from "@videojs/utils/string";
|
||||
//#region src/core/tasks/task.ts
|
||||
/**
|
||||
* Generic reusable task that wraps an async run function.
|
||||
*
|
||||
* Owns its own AbortController so it can always be aborted independently.
|
||||
* Optionally composes an external AbortSignal so that a parent's cancellation
|
||||
* propagates into the task's work without requiring the caller to track the
|
||||
* task separately.
|
||||
*
|
||||
* Ordering guarantee: `value` is written before `status` transitions to `'done'`;
|
||||
* `error` is written before `status` transitions to `'error'`. Any reader
|
||||
* observing `status === 'done'` is guaranteed `value` is already present.
|
||||
*/
|
||||
var Task = class {
|
||||
id;
|
||||
#runFn;
|
||||
#abortController = new AbortController();
|
||||
#signal;
|
||||
#status = "pending";
|
||||
#value = void 0;
|
||||
#error = void 0;
|
||||
constructor(runFn, config) {
|
||||
this.#runFn = runFn;
|
||||
const rawId = config?.id;
|
||||
this.id = typeof rawId === "function" ? rawId() : rawId ?? generateId();
|
||||
this.#signal = config?.signal ? anyAbortSignal([this.#abortController.signal, config.signal]) : this.#abortController.signal;
|
||||
}
|
||||
get status() {
|
||||
return this.#status;
|
||||
}
|
||||
get value() {
|
||||
return this.#value;
|
||||
}
|
||||
get error() {
|
||||
return this.#error;
|
||||
}
|
||||
async run() {
|
||||
this.#status = "running";
|
||||
try {
|
||||
const result = await this.#runFn(this.#signal);
|
||||
this.#value = result;
|
||||
this.#status = "done";
|
||||
return result;
|
||||
} catch (e) {
|
||||
this.#error = e;
|
||||
this.#status = "error";
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
abort() {
|
||||
this.#abortController.abort();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Runs tasks concurrently, deduplicated by task id.
|
||||
*
|
||||
* If a task with a given id is already in flight, subsequent schedule() calls
|
||||
* for that id are silently ignored until the first completes. Tasks are stored
|
||||
* so abortAll() can cancel any in-flight work (e.g. on engine cleanup).
|
||||
*/
|
||||
var ConcurrentRunner = class {
|
||||
#pending = /* @__PURE__ */ new Map();
|
||||
#settled = Promise.resolve();
|
||||
#resolveSettled = null;
|
||||
#destroyed = false;
|
||||
schedule(task) {
|
||||
if (this.#destroyed) return Promise.resolve();
|
||||
const existing = this.#pending.get(task.id);
|
||||
if (existing) return existing.promise;
|
||||
if (this.#pending.size === 0) this.#settled = new Promise((resolve) => {
|
||||
this.#resolveSettled = resolve;
|
||||
});
|
||||
const promise = task.run();
|
||||
promise.catch(() => {});
|
||||
const cleanup = () => {
|
||||
this.#pending.delete(task.id);
|
||||
if (this.#pending.size === 0) {
|
||||
this.#resolveSettled?.();
|
||||
this.#resolveSettled = null;
|
||||
}
|
||||
};
|
||||
promise.then(cleanup, cleanup);
|
||||
this.#pending.set(task.id, {
|
||||
task,
|
||||
promise
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
/**
|
||||
* Registers a callback to fire when all currently in-flight tasks settle.
|
||||
* If the runner is already idle, the callback is never called. If abortAll()
|
||||
* is called before the batch settles, the callback is superseded and silently
|
||||
* dropped — no stale callbacks, no generation token required by the caller.
|
||||
*/
|
||||
whenSettled(callback) {
|
||||
if (this.#pending.size === 0) return;
|
||||
const captured = this.#settled;
|
||||
captured.then(() => {
|
||||
if (this.#settled !== captured) return;
|
||||
callback();
|
||||
}, () => {});
|
||||
}
|
||||
abortAll() {
|
||||
for (const { task } of this.#pending.values()) task.abort();
|
||||
this.#pending.clear();
|
||||
this.#resolveSettled?.();
|
||||
this.#resolveSettled = null;
|
||||
this.#settled = Promise.resolve();
|
||||
}
|
||||
destroy() {
|
||||
this.#destroyed = true;
|
||||
this.abortAll();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Runs tasks one at a time in submission order.
|
||||
*
|
||||
* Each schedule() call returns a Promise that resolves or rejects with the
|
||||
* task's result when it is eventually executed. Tasks wait in queue until the
|
||||
* prior task completes.
|
||||
*
|
||||
* Serialization is achieved by chaining each task's run() onto the tail of a
|
||||
* shared promise chain — no explicit queue or drain loop needed.
|
||||
*
|
||||
* abortAll() aborts all pending (not yet started) tasks and the currently
|
||||
* in-flight task. Pending tasks still run briefly but receive an aborted
|
||||
* signal and are expected to exit early.
|
||||
*/
|
||||
var SerialRunner = class {
|
||||
#chain = Promise.resolve();
|
||||
#pending = /* @__PURE__ */ new Set();
|
||||
#current = null;
|
||||
#destroyed = false;
|
||||
schedule(task) {
|
||||
if (this.#destroyed) return Promise.resolve();
|
||||
const t = task;
|
||||
this.#pending.add(t);
|
||||
const result = this.#chain.then(() => {
|
||||
this.#pending.delete(t);
|
||||
this.#current = t;
|
||||
return task.run();
|
||||
}).finally(() => {
|
||||
this.#current = null;
|
||||
});
|
||||
this.#chain = result.then(() => {}, () => {});
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* A promise that resolves when all currently-scheduled tasks have settled.
|
||||
* Use the reference as a generation token: capture it after scheduling a
|
||||
* batch, then check identity in the resolution callback to detect whether
|
||||
* a subsequent abortAll() + new batch has superseded this one.
|
||||
*/
|
||||
get settled() {
|
||||
return this.#chain;
|
||||
}
|
||||
/**
|
||||
* Registers a callback to fire when all currently-pending tasks settle.
|
||||
* If the runner is already idle (no pending or running tasks), the callback
|
||||
* is never called. If new tasks are scheduled before the current batch
|
||||
* settles, the callback is superseded and silently dropped — no stale
|
||||
* callbacks, no generation token required by the caller.
|
||||
*/
|
||||
whenSettled(callback) {
|
||||
if (this.#pending.size === 0 && this.#current === null) return;
|
||||
const currentChain = this.#chain;
|
||||
currentChain.then(() => {
|
||||
if (this.#chain !== currentChain) return;
|
||||
callback();
|
||||
}, () => {});
|
||||
}
|
||||
/** Aborts and clears queued tasks without touching the in-flight task. */
|
||||
abortPending() {
|
||||
for (const task of this.#pending) task.abort();
|
||||
this.#pending.clear();
|
||||
}
|
||||
abortAll() {
|
||||
this.abortPending();
|
||||
this.#current?.abort();
|
||||
}
|
||||
destroy() {
|
||||
this.#destroyed = true;
|
||||
this.abortAll();
|
||||
}
|
||||
};
|
||||
//#endregion
|
||||
export { ConcurrentRunner, SerialRunner, Task };
|
||||
|
||||
//# sourceMappingURL=task.js.map
|
||||
Reference in New Issue
Block a user