Skip to content

Commit 46395b3

Browse files
committed
Make locks releasable more than once
1 parent d3896d9 commit 46395b3

File tree

2 files changed

+47
-2
lines changed

2 files changed

+47
-2
lines changed

packages/@aws-cdk/tmp-toolkit-helpers/src/api/rwlock.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,14 @@ export class RWLock {
3939

4040
await writeFileAtomic(this.writerFile, this.pidString);
4141

42+
let released = false;
4243
return {
4344
release: async () => {
44-
await deleteFile(this.writerFile);
45+
// Releasing needs a flag, otherwise we might delete a file that some other lock has created in the mean time.
46+
if (!released) {
47+
await deleteFile(this.writerFile);
48+
released = true;
49+
}
4550
},
4651
convertToReaderLock: async () => {
4752
// Acquire the read lock before releasing the write lock. Slightly less
@@ -80,9 +85,15 @@ export class RWLock {
8085
private async doAcquireRead(): Promise<IReadLock> {
8186
const readerFile = this.readerFile();
8287
await writeFileAtomic(readerFile, this.pidString);
88+
89+
let released = false;
8390
return {
8491
release: async () => {
85-
await deleteFile(readerFile);
92+
// Releasing needs a flag, otherwise we might delete a file that some other lock has created in the mean time.
93+
if (!released) {
94+
await deleteFile(readerFile);
95+
released = true;
96+
}
8697
},
8798
};
8899
}
@@ -152,6 +163,9 @@ export class RWLock {
152163
* An acquired lock
153164
*/
154165
export interface IReadLock {
166+
/**
167+
* Release the lock. Can be called more than once.
168+
*/
155169
release(): Promise<void>;
156170
}
157171

packages/aws-cdk/test/api/rwlock.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* eslint-disable import/order */
2+
import { promises as fs } from 'node:fs';
23
import * as os from 'os';
34
import * as path from 'path';
45
import { RWLock } from '../../lib/api/rwlock';
@@ -51,3 +52,33 @@ test('can convert writer to reader lock', async () => {
5152
await r.release();
5253
}
5354
});
55+
56+
test('can release writer lock more than once, second invocation does nothing', async () => {
57+
const unlink = jest.spyOn(fs, 'unlink');
58+
59+
// GIVEN
60+
const lock = new RWLock(testDir());
61+
const r = await lock.acquireWrite();
62+
63+
// WHEN
64+
await r.release();
65+
expect(unlink).toHaveBeenCalledTimes(1);
66+
67+
await r.release();
68+
expect(unlink).toHaveBeenCalledTimes(1);
69+
});
70+
71+
test('can release reader lock more than once, second invocation does nothing', async () => {
72+
const unlink = jest.spyOn(fs, 'unlink');
73+
74+
// GIVEN
75+
const lock = new RWLock(testDir());
76+
const r = await lock.acquireRead();
77+
78+
// WHEN
79+
await r.release();
80+
expect(unlink).toHaveBeenCalledTimes(1);
81+
82+
await r.release();
83+
expect(unlink).toHaveBeenCalledTimes(1);
84+
});

0 commit comments

Comments
 (0)