Skip to content

Commit 6e36477

Browse files
author
Mendes Hugo
committed
feat(mutex): add the mutex implementation
1 parent eb4fe52 commit 6e36477

File tree

3 files changed

+45
-9
lines changed

3 files changed

+45
-9
lines changed

src/mutex/mutex.spec.ts

+15
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ describe("Mutex", () => {
3030
expect(mutex.isLocked).toBeFalse();
3131
expect(mutex.queueLength).toBe(0);
3232

33+
// Lock a first time
34+
await mutex.lock();
35+
3336
const [elapsed] = await timeFunction(async () => {
3437
setTimeout(() => {
3538
expect(mutex.isLocked).toBeTrue();
@@ -50,6 +53,9 @@ describe("Mutex", () => {
5053
it("should work with many lock", async () => {
5154
const mutex = new Mutex();
5255

56+
// Lock a first time
57+
await mutex.lock();
58+
5359
const min = delay / 2;
5460
const med = delay;
5561
const max = delay * 2;
@@ -113,6 +119,9 @@ describe("Mutex", () => {
113119
it("should work with tryLock/unlock", async () => {
114120
const mutex = new Mutex();
115121

122+
// Lock a first time
123+
await mutex.lock();
124+
116125
const [elapsed] = await timeFunction(async () => {
117126
setTimeout(() => mutex.unlock(), delay);
118127
await mutex.tryLock(delay * 5);
@@ -125,6 +134,9 @@ describe("Mutex", () => {
125134
it("should thrown an error when the time exceeds", async () => {
126135
const mutex = new Mutex();
127136

137+
// Lock a first time
138+
await mutex.lock();
139+
128140
setTimeout(() => {
129141
expect(mutex.queueLength).toBe(1);
130142
expect(mutex.isLocked).toBeTrue();
@@ -145,6 +157,9 @@ describe("Mutex", () => {
145157
const mutex = new Mutex();
146158
const reason = "test";
147159

160+
// Lock a first time
161+
await mutex.lock();
162+
148163
setTimeout(() => {
149164
expect(mutex.isLocked).toBeTrue();
150165
expect(mutex.queueLength).toBe(3);

src/mutex/mutex.ts

+27-9
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,70 @@
1+
import { Semaphore } from "../semaphore";
12
import { Synchronizer } from "../synchronizer.interface";
23

4+
/**
5+
* A mutex to manage "concurrency in Javascript" (as mentioned in the mozilla documentation).
6+
*
7+
* Implementation's note: the mutex simply use an internal semaphore.
8+
*
9+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#promise_concurrency
10+
*/
311
export class Mutex implements Synchronizer {
12+
/**
13+
* The internal semaphore for mutex implementation
14+
*/
15+
private readonly semaphore = new Semaphore(1);
16+
417
/**
518
* @returns if the current mutex is currently locked
619
*/
720
public get isLocked(): boolean {
8-
throw new Error("Not implemented yet");
21+
return this.queueLength !== 0;
922
}
1023

1124
/**
1225
* @inheritDoc
1326
*/
1427
public get queueLength(): number {
15-
throw new Error("Not implemented yet");
28+
return this.semaphore.queueLength;
1629
}
1730

1831
/**
1932
* Locks this mutex
2033
*
34+
* @throws {ConcurrencyInterruptedException} when the mutex is interrupted
2135
* @returns a promise when the lock has been set
2236
*/
2337
public lock(): Promise<void> {
24-
throw new Error("Not implemented yet");
38+
return this.semaphore.acquire(1);
2539
}
2640

2741
/**
28-
* Locks this mutex within a time limit.
42+
* Locks this mutex within a time limit
2943
*
30-
* Throws an error if the given time exceeds.
44+
* Throws an error if the given time exceeds
3145
*
3246
* @param timeout maximum time (in ms) to lock
47+
* @throws {ConcurrencyExceedTimeoutException} when the time limit exceeds
48+
* @throws {ConcurrencyInterruptedException} when the mutex is interrupted
3349
* @returns a promise when the lock has been set
3450
*/
3551
public tryLock(timeout: number): Promise<void> {
36-
throw new Error("Not implemented yet");
52+
return this.semaphore.tryAcquire(timeout, 1);
3753
}
3854

3955
/**
4056
* Unlocks this mutex
4157
*/
4258
public unlock() {
43-
throw new Error("Not implemented yet");
59+
this.semaphore.release();
4460
}
4561

4662
/**
47-
* @inheritDoc
63+
* Interrupts all awaiting "Threads" with an [exception]{@link ConcurrencyInterruptedException}.
64+
*
65+
* @param reason The reason why this mutex is being interrupted
4866
*/
4967
public interrupt(reason: unknown) {
50-
throw new Error("Not implemented yet");
68+
this.semaphore.interrupt(reason, 1);
5169
}
5270
}

src/synchronizer.interface.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/**
2+
* A synchronizer is an object or class that helps managing concurrency
3+
*/
14
export interface Synchronizer {
25
/**
36
* Interrupts all awaiting "Threads" with an [exception]{@link ConcurrencyInterruptedException}.

0 commit comments

Comments
 (0)