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
@@ -30,9 +37,11 @@ end = struct
3037 [@@ inline]
3138end
3239
40+ type awaiter = [ `Init | `Resumed | `Waiting of unit -> unit ] Atomic .t
3341type determined = [ `After | `Before ]
3442
35- type 'a loc = { state : 'a state Atomic .t ; id : int }
43+ type 'a loc = { state : 'a state Atomic .t ; id : int ; awaiters : awaiters }
44+ and awaiters = awaiter list loc
3645and 'a state = { mutable before : 'a ; mutable after : 'a ; mutable casn : casn }
3746and cass = CASN : 'a loc * 'a state * cass * cass -> cass | NIL : cass
3847and casn = status Atomic .t
@@ -214,12 +223,23 @@ let cas loc before state =
214223let inc x = x + 1
215224let dec x = x - 1
216225
226+ let make_awaiters id =
227+ let state = Atomic. make @@ new_state [] and id = AwaitableId. to_awaiters id in
228+ let rec awaiters = { state; id; awaiters } in
229+ awaiters
230+
217231module Loc = struct
218232 type 'a t = 'a loc
219233
220- let make after =
221- { state = Atomic. make @@ new_state after; id = Id. get_unique () }
234+ let make ?(awaitable = false ) after =
235+ let state = Atomic. make @@ new_state after in
236+ let id =
237+ if awaitable then AwaitableId. get_unique () else Id. get_unique ()
238+ in
239+ let awaiters = if awaitable then make_awaiters id else Obj. magic () in
240+ { state; id; awaiters }
222241
242+ let is_awaitable loc = AwaitableId. is loc.id
223243 let get_id loc = loc.id [@@ inline]
224244
225245 let get loc =
@@ -539,6 +559,37 @@ module Xt = struct
539559
540560 let call { tx } = tx [@@ inline]
541561
562+ let rec take_awaiters ~xt awaiters = function
563+ | NIL -> awaiters
564+ | CASN (loc , state , l , r ) ->
565+ let awaiters =
566+ if l != NIL then take_awaiters ~xt awaiters l else awaiters
567+ in
568+ if AwaitableId. is loc.id then
569+ let awaiters =
570+ if state.before != state.after then
571+ match exchange ~xt loc.awaiters [] with
572+ | [] -> awaiters
573+ | aws -> aws :: awaiters
574+ else awaiters
575+ in
576+ take_awaiters ~xt awaiters r
577+ else awaiters
578+
579+ let resume_awaiters awaiters =
580+ awaiters
581+ |> List. iter @@ List. iter
582+ @@ fun awaiter ->
583+ match Atomic. exchange awaiter `Resumed with
584+ | `Waiting resume -> resume ()
585+ | _ -> ()
586+ [@@ inline never]
587+
588+ let resume_awaiters = function
589+ | [] -> ()
590+ | awaiters -> resume_awaiters awaiters
591+ [@@ inline]
592+
542593 let attempt (mode : Mode.t ) tx =
543594 let xt =
544595 let casn = Atomic. make (mode :> status )
@@ -547,6 +598,7 @@ module Xt = struct
547598 { casn; cass; post_commit }
548599 in
549600 let result = tx ~xt in
601+ let awaiters = take_awaiters ~xt [] xt.cass in
550602 match xt.cass with
551603 | NIL -> Action. run xt.post_commit result
552604 | CASN (loc , state , NIL, NIL) ->
@@ -557,22 +609,97 @@ module Xt = struct
557609 if cas loc before state then Action. run xt.post_commit result
558610 else exit ()
559611 | cass ->
560- if determine_for_owner xt.casn cass then
561- Action. run xt.post_commit result
612+ if determine_for_owner xt.casn cass then (
613+ resume_awaiters awaiters;
614+ Action. run xt.post_commit result)
562615 else exit ()
563616
564- let rec commit backoff mode tx =
565- match attempt mode tx with
566- | result -> result
567- | exception Mode. Interference ->
568- commit (Backoff. once backoff) Mode. lock_free tx
569- | exception Exit -> commit (Backoff. once backoff) mode tx
617+ let attempt ?(mode = Mode. lock_free) tx = attempt mode tx.tx [@@ inline]
570618
571- let commit ?(backoff = Backoff. default) ?(mode = Mode. obstruction_free) tx =
572- commit backoff mode tx.tx
573- [@@ inline]
619+ type scheduler = ((unit -> unit ) -> unit ) -> unit
620+
621+ let rec add_awaiter ~xt awaiter = function
622+ | NIL -> ()
623+ | CASN (loc , _ , l , r ) ->
624+ if l != NIL then add_awaiter ~xt awaiter l;
625+ if AwaitableId. is loc.id then (
626+ modify ~xt loc.awaiters (List. cons awaiter);
627+ add_awaiter ~xt awaiter r)
628+
629+ let rec reset casn = function
630+ | NIL -> NIL
631+ | CASN (loc , state , l , r ) as old ->
632+ let l' = reset casn l and r' = reset casn r in
633+ if is_cmp casn state then
634+ if l == l' && r == r' then old else CASN (loc, state, l', r')
635+ else
636+ let state' = Atomic. get loc.state in
637+ let current = eval state' in
638+ if current != state.before then exit () else CASN (loc, state', l, r)
574639
575- let attempt ?(mode = Mode. lock_free) tx = attempt mode tx.tx [@@ inline]
640+ let rec commit backoff (mode : Mode.t ) scheduler_opt tx =
641+ let xt =
642+ let casn = Atomic. make (mode :> status )
643+ and cass = NIL
644+ and post_commit = Action. noop in
645+ { casn; cass; post_commit }
646+ in
647+ match tx ~xt with
648+ | result -> (
649+ let awaiters = take_awaiters ~xt [] xt.cass in
650+ match xt.cass with
651+ | NIL -> Action. run xt.post_commit result
652+ | CASN (loc , state , NIL, NIL) ->
653+ if is_cmp xt.casn state then Action. run xt.post_commit result
654+ else
655+ let before = state.before in
656+ state.before < - state.after;
657+ if cas loc before state then Action. run xt.post_commit result
658+ else commit (Backoff. once backoff) mode scheduler_opt tx
659+ | cass -> (
660+ match determine_for_owner xt.casn cass with
661+ | true ->
662+ resume_awaiters awaiters;
663+ Action. run xt.post_commit result
664+ | false -> commit (Backoff. once backoff) mode scheduler_opt tx
665+ | exception Mode. Interference ->
666+ commit (Backoff. once backoff) Mode. lock_free scheduler_opt tx))
667+ | exception Exit -> (
668+ match scheduler_opt with
669+ | None -> commit (Backoff. once backoff) mode scheduler_opt tx
670+ | Some scheduler -> (
671+ match reset xt.casn xt.cass with
672+ | cass -> (
673+ xt.cass < - cass;
674+ let self = Atomic. make `Init in
675+ add_awaiter ~xt self cass;
676+ if xt.cass == cass then
677+ commit (Backoff. once backoff) mode scheduler_opt tx
678+ else
679+ match determine_for_owner xt.casn xt.cass with
680+ | true ->
681+ if Atomic. get self == `Init then
682+ scheduler (fun resume ->
683+ if
684+ not
685+ (Atomic. compare_and_set self `Init
686+ (`Waiting resume))
687+ then resume () );
688+ (* TODO: remove awaiters *)
689+ commit
690+ (Backoff. once (Backoff. reset backoff))
691+ mode scheduler_opt tx
692+ | false -> commit (Backoff. once backoff) mode scheduler_opt tx
693+ | exception Mode. Interference ->
694+ commit (Backoff. once backoff) Mode. lock_free scheduler_opt
695+ tx)
696+ | exception Exit ->
697+ commit (Backoff. once backoff) mode scheduler_opt tx))
698+
699+ let commit ?(backoff = Backoff. default) ?(mode = Mode. obstruction_free)
700+ ?scheduler { tx } =
701+ commit backoff mode scheduler tx
702+ [@@ inline]
576703
577704 let of_tx tx ~xt =
578705 let (_, cass, post_commit), x = tx (xt.casn, xt.cass, xt.post_commit) in
0 commit comments