2021-11-23 00:03:59 +03:00
|
|
|
import { Deque } from './deque'
|
|
|
|
|
2021-04-20 21:58:50 +03:00
|
|
|
type LockInfo = [Promise<void>, () => void]
|
|
|
|
|
2021-05-16 23:05:53 +03:00
|
|
|
/**
|
|
|
|
* Simple class implementing a semaphore like
|
|
|
|
* behaviour.
|
|
|
|
*/
|
|
|
|
export class AsyncLock {
|
2021-11-23 00:03:59 +03:00
|
|
|
private _queue = new Deque<LockInfo>()
|
2021-04-18 16:23:25 +03:00
|
|
|
|
|
|
|
async acquire(): Promise<void> {
|
2021-11-23 00:03:59 +03:00
|
|
|
let info
|
2023-06-05 03:30:48 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
while ((info = this._queue.peekFront())) {
|
|
|
|
await info[0]
|
2021-04-20 21:58:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
let unlock: () => void
|
|
|
|
const prom = new Promise<void>((resolve) => {
|
|
|
|
unlock = resolve
|
2021-04-18 16:23:25 +03:00
|
|
|
})
|
2021-04-20 21:58:50 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
this._queue.pushBack([prom, unlock!])
|
2021-04-18 16:23:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
release(): void {
|
2022-06-30 16:32:56 +03:00
|
|
|
if (!this._queue.length) throw new Error('Nothing to release')
|
2021-11-23 00:03:59 +03:00
|
|
|
|
|
|
|
this._queue.popFront()![1]()
|
|
|
|
}
|
|
|
|
|
|
|
|
with(func: () => Promise<void>): Promise<void> {
|
|
|
|
let err: unknown = null
|
2023-06-05 03:30:48 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
return this.acquire()
|
|
|
|
.then(() => func())
|
2023-09-03 02:37:51 +03:00
|
|
|
.catch((e) => void (err = e))
|
2021-11-23 00:03:59 +03:00
|
|
|
.then(() => {
|
|
|
|
this.release()
|
2023-09-03 02:37:51 +03:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
2021-11-23 00:03:59 +03:00
|
|
|
if (err) throw err
|
|
|
|
})
|
2021-04-18 16:23:25 +03:00
|
|
|
}
|
|
|
|
}
|