Name call serializers differently

This commit is contained in:
Vanilagy
2025-12-30 11:44:27 +01:00
parent aafb928460
commit 8467a9d672
5 changed files with 36 additions and 27 deletions
+2 -2
View File
@@ -14,7 +14,7 @@ import {
assert, assert,
AsyncMutex, AsyncMutex,
AsyncMutexLock, AsyncMutexLock,
CallSerializer2, ForgivingCallSerializer,
defer, defer,
isFirefox, isFirefox,
last, last,
@@ -255,7 +255,7 @@ export class PacketCursor<T extends InputTrack = InputTrack> {
private _reader: PacketReader<T>; private _reader: PacketReader<T>;
private _options: PacketRetrievalOptions; private _options: PacketRetrievalOptions;
private _nextIsFirst = true; private _nextIsFirst = true;
private _callSerializer = new CallSerializer2(); private _callSerializer = new ForgivingCallSerializer();
constructor(track: T, options: PacketRetrievalOptions = {}) { constructor(track: T, options: PacketRetrievalOptions = {}) {
if (!(track instanceof InputTrack)) { if (!(track instanceof InputTrack)) {
+3 -3
View File
@@ -20,7 +20,7 @@ import {
} from './codec-data'; } from './codec-data';
import { CustomAudioDecoder, customAudioDecoders, CustomVideoDecoder, customVideoDecoders } from './custom-coder'; import { CustomAudioDecoder, customAudioDecoders, CustomVideoDecoder, customVideoDecoders } from './custom-coder';
import { import {
CallSerializer, NaiveCallSerializer,
Rotation, Rotation,
isChromium, isChromium,
toUint8Array, toUint8Array,
@@ -57,7 +57,7 @@ export class VideoDecoderWrapper extends DecoderWrapper<VideoSample> {
decoder: VideoDecoder | null = null; decoder: VideoDecoder | null = null;
customDecoder: CustomVideoDecoder | null = null; customDecoder: CustomVideoDecoder | null = null;
customDecoderCallSerializer = new CallSerializer(); customDecoderCallSerializer = new NaiveCallSerializer();
customDecoderQueueSize = 0; customDecoderQueueSize = 0;
customDecoderClosed = false; customDecoderClosed = false;
@@ -645,7 +645,7 @@ export class AudioDecoderWrapper extends DecoderWrapper<AudioSample> {
decoder: AudioDecoder | null = null; decoder: AudioDecoder | null = null;
customDecoder: CustomAudioDecoder | null = null; customDecoder: CustomAudioDecoder | null = null;
customDecoderCallSerializer = new CallSerializer(); customDecoderCallSerializer = new NaiveCallSerializer();
customDecoderQueueSize = 0; customDecoderQueueSize = 0;
customDecoderClosed = false; customDecoderClosed = false;
+3 -3
View File
@@ -23,7 +23,7 @@ import { OutputAudioTrack, OutputSubtitleTrack, OutputTrack, OutputVideoTrack }
import { import {
assert, assert,
assertNever, assertNever,
CallSerializer, NaiveCallSerializer,
clamp, clamp,
isFirefox, isFirefox,
last, last,
@@ -214,7 +214,7 @@ class VideoEncoderWrapper {
private resizeCanvas: HTMLCanvasElement | OffscreenCanvas | null = null; private resizeCanvas: HTMLCanvasElement | OffscreenCanvas | null = null;
private customEncoder: CustomVideoEncoder | null = null; private customEncoder: CustomVideoEncoder | null = null;
private customEncoderCallSerializer = new CallSerializer(); private customEncoderCallSerializer = new NaiveCallSerializer();
private customEncoderQueueSize = 0; private customEncoderQueueSize = 0;
// Alpha stuff // Alpha stuff
@@ -1342,7 +1342,7 @@ class AudioEncoderWrapper {
private writeOutputValue: ((view: DataView, byteOffset: number, value: number) => void) | null = null; private writeOutputValue: ((view: DataView, byteOffset: number, value: number) => void) | null = null;
private customEncoder: CustomAudioEncoder | null = null; private customEncoder: CustomAudioEncoder | null = null;
private customEncoderCallSerializer = new CallSerializer(); private customEncoderCallSerializer = new NaiveCallSerializer();
private customEncoderQueueSize = 0; private customEncoderQueueSize = 0;
private lastEndSampleIndex: number | null = null; private lastEndSampleIndex: number | null = null;
+24 -15
View File
@@ -675,20 +675,6 @@ export const computeRationalApproximation = (x: number, maxDenominator: number)
}; };
}; };
export class CallSerializer {
currentPromise = Promise.resolve();
errored = false;
call(fn: () => Promise<void> | void) {
return this.currentPromise = this.currentPromise
.then(fn)
.catch((error) => {
this.errored = true;
throw error;
});
}
}
let isWebKitCache: boolean | null = null; let isWebKitCache: boolean | null = null;
export const isWebKit = () => { export const isWebKit = () => {
if (isWebKitCache !== null) { if (isWebKitCache !== null) {
@@ -938,7 +924,30 @@ export class AsyncMutexLock implements Disposable {
} }
} }
export class CallSerializer2 { /**
* A simple call serializer that works by chaining promises. When one callback throws, the serializer becomes bricked,
* meaning all future calls will also throw.
*/
export class NaiveCallSerializer {
currentPromise = Promise.resolve();
errored = false;
call(fn: () => Promise<void> | void) {
return this.currentPromise = this.currentPromise
.then(fn)
.catch((error) => {
this.errored = true;
throw error;
});
}
}
/**
* A more complex call serializer implementation that works with optionally asynchronous functions. It is forgiving
* in the sense that when a call throws, the error is surfaced but subsequent calls will go through again. So, it
* recovers.
*/
export class ForgivingCallSerializer {
private currentPromise: Promise<unknown> | null = null; private currentPromise: Promise<unknown> | null = null;
private queuedCalls = 0; private queuedCalls = 0;
@@ -1,5 +1,5 @@
import { expect, test } from 'vitest'; import { expect, test } from 'vitest';
import { CallSerializer2 } from '../../src/misc.js'; import { ForgivingCallSerializer } from '../../src/misc.js';
const executeDelayed = async <T>(fn: () => T) => { const executeDelayed = async <T>(fn: () => T) => {
await new Promise(resolve => setTimeout(resolve, 10)); await new Promise(resolve => setTimeout(resolve, 10));
@@ -8,7 +8,7 @@ const executeDelayed = async <T>(fn: () => T) => {
test('Call serialization and return values', async () => { test('Call serialization and return values', async () => {
const numbers: number[] = []; const numbers: number[] = [];
const serializer = new CallSerializer2(); const serializer = new ForgivingCallSerializer();
const first = serializer.call(() => numbers.push(1)); const first = serializer.call(() => numbers.push(1));
const second = serializer.call(() => executeDelayed(() => numbers.push(2))); const second = serializer.call(() => executeDelayed(() => numbers.push(2)));
@@ -25,7 +25,7 @@ test('Call serialization and return values', async () => {
}); });
test('Synchronous return value', async () => { test('Synchronous return value', async () => {
const serializer = new CallSerializer2(); const serializer = new ForgivingCallSerializer();
const first = serializer.call(() => {}); const first = serializer.call(() => {});
expect(first).not.toBeInstanceOf(Promise); expect(first).not.toBeInstanceOf(Promise);
@@ -42,7 +42,7 @@ test('Synchronous return value', async () => {
}); });
test('Error handling', async () => { test('Error handling', async () => {
const serializer = new CallSerializer2(); const serializer = new ForgivingCallSerializer();
expect(() => serializer.call(() => { expect(() => serializer.call(() => {
throw new Error('yo'); throw new Error('yo');