Files
workinf_Blender_Wasm/web/app/src/engine-client/engine-variant-bootstrap.ts
mes123456 7c16b279ae
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Advance M7 workflows and release operations
2026-08-15 17:43:53 -04:00

196 lines
6.6 KiB
TypeScript

import {
bindWebEngineRelease,
selectWebEngineVariant,
type WasmThreadingCapabilities,
type WebEngineReleaseBinding,
type WebEngineVariantPolicy,
} from "../../../protocol/engine-variant";
import type { CapabilityGateResult } from "../../../protocol/capability-gates";
import type { WebEngineManifestV2, WebEngineVariantV2 } from "../../../protocol/manifest";
export interface EngineVariantResourceState {
handles: number;
workers: number;
timers: number;
pendingRequests: number;
}
export interface EngineVariantSession<Project = ArrayBuffer> {
readonly variant: WebEngineVariantV2;
openProject(project: Project): Promise<void>;
dispose(): Promise<EngineVariantResourceState>;
resourceState(): EngineVariantResourceState;
testOnlySeedTrackedResources?(): void;
}
export interface EngineVariantFallbackReason {
code: "PTHREAD_INITIALIZATION_FAILED";
message: string;
}
export interface EngineVariantBootstrapResult {
status: "READY";
selected: "single" | "pthread";
attempted: Array<"single" | "pthread">;
fallbackReason: EngineVariantFallbackReason | null;
pthreadGate: CapabilityGateResult;
openCount: number;
failedAttemptCleanup: EngineVariantResourceState | null;
}
export interface EngineVariantBootstrapOutcome<Project = ArrayBuffer> {
result: EngineVariantBootstrapResult;
session: EngineVariantSession<Project>;
}
export type WebEngineRefreshRequired = Extract<WebEngineReleaseBinding, { status: "REFRESH_REQUIRED" }>;
export type WebEngineReleaseBootstrapOutcome<Project = ArrayBuffer> =
| EngineVariantBootstrapOutcome<Project>
| { result: WebEngineRefreshRequired; session: null };
export interface EngineVariantBootstrapDependencies<Project = ArrayBuffer> {
initialize(variant: WebEngineVariantV2): Promise<EngineVariantSession<Project>>;
}
export interface EngineVariantBootstrapTestHooks {
failPthreadAfterInitialize?: boolean;
seedTrackedPthreadResources?: boolean;
}
export class EngineVariantLoadError extends Error {
readonly code: "ENGINE_VARIANT_RESOURCE_REQUEST_FAILED" | "ENGINE_VARIANT_RESOURCE_HASH_MISMATCH";
constructor(code: EngineVariantLoadError["code"], message: string, options?: ErrorOptions) {
super(message, options);
this.name = "EngineVariantLoadError";
this.code = code;
}
}
export class EngineVariantBootstrapError extends Error {
readonly code: "ENGINE_VARIANT_INTEGRITY_FAILED";
readonly attempted: Array<"single" | "pthread">;
constructor(attempted: Array<"single" | "pthread">, cause: EngineVariantLoadError) {
super(`${cause.code}: ${cause.message}`, { cause });
this.name = "EngineVariantBootstrapError";
this.code = "ENGINE_VARIANT_INTEGRITY_FAILED";
this.attempted = [...attempted];
}
}
function failureMessage(error: unknown): string {
return error instanceof Error && error.message ? error.message : "pthread engine initialization failed";
}
function rejectIntegrityFailure(
error: unknown,
attempted: Array<"single" | "pthread">,
): void {
if (error instanceof EngineVariantLoadError && error.code === "ENGINE_VARIANT_RESOURCE_HASH_MISMATCH") {
throw new EngineVariantBootstrapError(attempted, error);
}
}
async function bootstrapSelectedWebEngineVariant<Project = ArrayBuffer>(
manifest: WebEngineManifestV2,
policy: WebEngineVariantPolicy,
capabilities: WasmThreadingCapabilities,
dependencies: EngineVariantBootstrapDependencies<Project>,
pendingProject?: Project,
testHooks?: EngineVariantBootstrapTestHooks,
): Promise<EngineVariantBootstrapOutcome<Project>> {
const selection = selectWebEngineVariant(manifest, policy, capabilities);
if (!selection.selectedVariant) throw new Error("PTHREAD_REQUIRED_CAPABILITY_BLOCKED");
const attempted: Array<"single" | "pthread"> = [];
let failedAttemptCleanup: EngineVariantResourceState | null = null;
let fallbackReason: EngineVariantFallbackReason | null = null;
let session: EngineVariantSession<Project> | null = null;
const initialize = async (variant: WebEngineVariantV2): Promise<EngineVariantSession<Project>> => {
attempted.push(variant.id);
return dependencies.initialize(variant);
};
try {
session = await initialize(selection.selectedVariant);
if (selection.selectedVariant.id === "pthread") {
if (testHooks?.seedTrackedPthreadResources) session.testOnlySeedTrackedResources?.();
if (testHooks?.failPthreadAfterInitialize) throw new Error("TEST_PTHREAD_INITIALIZATION_FAILURE");
}
}
catch (error) {
const failedSession = session;
if (failedSession) failedAttemptCleanup = await failedSession.dispose();
rejectIntegrityFailure(error, attempted);
if (policy !== "AUTO" || selection.selectedVariant.id !== "pthread") throw error;
fallbackReason = { code: "PTHREAD_INITIALIZATION_FAILED", message: failureMessage(error) };
const single = manifest.variants.find((variant) => variant.id === "single");
if (!single) throw new Error("ENGINE_MANIFEST_VARIANTS_MISSING", { cause: error });
try {
session = await initialize(single);
}
catch (fallbackError) {
rejectIntegrityFailure(fallbackError, attempted);
throw fallbackError;
}
}
if (!session) throw new Error("ENGINE_VARIANT_INITIALIZATION_FAILED");
let openCount = 0;
if (pendingProject !== undefined) {
try {
await session.openProject(pendingProject);
openCount = 1;
}
catch (error) {
await session.dispose();
throw error;
}
}
return {
result: {
status: "READY",
selected: session.variant.id,
attempted,
fallbackReason,
pthreadGate: selection.pthreadGate,
openCount,
failedAttemptCleanup,
},
session,
};
}
export async function bootstrapWebEngineRelease<Project = ArrayBuffer>(
expectedReleaseId: string,
manifest: WebEngineManifestV2,
policy: WebEngineVariantPolicy,
capabilities: WasmThreadingCapabilities,
dependencies: EngineVariantBootstrapDependencies<Project>,
pendingProject?: Project,
testHooks?: EngineVariantBootstrapTestHooks,
): Promise<WebEngineReleaseBootstrapOutcome<Project>> {
const binding = bindWebEngineRelease(expectedReleaseId, manifest);
if (binding.status === "REFRESH_REQUIRED") {
return { result: binding, session: null };
}
return bootstrapSelectedWebEngineVariant(
binding.manifest,
policy,
capabilities,
dependencies,
pendingProject,
testHooks,
);
}
export function engineVariantStatusLabel(result: EngineVariantBootstrapResult): string {
if (result.selected === "pthread") return "Runtime: pthread";
return result.fallbackReason
? `Runtime: single (pthread fallback: ${result.fallbackReason.code})`
: "Runtime: single";
}