fix(studio): a timeline jump no longer starts thumbnail loads it is about to cancel (#5085)

* fix(studio): a timeline jump no longer starts thumbnail loads it is about to cancel

* fix(studio): pump inline after the release batch, and pin the synchronous slot free
This commit is contained in:
Miguel Ángel
2026-10-06 18:04:50 +00:00
committed by GitHub
parent 0ca28db4f8
commit b9e511a768
2 changed files with 21 additions and 2 deletions
@@ -219,6 +219,26 @@ describe("ThumbnailScheduler", () => {
expect(scheduler.getDiagnostics()).toMatchObject({ queued: 0, active: 0, leases: 0 });
});
it("starts nothing that a release later in the same batch drops", async () => {
const scheduler = new ThumbnailScheduler(
resolveTimelineViewportBudgets({ concurrentVideoDecodes: 1 }),
);
const video = (key: string, load: ThumbnailRequest["load"]) =>
scheduler.acquire(request(key, load, "visible", { kind: "video" }), vi.fn());
const activeLease = video("active", () => deferred<ThumbnailLoadedResult>().promise);
const droppedLoad = vi.fn(async () => result("dropped"));
const droppedLease = video("dropped", droppedLoad);
const keptLoad = vi.fn(async () => result("kept"));
video("kept", keptLoad);
// A timeline jump unmounts its clips in one commit, the loading one first.
activeLease.release();
droppedLease.release();
await flush();
expect(droppedLoad).not.toHaveBeenCalled();
expect(keptLoad).toHaveBeenCalledTimes(1);
});
it("frees an aborted job's slot at once, even when its loader ignores the abort", async () => {
const scheduler = new ThumbnailScheduler(
resolveTimelineViewportBudgets({ concurrentVideoDecodes: 1 }),
@@ -231,7 +251,6 @@ describe("ThumbnailScheduler", () => {
lease.release();
const nextLoad = vi.fn(async () => result("next"));
scheduler.acquire(request("next", nextLoad, "visible", { kind: "video" }), vi.fn());
await flush();
expect(nextLoad).toHaveBeenCalledTimes(1);
});
@@ -316,7 +316,7 @@ export class ThumbnailScheduler {
slotFree = true;
this.activeByBucket[bucket]--;
this.activeByKind[entry.request.kind]--;
this.pump();
queueMicrotask(() => this.pump());
};
controller.signal.addEventListener("abort", freeSlot, { once: true });
this.notify(entry);