55
66module Backoff = Backoff
77
8+ module AwaitableId = struct
9+ let id = Atomic. make (Int. min_int asr 1 )
10+ let get_unique () = Atomic. fetch_and_add id 1
11+ let is id = id < 0
12+ let to_awaiters id = Int. min_int - 1 - id
13+ end
14+
815module Id = struct
916 let id = Atomic. make 1
1017 let get_unique () = Atomic. fetch_and_add id 1
1118end
1219
20+ type awaiter = [ `Init | `Resumed | `Waiting of unit -> unit ] Atomic .t
1321type determined = [ `After | `Before ]
1422
15- type 'a loc = { state : 'a state Atomic .t ; id : int }
23+ type 'a loc = { state : 'a state Atomic .t ; id : int ; awaiters : awaiters }
24+ and awaiters = awaiter list loc
1625and 'a state = { mutable before : 'a ; mutable after : 'a ; mutable casn : casn }
1726and cass = CASN : 'a loc * 'a state * cass * cass -> cass | NIL : cass
1827and casn = status Atomic .t
@@ -190,11 +199,26 @@ let cas loc before state =
190199 && Atomic. compare_and_set loc.state state' state
191200 [@@ inline]
192201
202+ let make_awaiters id =
203+ let rec awaiters =
204+ {
205+ state = Atomic. make @@ new_state [] ;
206+ id = AwaitableId. to_awaiters id;
207+ awaiters;
208+ }
209+ in
210+ awaiters
211+
193212module Loc = struct
194213 type 'a t = 'a loc
195214
196- let make after =
197- { state = Atomic. make @@ new_state after; id = Id. get_unique () }
215+ let make ?(awaitable = false ) after =
216+ let state = Atomic. make @@ new_state after in
217+ let id =
218+ if awaitable then AwaitableId. get_unique () else Id. get_unique ()
219+ in
220+ let awaiters = if awaitable then make_awaiters id else Obj. magic () in
221+ { state; id; awaiters }
198222
199223 let get_id loc = loc.id [@@ inline]
200224
@@ -446,9 +470,41 @@ module Xt = struct
446470
447471 let call { tx } = tx [@@ inline]
448472
473+ let rec take_awaiters ~xt awaiters = function
474+ | NIL -> awaiters
475+ | CASN (loc , state , l , r ) ->
476+ let awaiters =
477+ if l != NIL then take_awaiters ~xt awaiters l else awaiters
478+ in
479+ if AwaitableId. is loc.id then
480+ let awaiters =
481+ if state.before != state.after then
482+ match exchange ~xt loc.awaiters [] with
483+ | [] -> awaiters
484+ | aws -> aws :: awaiters
485+ else awaiters
486+ in
487+ take_awaiters ~xt awaiters r
488+ else awaiters
489+
490+ let resume_awaiters awaiters =
491+ awaiters
492+ |> List. iter @@ List. iter
493+ @@ fun awaiter ->
494+ match Atomic. exchange awaiter `Resumed with
495+ | `Waiting resume -> resume ()
496+ | _ -> ()
497+ [@@ inline never]
498+
499+ let resume_awaiters = function
500+ | [] -> ()
501+ | awaiters -> resume_awaiters awaiters
502+ [@@ inline]
503+
449504 let attempt (mode : Mode.t ) tx =
450505 let xt = { casn = Atomic. make (mode :> status ); cass = NIL } in
451506 let result = tx ~xt in
507+ let awaiters = take_awaiters ~xt [] xt.cass in
452508 match xt.cass with
453509 | NIL -> result
454510 | CASN (loc , state , NIL, NIL) ->
@@ -457,20 +513,83 @@ module Xt = struct
457513 let before = state.before in
458514 state.before < - state.after;
459515 if cas loc before state then result else exit ()
460- | cass -> if determine_for_owner xt.casn cass then result else exit ()
516+ | cass ->
517+ if determine_for_owner xt.casn cass then (
518+ resume_awaiters awaiters;
519+ result)
520+ else exit ()
461521
462- let rec commit backoff mode tx =
463- match attempt mode tx with
464- | result -> result
465- | exception Mode. Interference ->
466- commit (Backoff. once backoff) Mode. lock_free tx
467- | exception Exit -> commit (Backoff. once backoff) mode tx
522+ let attempt ?(mode = Mode. lock_free) tx = attempt mode tx.tx [@@ inline]
468523
469- let commit ?(backoff = Backoff. default) ?(mode = Mode. obstruction_free) tx =
470- commit backoff mode tx.tx
471- [@@ inline]
524+ type scheduler = ((unit -> unit ) -> unit ) -> unit
525+
526+ let rec add_awaiter ~xt awaiter = function
527+ | NIL -> ()
528+ | CASN (loc , _ , l , r ) ->
529+ if l != NIL then add_awaiter ~xt awaiter l;
530+ if AwaitableId. is loc.id then (
531+ modify ~xt loc.awaiters (List. cons awaiter);
532+ add_awaiter ~xt awaiter r)
533+
534+ let rec reset casn = function
535+ | NIL -> NIL
536+ | CASN (loc , state , l , r ) as old ->
537+ let l' = reset casn l and r' = reset casn r in
538+ if is_cmp casn state then
539+ if l == l' && r == r' then old else CASN (loc, state, l', r')
540+ else
541+ let state' = Atomic. get loc.state in
542+ let current = eval state' in
543+ if current != state.before then exit () else CASN (loc, state', l, r)
472544
473- let attempt ?(mode = Mode. lock_free) tx = attempt mode tx.tx [@@ inline]
545+ let rec commit backoff (mode : Mode.t ) scheduler_opt tx =
546+ let xt = { casn = Atomic. make (mode :> status ); cass = NIL } in
547+ match tx ~xt with
548+ | result -> (
549+ let awaiters = take_awaiters ~xt [] xt.cass in
550+ match determine_for_owner xt.casn xt.cass with
551+ | true ->
552+ resume_awaiters awaiters;
553+ result
554+ | false -> commit (Backoff. once backoff) mode scheduler_opt tx
555+ | exception Mode. Interference ->
556+ commit (Backoff. once backoff) Mode. lock_free scheduler_opt tx)
557+ | exception Exit -> (
558+ match scheduler_opt with
559+ | None -> commit (Backoff. once backoff) mode scheduler_opt tx
560+ | Some scheduler -> (
561+ match reset xt.casn xt.cass with
562+ | cass -> (
563+ xt.cass < - cass;
564+ let self = Atomic. make `Init in
565+ add_awaiter ~xt self cass;
566+ if xt.cass == cass then
567+ commit (Backoff. once backoff) mode scheduler_opt tx
568+ else
569+ match determine_for_owner xt.casn xt.cass with
570+ | true ->
571+ if Atomic. get self == `Init then
572+ scheduler (fun resume ->
573+ if
574+ not
575+ (Atomic. compare_and_set self `Init
576+ (`Waiting resume))
577+ then resume () );
578+ (* TODO: remove awaiters *)
579+ commit
580+ (Backoff. once (Backoff. reset backoff))
581+ mode scheduler_opt tx
582+ | false -> commit (Backoff. once backoff) mode scheduler_opt tx
583+ | exception Mode. Interference ->
584+ commit (Backoff. once backoff) Mode. lock_free scheduler_opt
585+ tx)
586+ | exception Exit ->
587+ commit (Backoff. once backoff) mode scheduler_opt tx))
588+
589+ let commit ?(backoff = Backoff. default) ?(mode = Mode. obstruction_free)
590+ ?scheduler { tx } =
591+ commit backoff mode scheduler tx
592+ [@@ inline]
474593
475594 let of_tx tx ~xt =
476595 let (_, cass), x = tx (xt.casn, xt.cass) in
0 commit comments