33 * Copyright (c) 2023, Vesa Karvonen <vesa.a.j.k@gmail.com>
44 *)
55
6+ module Trigger = Trigger
67module Backoff = Backoff
78
9+ module AwaitableId = struct
10+ let id = Atomic. make (Int. min_int asr 1 )
11+ let get_unique () = Atomic. fetch_and_add id 1
12+ let is id = id < 0
13+ let to_awaiters id = Int. min_int - 1 - id
14+ end
15+
816module Id = struct
917 let id = Atomic. make 1
1018 let get_unique () = Atomic. fetch_and_add id 1
@@ -30,9 +38,11 @@ end = struct
3038 [@@ inline]
3139end
3240
41+ type awaiter = [ `Init | `Resumed | `Waiting of unit -> unit ] Atomic .t
3342type determined = [ `After | `Before ]
3443
35- type 'a loc = { state : 'a state Atomic .t ; id : int }
44+ type 'a loc = { state : 'a state Atomic .t ; id : int ; awaiters : awaiters }
45+ and awaiters = awaiter list loc
3646and 'a state = { mutable before : 'a ; mutable after : 'a ; mutable casn : casn }
3747and cass = CASN : 'a loc * 'a state * cass * cass -> cass | NIL : cass
3848and casn = status Atomic .t
@@ -214,12 +224,23 @@ let cas loc before state =
214224let inc x = x + 1
215225let dec x = x - 1
216226
227+ let make_awaiters id =
228+ let state = Atomic. make @@ new_state [] and id = AwaitableId. to_awaiters id in
229+ let rec awaiters = { state; id; awaiters } in
230+ awaiters
231+
217232module Loc = struct
218233 type 'a t = 'a loc
219234
220- let make after =
221- { state = Atomic. make @@ new_state after; id = Id. get_unique () }
235+ let make ?(awaitable = false ) after =
236+ let state = Atomic. make @@ new_state after in
237+ let id =
238+ if awaitable then AwaitableId. get_unique () else Id. get_unique ()
239+ in
240+ let awaiters = if awaitable then make_awaiters id else Obj. magic () in
241+ { state; id; awaiters }
222242
243+ let is_awaitable loc = AwaitableId. is loc.id
223244 let get_id loc = loc.id [@@ inline]
224245
225246 let get loc =
@@ -391,38 +412,106 @@ module Xt = struct
391412
392413 let call { tx } = tx [@@ inline]
393414
394- let attempt (mode : Mode.t ) tx =
415+ let rec take_awaiters ~xt awaiters = function
416+ | NIL -> awaiters
417+ | CASN (loc , state , l , r ) ->
418+ let awaiters =
419+ if l != NIL then take_awaiters ~xt awaiters l else awaiters
420+ in
421+ if AwaitableId. is loc.id then
422+ let awaiters =
423+ if state.before != state.after then
424+ match exchange ~xt loc.awaiters [] with
425+ | [] -> awaiters
426+ | aws -> aws :: awaiters
427+ else awaiters
428+ in
429+ take_awaiters ~xt awaiters r
430+ else awaiters
431+
432+ let resume_awaiters awaiters =
433+ awaiters
434+ |> List. iter @@ List. iter
435+ @@ fun awaiter ->
436+ match Atomic. exchange awaiter `Resumed with
437+ | `Waiting resume -> resume ()
438+ | _ -> ()
439+ [@@ inline never]
440+
441+ let resume_awaiters = function
442+ | [] -> ()
443+ | awaiters -> resume_awaiters awaiters
444+ [@@ inline]
445+
446+ let rec add_awaiter ~xt awaiter = function
447+ | NIL -> ()
448+ | CASN (loc , _ , l , r ) ->
449+ if l != NIL then add_awaiter ~xt awaiter l;
450+ if AwaitableId. is loc.id then (
451+ modify ~xt loc.awaiters (List. cons awaiter);
452+ add_awaiter ~xt awaiter r)
453+
454+ let rec reset casn = function
455+ | NIL -> NIL
456+ | CASN (loc , state , l , r ) as old ->
457+ let l' = reset casn l and r' = reset casn r in
458+ if is_cmp casn state then
459+ if l == l' && r == r' then old else CASN (loc, state, l', r')
460+ else
461+ let state' = Atomic. get loc.state in
462+ let current = eval state' in
463+ if current != state.before then exit () else CASN (loc, state', l, r)
464+
465+ let rec commit backoff (mode : Mode.t ) tx =
395466 let xt =
396467 let casn = Atomic. make (mode :> status )
397468 and cass = NIL
398469 and post_commit = Action. noop in
399470 { casn; cass; post_commit }
400471 in
401- let result = tx ~xt in
402- match xt.cass with
403- | NIL -> Action. run xt.post_commit result
404- | CASN (loc , state , NIL, NIL) ->
405- if is_cmp xt.casn state then Action. run xt.post_commit result
406- else
407- let before = state.before in
408- state.before < - state.after;
409- if cas loc before state then Action. run xt.post_commit result
410- else exit ()
411- | cass ->
412- if determine_for_owner xt.casn cass then
413- Action. run xt.post_commit result
414- else exit ()
415-
416- let rec commit backoff mode tx =
417- match attempt mode tx with
418- | result -> result
419- | exception Mode. Interference ->
420- commit (Backoff. once backoff) Mode. lock_free tx
421- | exception Exit -> commit (Backoff. once backoff) mode tx
422-
423- let commit ?(backoff = Backoff. default) ?(mode = Mode. obstruction_free) tx =
424- commit backoff mode tx.tx
472+ match tx ~xt with
473+ | result -> (
474+ let awaiters = take_awaiters ~xt [] xt.cass in
475+ match xt.cass with
476+ | NIL -> Action. run xt.post_commit result
477+ | CASN (loc , state , NIL, NIL) ->
478+ if is_cmp xt.casn state then Action. run xt.post_commit result
479+ else
480+ let before = state.before in
481+ state.before < - state.after;
482+ if cas loc before state then Action. run xt.post_commit result
483+ else commit (Backoff. once backoff) mode tx
484+ | cass -> (
485+ match determine_for_owner xt.casn cass with
486+ | true ->
487+ resume_awaiters awaiters;
488+ Action. run xt.post_commit result
489+ | false -> commit (Backoff. once backoff) mode tx
490+ | exception Mode. Interference ->
491+ commit (Backoff. once backoff) Mode. lock_free tx))
492+ | exception Exit -> (
493+ match reset xt.casn xt.cass with
494+ | cass -> (
495+ xt.cass < - cass;
496+ let self = Atomic. make `Init in
497+ add_awaiter ~xt self cass;
498+ if xt.cass == cass then commit (Backoff. once backoff) mode tx
499+ else
500+ match determine_for_owner xt.casn xt.cass with
501+ | true ->
502+ (if Atomic. get self == `Init then
503+ let t = Trigger. prepare_for_await () in
504+ if Atomic. compare_and_set self `Init (`Waiting t.release)
505+ then t.await () );
506+ (* TODO: remove awaiters *)
507+ commit (Backoff. once (Backoff. reset backoff)) mode tx
508+ | false -> commit (Backoff. once backoff) mode tx
509+ | exception Mode. Interference ->
510+ commit (Backoff. once backoff) Mode. lock_free tx)
511+ | exception Exit -> commit (Backoff. once backoff) mode tx)
512+
513+ let commit ?(backoff = Backoff. default) ?(mode = Mode. obstruction_free) { tx }
514+ =
515+ commit backoff mode tx
425516 [@@ inline]
426-
427- let attempt ?(mode = Mode. lock_free) tx = attempt mode tx.tx [@@ inline]
428517end
0 commit comments