DirtyHairy / async-mutex

A mutex for synchronizing async workflows in Javascript
MIT License
1.14k stars 63 forks source link

Cancel doesn't work as expected #67

Closed ivstiv closed 1 year ago

ivstiv commented 1 year ago

It looks like when I cancel a mutex it doesn't throw the E_CANCELLED error and the runExclusive callback finishes. Here is the code to reproduce it:

const mutex = new Mutex();

const longOperation = async () => {
  // wait 5 seconds
  await new Promise(res => setTimeout(res, 5000));
};

const execute = async (id: number) => {
  console.log("Executing", id);
  if(mutex.isLocked()) {
    console.log("Mutex is locked", id);
    mutex.cancel();
    mutex.release();
  }
  await mutex.runExclusive(async () => {
    await longOperation();
  });
  console.log("Execute finished running", id);
};

void execute(1);
void execute(2);

Output:

Executing 1
Executing 2
Mutex is locked 2
// 5 seconds pass...
Execute finished running 1
Execute finished running 2

Expected output:

Executing 1
Executing 2
Mutex is locked 2
thrown E_CANCELLED error by the first execution
// 5 seconds pass...
Execute finished running 2

Note 1: I am calling the release() because of this behaviour:

Note that while all pending locks are cancelled, a currently held lock will not be revoked. In consequence, the mutex may not be available even after cancel() has been called.

DirtyHairy commented 1 year ago

Hi @ivstiv!

What you describe is expected behaviour. Cancelling the mutex will reject all promises that have been acquired by waiters, but will (and cannot) cancel locks that already have been acquired. So, what happens in your example:

The important parts are: