diff --git a/packages/aws-cdk/lib/api/util/rwlock.ts b/packages/aws-cdk/lib/api/util/rwlock.ts index 6667bd17afe06..6fb0e4c945dfd 100644 --- a/packages/aws-cdk/lib/api/util/rwlock.ts +++ b/packages/aws-cdk/lib/api/util/rwlock.ts @@ -14,13 +14,12 @@ import * as path from 'path'; export class RWLock { private readonly pidString: string; private readonly writerFile: string; - private readonly readerFile: string; + private readCounter = 0; constructor(public readonly directory: string) { this.pidString = `${process.pid}`; this.writerFile = path.join(this.directory, 'synth.lock'); - this.readerFile = path.join(this.directory, `read.${this.pidString}.lock`); } /** @@ -62,14 +61,26 @@ export class RWLock { return this.doAcquireRead(); } + /** + * Obtains the name fo a (new) `readerFile` to use. This includes a counter so + * that if multiple threads of the same PID attempt to concurrently acquire + * the same lock, they're guaranteed to use a different reader file name (only + * one thread will ever execute JS code at once, guaranteeing the readCounter + * is incremented "atomically" from the point of view of this PID.). + */ + private readerFile(): string { + return path.join(this.directory, `read.${this.pidString}.${++this.readCounter}.lock`); + } + /** * Do the actual acquiring of a read lock. */ private async doAcquireRead(): Promise { - await writeFileAtomic(this.readerFile, this.pidString); + const readerFile = this.readerFile(); + await writeFileAtomic(readerFile, this.pidString); return { release: async () => { - await deleteFile(this.readerFile); + await deleteFile(readerFile); }, }; } @@ -102,7 +113,7 @@ export class RWLock { * Check the current readers (if any) */ private async currentReaders(): Promise { - const re = /^read\.([^.]+)\.lock$/; + const re = /^read\.([^.]+)\.[^.]+\.lock$/; const ret = new Array(); let children; @@ -156,9 +167,10 @@ async function readFileIfExists(filename: string): Promise { } } +let tmpCounter = 0; async function writeFileAtomic(filename: string, contents: string): Promise { await fs.mkdir(path.dirname(filename), { recursive: true }); - const tmpFile = `${filename}.${process.pid}`; + const tmpFile = `${filename}.${process.pid}_${++tmpCounter}`; await fs.writeFile(tmpFile, contents, { encoding: 'utf-8' }); await fs.rename(tmpFile, filename); } @@ -181,4 +193,4 @@ function processExists(pid: number) { } catch (e) { return false; } -} \ No newline at end of file +}