@@ -250,6 +250,47 @@ describe("Semaphore", () => {
250250 expect ( semaphore . queueLength ) . toBe ( 0 ) ;
251251 expect ( semaphore . permitsAvailable ) . toBe ( 3 ) ; // the release in the timeout
252252 } ) ;
253+
254+ it ( "should release other `acquire` or `tryAcquire` when a `tryAcquire` fails" , async ( ) => {
255+ const semaphore = new Semaphore ( 1 ) ;
256+
257+ setTimeout ( ( ) => {
258+ expect ( semaphore . permitsAvailable ) . toBe ( 0 ) ;
259+ expect ( semaphore . permitsRequired ) . toBe ( 5 ) ;
260+ expect ( semaphore . queueLength ) . toBe ( 2 ) ;
261+ semaphore . release ( ) ;
262+ expect ( semaphore . permitsRequired ) . toBe ( 4 ) ;
263+ expect ( semaphore . queueLength ) . toBe ( 2 ) ;
264+ } , delay ) ;
265+
266+ await Promise . all ( [
267+ // second acquire in time
268+ new Promise ( resolve => setTimeout ( resolve , 10 ) ) . then ( ( ) => semaphore . acquire ( 2 ) ) ,
269+
270+ semaphore
271+ . tryAcquire ( delay * 2 , 3 )
272+ . catch ( ( error : unknown ) => {
273+ if ( error instanceof ConcurrencyExceedTimeoutException ) {
274+ return ;
275+ }
276+ throw error ;
277+ } )
278+ . finally ( ( ) => {
279+ // The release in the `setTimeout` reduced the required permits
280+ // of the second acquire to 1, so still 0 available
281+ expect ( semaphore . permitsAvailable ) . toBe ( 0 ) ;
282+ expect ( semaphore . permitsRequired ) . toBe ( 1 ) ;
283+ expect ( semaphore . queueLength ) . toBe ( 1 ) ;
284+
285+ setTimeout ( ( ) => semaphore . release ( 2 ) , delay ) ;
286+ } )
287+ ] ) ;
288+
289+ // The state is reset: 2 permits released for a single successful acquire (+ the initial one)
290+ expect ( semaphore . permitsAvailable ) . toBe ( 1 ) ;
291+ expect ( semaphore . permitsRequired ) . toBe ( 0 ) ;
292+ expect ( semaphore . queueLength ) . toBe ( 0 ) ;
293+ } ) ;
253294 } ) ;
254295
255296 it ( "should release all" , async ( ) => {
0 commit comments