import type { InternalWaitConstraintsEntry, InternalWaitConstraintsOptions, } from "./internal/wait-constraints.ts" import { internalAssertTimeoutOption, internalBindWaitConstraints, internalCleanupWaitConstraints, internalThrowIfAborted, } from "./internal/wait-constraints.ts" import type { InternalWaitQueueEntry } from "./internal/wait-queue.ts" import { InternalWaitQueue } from "./internal/wait-queue.ts" import type { ModePermitDetails } from "./permit.ts" import { Permit } from "./permit.ts" /** * @description 表示读写锁内部使用的两种获取模式。 */ type InternalReadWriteLockAcquireMode = "read" | "write" /** * @description 表示读写锁对外暴露的持锁模式。 */ export type ReadWriteLockMode = InternalReadWriteLockAcquireMode /** * @description 表示 `ReadWriteLock` 成功获取后的占用句柄。 */ export type ReadWriteLockPermit = Permit> /** * @description 表示读写锁内部等待队列中的节点。 */ interface InternalReadWriteLockQueueEntry extends InternalWaitQueueEntry, InternalWaitConstraintsEntry { mode: InternalReadWriteLockAcquireMode resolve: (permit: ReadWriteLockPermit) => void reject: (reason: unknown) => void isSettled: boolean } /** * @description 表示调用读锁或写锁获取方法时可选的等待约束。 */ export interface ReadWriteLockAcquireOptions extends InternalWaitConstraintsOptions { /** * @description 单次等待允许持续的最长时间,单位为毫秒。 */ timeout?: number | undefined /** * @description 用于主动中止本次等待的信号。 */ abortSignal?: AbortSignal | undefined } const INTERNAL_READ_WRITE_LOCK_DUPLICATE_RELEASE_MESSAGE = "ReadWriteLock permit release was called more than once." /** * @description 表示读写锁在检测到重复释放时使用的处理函数。 */ export type ReadWriteLockDuplicateReleaseHandler = (message: string) => void /** * @description 表示构造 `ReadWriteLock` 时支持的配置。 */ export interface ReadWriteLockOptions { /** * @description 重复调用同一个 permit 的 `release()` 时触发的可选处理器。 */ onDuplicateRelease?: ReadWriteLockDuplicateReleaseHandler | undefined } /** * @description 表示一个读写锁。 * * 读锁允许多个读取者并发进入,写锁则要求完全独占。 * 当前实现采用 FIFO 等待队列,并保证一旦队头出现写者,后续读者不会越过该写者插队。 */ export class ReadWriteLock { private readonly queue: InternalWaitQueue private internalPendingReaderCount: number private internalPendingWriterCount: number private internalActiveReaderCount: number private internalHasActiveWriter: boolean private readonly onDuplicateRelease: ReadWriteLockDuplicateReleaseHandler | undefined /** * @description 创建一个读写锁实例。 */ constructor(options: ReadWriteLockOptions = {}) { this.queue = new InternalWaitQueue() this.internalPendingReaderCount = 0 this.internalPendingWriterCount = 0 this.internalActiveReaderCount = 0 this.internalHasActiveWriter = false this.onDuplicateRelease = options.onDuplicateRelease } /** * @description 判断当前是否至少存在一个活动中的读者。 */ isReadLocked(): boolean { return this.internalActiveReaderCount !== 0 } /** * @description 判断当前是否存在活动中的写者。 */ isWriteLocked(): boolean { return this.internalHasActiveWriter } /** * @description 判断当前读写锁是否被任意模式占用。 */ isLocked(): boolean { return this.internalHasActiveWriter || this.internalActiveReaderCount !== 0 } /** * @description 读取等待队列中的总任务数。 */ getPendingCount(): number { return this.queue.getCount() } /** * @description 读取等待中的读者数量。 */ getPendingReaderCount(): number { return this.internalPendingReaderCount } /** * @description 读取等待中的写者数量。 */ getPendingWriterCount(): number { return this.internalPendingWriterCount } /** * @description 读取当前已进入临界区的读者数量。 */ getActiveReaderCount(): number { return this.internalActiveReaderCount } /** * @description 把等待节点加入队列,并同步更新按模式拆分的统计计数。 */ private enqueueEntry(entry: InternalReadWriteLockQueueEntry): void { this.queue.enqueue(entry) if (entry.mode === "read") { this.internalPendingReaderCount = this.internalPendingReaderCount + 1 } else { this.internalPendingWriterCount = this.internalPendingWriterCount + 1 } } /** * @description 从等待队列中移除指定节点,并同步回收对应的统计计数。 */ private removeEntry(entry: InternalReadWriteLockQueueEntry): void { if (entry.isQueued === false) { return } this.queue.remove(entry) if (entry.mode === "read") { this.internalPendingReaderCount = this.internalPendingReaderCount - 1 } else { this.internalPendingWriterCount = this.internalPendingWriterCount - 1 } } /** * @description 构造一个读锁 permit。 */ private buildReadPermit(): ReadWriteLockPermit { return new Permit>({ details: { coordination: "read-write-lock", mode: "read" }, duplicateReleaseMessage: INTERNAL_READ_WRITE_LOCK_DUPLICATE_RELEASE_MESSAGE, onDuplicateRelease: this.onDuplicateRelease, onRelease: (): void => { this.internalActiveReaderCount = this.internalActiveReaderCount - 1 this.dispatch() }, }) } /** * @description 构造一个写锁 permit。 */ private buildWritePermit(): ReadWriteLockPermit { return new Permit>({ details: { coordination: "read-write-lock", mode: "write" }, duplicateReleaseMessage: INTERNAL_READ_WRITE_LOCK_DUPLICATE_RELEASE_MESSAGE, onDuplicateRelease: this.onDuplicateRelease, onRelease: (): void => { this.internalHasActiveWriter = false this.dispatch() }, }) } /** * @description 尝试按 FIFO 规则调度队头等待者。 * * 规则如下: * 1. 若已有活动写者,则任何人都不能继续进入。 * 2. 若队头是写者,则必须等到所有活动读者结束后才可授予写锁。 * 3. 若队头是读者,则会批量放行连续的读者,直到遇到写者或队列结束。 */ private dispatch(): void { if (this.internalHasActiveWriter === true) { return } while (true) { const head = this.queue.peek() if (head === undefined) { return } if (head.mode === "write") { if (this.internalActiveReaderCount !== 0) { return } const writerEntry = this.queue.shift() if (writerEntry === undefined) { return } this.internalPendingWriterCount = this.internalPendingWriterCount - 1 if (writerEntry.isSettled === true) { continue } this.internalHasActiveWriter = true writerEntry.isSettled = true internalCleanupWaitConstraints(writerEntry) writerEntry.resolve(this.buildWritePermit()) return } while (true) { const readerHead = this.queue.peek() if (readerHead === undefined || readerHead.mode !== "read") { break } const readerEntry = this.queue.shift() if (readerEntry === undefined) { return } this.internalPendingReaderCount = this.internalPendingReaderCount - 1 if (readerEntry.isSettled === true) { continue } this.internalActiveReaderCount = this.internalActiveReaderCount + 1 readerEntry.isSettled = true internalCleanupWaitConstraints(readerEntry) readerEntry.resolve(this.buildReadPermit()) } if (this.internalActiveReaderCount !== 0) { return } } } /** * @description 以失败状态结束一个等待节点,并尝试继续调度后继等待者。 */ private rejectEntry(entry: InternalReadWriteLockQueueEntry, reason: unknown): void { if (entry.isSettled === true) { return } entry.isSettled = true internalCleanupWaitConstraints(entry) this.removeEntry(entry) entry.reject(reason) this.dispatch() } /** * @description 尝试立即获取读锁;若当前已有写者或队列中存在排队任务,则返回 `undefined`。 */ tryAcquireRead(): ReadWriteLockPermit | undefined { if (this.internalHasActiveWriter === true || this.queue.isEmpty() === false) { return undefined } this.internalActiveReaderCount = this.internalActiveReaderCount + 1 return this.buildReadPermit() } /** * @description 尝试立即获取写锁;只有完全空闲时才会成功。 */ tryAcquireWrite(): ReadWriteLockPermit | undefined { if ( this.internalHasActiveWriter === true || this.internalActiveReaderCount !== 0 || this.queue.isEmpty() === false ) { return undefined } this.internalHasActiveWriter = true return this.buildWritePermit() } /** * @description 等待直到成功获取读锁。 */ async acquireRead(options: ReadWriteLockAcquireOptions = {}): Promise { internalAssertTimeoutOption("ReadWriteLock read acquire", options.timeout) internalThrowIfAborted("ReadWriteLock read acquire", options.abortSignal) const immediatePermit = this.tryAcquireRead() if (immediatePermit !== undefined) { return immediatePermit } const permit = await new Promise((resolve, reject) => { const entry: InternalReadWriteLockQueueEntry = { mode: "read", resolve, reject, isSettled: false, isQueued: false, previous: undefined, next: undefined, timeoutId: undefined, abortSignal: undefined, abortListener: undefined, } internalBindWaitConstraints(entry, options, { abortOperation: "ReadWriteLock read acquire", timeoutOperation: "ReadWriteLock read acquire", onAbort: (error) => { this.rejectEntry(entry, error) }, onTimeout: (error) => { this.rejectEntry(entry, error) }, }) this.enqueueEntry(entry) this.dispatch() }) return permit } /** * @description 等待直到成功获取写锁。 */ async acquireWrite(options: ReadWriteLockAcquireOptions = {}): Promise { internalAssertTimeoutOption("ReadWriteLock write acquire", options.timeout) internalThrowIfAborted("ReadWriteLock write acquire", options.abortSignal) const immediatePermit = this.tryAcquireWrite() if (immediatePermit !== undefined) { return immediatePermit } const permit = await new Promise((resolve, reject) => { const entry: InternalReadWriteLockQueueEntry = { mode: "write", resolve, reject, isSettled: false, isQueued: false, previous: undefined, next: undefined, timeoutId: undefined, abortSignal: undefined, abortListener: undefined, } internalBindWaitConstraints(entry, options, { abortOperation: "ReadWriteLock write acquire", timeoutOperation: "ReadWriteLock write acquire", onAbort: (error) => { this.rejectEntry(entry, error) }, onTimeout: (error) => { this.rejectEntry(entry, error) }, }) this.enqueueEntry(entry) this.dispatch() }) return permit } /** * @description 在读锁保护下执行回调。 */ async runExclusiveRead( callback: () => Promise, options?: ReadWriteLockAcquireOptions, ): Promise async runExclusiveRead(callback: () => T, options?: ReadWriteLockAcquireOptions): Promise async runExclusiveRead( callback: () => Promise | T, options: ReadWriteLockAcquireOptions = {}, ): Promise { const permit = await this.acquireRead(options) try { return await callback() } finally { permit.release() } } /** * @description 在写锁保护下执行回调。 */ async runExclusiveWrite( callback: () => Promise, options?: ReadWriteLockAcquireOptions, ): Promise async runExclusiveWrite(callback: () => T, options?: ReadWriteLockAcquireOptions): Promise async runExclusiveWrite( callback: () => Promise | T, options: ReadWriteLockAcquireOptions = {}, ): Promise { const permit = await this.acquireWrite(options) try { return await callback() } finally { permit.release() } } }