diff --git a/shared/utility/async-lock.mjs b/shared/utility/async-lock.mjs new file mode 100644 index 0000000..73d7a71 --- /dev/null +++ b/shared/utility/async-lock.mjs @@ -0,0 +1,82 @@ +import { Queue } from './queue.mjs'; + +import { Queue } from './queue.mjs'; + +/** + * Represents an asynchronous lock for managing access to a shared resource. + * @class + */ +export class AsyncLock { + /** + * Creates an instance of AsyncLock. + * @constructor + */ + constructor() { + /** + * A queue to store pending requests for acquiring the lock. + * @type {Queue} + * @private + */ + this.pending_queue = new Queue(); + + /** + * The current state of the lock. + * @type {boolean} + * @private + */ + this.state = false; + + /** + * The ID associated with the current lock holder. + * @type {number} + * @private + */ + this.lock_id = 0; + } + + /** + * Checks the current state of the lock. + * @returns {boolean} True if the lock is acquired; otherwise, false. + */ + query() { + return this.state; + } + + /** + * Acquires the lock asynchronously. + * @returns {Promise} A promise that resolves with the unique ID associated with the acquired lock. + */ + acquire() { + if (!this.state) { + this.state = true; + this.lock_id += 1; + return Promise.resolve(this.lock_id); + } + return new Promise((resolve, _reject) => this.pending_queue.push(resolve)); + } + + /** + * Releases the lock with the specified ID. + * @param {number} lock_id The ID of the lock to release. + */ + release(lock_id) { + if (!this.state || lock_id !== this.lock_id) { return; } + if (this.pending_queue.empty()) { + this.state = false; + return; + } + + let resolve = this.pending_queue.front(); + this.pending_queue.popFront(); + this.lock_id += 1; + resolve(this.lock_id); + } + + /** + * Releases the lock forcefully even you are not the owner. + * BE CAREFUL WHAT YOU WISH FOR!! + */ + forceRelease() { + this.release(this.lock_id); + } +}; \ No newline at end of file diff --git a/shared/utility/index.mjs b/shared/utility/index.mjs index 9f7a1ea..628e0b0 100644 --- a/shared/utility/index.mjs +++ b/shared/utility/index.mjs @@ -1,2 +1,3 @@ export { areArrayBuffersEqual } from './buffer.mjs'; export { Queue, QueueEmptyError } from './queue.mjs'; +export { AsyncLock } from './async-lock.mjs';