@@ -355,13 +355,14 @@ module Awaitable = struct
355
355
let update t ~signal ~count =
356
356
try
357
357
let signal = ref signal in
358
+ let count = ref count in
358
359
let backoff = ref Backoff. default in
359
360
while
360
361
not
361
362
(let before = Htbl. find_exn awaiters t in
362
363
match
363
- if ! signal then Awaiters. signal before ~count
364
- else Awaiters. cleanup before ~count
364
+ if ! signal then Awaiters. signal before ~count: ! count
365
+ else Awaiters. cleanup before ~count: ! count
365
366
with
366
367
| Zero -> Htbl. try_compare_and_remove awaiters t before
367
368
| One r ->
@@ -373,58 +374,70 @@ module Awaitable = struct
373
374
before == after
374
375
|| Htbl. try_compare_and_set awaiters t before after)
375
376
do
377
+ (* Even if the hash table update after signal fails, the trigger(s) have
378
+ been signaled. *)
376
379
signal := false ;
380
+ (* If a single awaiter and multi awaiter cleanup are attempted in
381
+ parallel it might be that a multi awaiter cleanup "succeeds" and yet
382
+ some awaiters are left in the queue. For this reason we perform a
383
+ multi awaiter cleanup after failure. It might be possible to improve
384
+ upon this with some more clever approach. *)
385
+ count := Int. max_int;
377
386
backoff := Backoff. once ! backoff
378
387
done
379
388
with Not_found -> ()
380
389
381
- let add_as (type a ) (t : a awaitable ) value =
382
- let trigger = Trigger. create () in
383
- let one : Awaiters.is1 =
384
- One { awaitable = t; value; trigger; counter = 0 ; next = Min0 Zero }
385
- in
386
- let backoff = ref Backoff. default in
387
- while
388
- not
389
- (match Htbl. find_exn awaiters (Packed t) with
390
- | before ->
391
- let many = Awaiters. snoc before one in
392
- Htbl. try_compare_and_set awaiters (Packed t) before (Min1 many)
393
- | exception Not_found -> Htbl. try_add awaiters (Packed t) (Min1 one))
394
- do
395
- backoff := Backoff. once ! backoff
396
- done ;
397
- one
398
-
399
390
module Awaiter = struct
400
391
type t = Awaiters .is1
401
392
402
- let add (type a ) (t : a awaitable ) =
403
- add_as t (Sys. opaque_identity (Obj. magic awaiters : a ))
393
+ let add_as (type a ) (t : a awaitable ) trigger value =
394
+ let one : Awaiters.is1 =
395
+ One { awaitable = t; value; trigger; counter = 0 ; next = Min0 Zero }
396
+ in
397
+ let backoff = ref Backoff. default in
398
+ while
399
+ not
400
+ (match Htbl. find_exn awaiters (Packed t) with
401
+ | before ->
402
+ let many = Awaiters. snoc before one in
403
+ Htbl. try_compare_and_set awaiters (Packed t) before (Min1 many)
404
+ | exception Not_found -> Htbl. try_add awaiters (Packed t) (Min1 one))
405
+ do
406
+ backoff := Backoff. once ! backoff
407
+ done ;
408
+ one
409
+
410
+ let add (type a ) (t : a awaitable ) trigger =
411
+ let unique_value = Sys. opaque_identity (Obj. magic awaiters : a ) in
412
+ add_as t trigger unique_value
404
413
405
414
let remove one =
406
415
Awaiters. signal_and_clear one;
407
416
update (Awaiters. awaitable_of one) ~signal: false ~count: 1
417
+ end
408
418
409
- let await one =
419
+ let await t value =
420
+ let trigger = Trigger. create () in
421
+ let one = Awaiter. add_as t trigger value in
422
+ if Awaiters. is_signalable one then Awaiter. remove one
423
+ else
410
424
match Awaiters. await one with
411
425
| None -> ()
412
426
| Some exn_bt ->
413
427
Awaiters. clear one;
414
428
update (Awaiters. awaitable_of one) ~signal: true ~count: 1 ;
415
429
Printexc. raise_with_backtrace (fst exn_bt) (snd exn_bt)
416
- end
417
-
418
- let await t value =
419
- let one = add_as t value in
420
- if Awaiters. is_signalable one then Awaiter. remove one else Awaiter. await one
421
430
422
431
let [@ inline] broadcast t = update (Packed t) ~signal: true ~count: Int. max_int
423
432
let [@ inline] signal t = update (Packed t) ~signal: true ~count: 1
424
433
425
434
let () =
426
435
Stdlib. at_exit @@ fun () ->
427
436
match Htbl. find_random_exn awaiters with
428
- | _ -> failwith " leaked awaitable"
437
+ | _ ->
438
+ (* This should not normally happen, but might happen due to the program
439
+ being forced to exit without proper cleanup. Otherwise this may
440
+ indicate a bug in the cleanup of awaiters. *)
441
+ Printf. eprintf " Awaitable leaked\n %!"
429
442
| exception Not_found -> ()
430
443
end
0 commit comments