Skip to content

Instantly share code, notes, and snippets.

@safareli
Last active January 18, 2021 13:23
Show Gist options
  • Select an option

  • Save safareli/7fe0a9f29088709d0169120ab4861ba5 to your computer and use it in GitHub Desktop.

Select an option

Save safareli/7fe0a9f29088709d0169120ab4861ba5 to your computer and use it in GitHub Desktop.
Added comments to mutex implementation from https://spin.atomicobject.com/2018/09/10/javascript-concurrency/
type CleanUp = () => void
export class Mutex<T> {
private mutex:Promise<void> = Promise.resolve();
lock(): Promise<CleanUp> {
let resolver = (unlock: CleanUp): void => {};
const result = new Promise<CleanUp>(resolve => { resolver = resolve });
// whatever is running currently, let's wait untill that's done
this.mutex = this.mutex.then(() => {
// and then we block.
return new Promise<void>(resolve => {
// resolve is passed to `resolver` which will result in the returned promise
// of the `lock` function to resolve with this function, and this will block
// until caller of `lock` will execute that function which will release any
// subsequent lock operations.
resolver(resolve)
});
});
return result
}
async dispatch(fn: (() => T) | (() => Promise<T>)): Promise<T> {
const unlock = await this.lock();
try {
return await Promise.resolve(fn());
} finally {
unlock();
}
}
}
type CleanUp = () => void
export class Mutex<T> {
private mutex:Promise<void> = Promise.resolve();
lock(): Promise<CleanUp> {
let begin = (unlock: CleanUp): void => {};
// whatever is running currently, let's wait untill that's done
this.mutex = this.mutex.then(() => {
// and then we block.
return new Promise<void>(resolve => {
// resolve is passed to begin which will result in the returned promise
// of the lock function to resolve with this resolve function, thus
// releasing any subsequent lock operations.
// NOTE: callbacks passed to then are resolved after current call stack
// is finished executing. (aka it's executed as if passed to setImediate)
// that's why begin is will be set to expected value.
begin(resolve)
});
});
return new Promise<CleanUp>(resolve => {
begin = (unlock: CleanUp): void => {
resolve(unlock)
}
});
}
async dispatch(fn: (() => T) | (() => Promise<T>)): Promise<T> {
const unlock = await this.lock();
try {
return await Promise.resolve(fn());
} finally {
unlock();
}
}
}
type CleanUp = () => void
export class Mutex<T> {
private inUse:boolean = false
private queue:(CleanUp)[] = []
async lock(): Promise<CleanUp> {
if (this.inUse === false) {
this.inUse = true
} else {
await new Promise<void>(resolve => { this.queue.push(resolve) });
}
return Promise.resolve(() => {
const release = this.queue.shift()
if(release){
release();
}
if (this.queue.length == 0) {
this.inUse = false
}
})
}
async dispatch(fn: (() => T) | (() => Promise<T>)): Promise<T> {
const unlock = await this.lock();
try {
return await Promise.resolve(fn());
} finally {
unlock();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment