Skip to content

tf-mediapipe getModelTask has no single-flight: two concurrent callers both miss the cache and both build a GPU task #880

Description

@sroussey

Defect

providers/tf-mediapipe/src/ai/common/TFMP_Runtime.ts caches built MediaPipe task instances by model_path + matched options, but the cache check and the insertion are separated by an await, with nothing recording the in-flight build.

TFMP_Runtime.ts:238-246:

const cachedTasks = modelTaskCache.get(model_path);
if (cachedTasks) {
  const matchedTask = cachedTasks.find((cached) => optionsMatch(cached.options, lookupOptions));
  if (matchedTask) {
    return matchedTask.task;
  }
}

const wasmFileset = await getWasmTask(model, emit, signal);   // ← yields

…and the result is only published at :271:

modelTaskCache.get(model_path)!.push(cachedTask);

Two concurrent callers with the same model_path and matching options both reach :238 before either reaches :271. Both miss, both download/instantiate, and one of the two built tasks is orphaned in the cache list — holding a WebGPU/WASM handle nothing will ever dispose.

Evidence

  • providers/tf-mediapipe/src/ai/common/TFMP_Runtime.ts:238-246 — check
  • providers/tf-mediapipe/src/ai/common/TFMP_Runtime.ts:271 — publish
  • No in-flight map: grep -n "InFlight\|inFlight\|pending" providers/tf-mediapipe/src/ai/common/TFMP_Runtime.ts finds nothing on this path.

The package already contains the correct pattern for its sibling runtime — TFMP_GenaiRuntime.ts uses withGenaiLock / isGenaiBusy — and the cactus provider has the shape too (cactusEngineLoadsInFlight, referenced in providers/cactus/src/ai.ts's export comment). This is a copy of an in-tree pattern, not a new design.

Proposed fix

Add a Map<string, Promise<TaskInstance>> keyed the same way the cache is, populated before the first await and cleared in finally; return the shared promise on a hit. ~15 LOC.

Why it matters

Every vision run-fn in the package goes through getModelTask, and a fanned-out graph (MapTask over N images) hits it N times concurrently by construction. On a WebGPU delegate the duplicate build is a second GPU context and a second copy of the model weights, with the loser leaked for the process lifetime.

Carried as a review finding for five cycles (2026-08-03 onward) without being tracked. The 2026-08-31 window touched nine other files in this package (388a2b8, 9ada0ea) and not this one. Filing it. Snapshot: workglow-dev/prdanalysis/grades/2026-08-31/libs-providers.md.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions