From 1245805c56b13219bdde57cdea1d64e6827842b7 Mon Sep 17 00:00:00 2001 From: laoquy056 Date: Sun, 24 May 2026 17:32:37 +0700 Subject: [PATCH] fix: prevent release overflow --- src/index.ts | 6 ++++++ test/sema.test.ts | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/index.ts b/src/index.ts index d584c64..82ec2f0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -193,6 +193,12 @@ export class Sema { } release(token?: any): void { + if (this.waiting.length === 0 && this.free.length >= this.nrTokens) { + throw new Error( + 'Sema#release() was called more times than Sema#acquire()' + ); + } + this.releaseEmitter.emit('release', this.noTokens ? '1' : token); } diff --git a/test/sema.test.ts b/test/sema.test.ts index 929a796..10c63ac 100644 --- a/test/sema.test.ts +++ b/test/sema.test.ts @@ -41,6 +41,24 @@ test('tryAcquire returns undefined', async () => { expect(s.tryAcquire()).toBeUndefined(); }); +test('release throws when called without an acquired token', () => { + const s = new Sema(1); + + expect(() => s.release()).toThrow( + 'Sema#release() was called more times than Sema#acquire()' + ); +}); + +test('release does not increase available tokens beyond the limit', async () => { + const s = new Sema(1); + + await s.acquire(); + s.release(); + + expect(s.tryAcquire()).toBeDefined(); + expect(s.tryAcquire()).toBeUndefined(); +}); + test('Pausing works', () => { const pauseFn = jest.fn(); const resumeFn = jest.fn();