@@ -355,13 +355,14 @@ module Awaitable = struct
355355 let update t ~signal ~count =
356356 try
357357 let signal = ref signal in
358+ let count = ref count in
358359 let backoff = ref Backoff. default in
359360 while
360361 not
361362 (let before = Htbl. find_exn awaiters t in
362363 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
365366 with
366367 | Zero -> Htbl. try_compare_and_remove awaiters t before
367368 | One r ->
@@ -373,58 +374,70 @@ module Awaitable = struct
373374 before == after
374375 || Htbl. try_compare_and_set awaiters t before after)
375376 do
377+ (* Even if the hash table update after signal fails, the trigger(s) have
378+ been signaled. *)
376379 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;
377386 backoff := Backoff. once ! backoff
378387 done
379388 with Not_found -> ()
380389
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-
399390 module Awaiter = struct
400391 type t = Awaiters .is1
401392
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
404413
405414 let remove one =
406415 Awaiters. signal_and_clear one;
407416 update (Awaiters. awaitable_of one) ~signal: false ~count: 1
417+ end
408418
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
410424 match Awaiters. await one with
411425 | None -> ()
412426 | Some exn_bt ->
413427 Awaiters. clear one;
414428 update (Awaiters. awaitable_of one) ~signal: true ~count: 1 ;
415429 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
421430
422431 let [@ inline] broadcast t = update (Packed t) ~signal: true ~count: Int. max_int
423432 let [@ inline] signal t = update (Packed t) ~signal: true ~count: 1
424433
425434 let () =
426435 Stdlib. at_exit @@ fun () ->
427436 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 %!"
429442 | exception Not_found -> ()
430443end
0 commit comments