@@ -9,18 +9,21 @@ type _ tdt =
9
9
| Nothing : [> `Nothing ] tdt
10
10
11
11
type state =
12
+ | Locked
12
13
| Unlocked
13
- | Locked of { fiber : Fiber.Maybe .t ; waiters : [ `Entry ] tdt Q .t }
14
+ | Queued of { fiber : Fiber.Maybe .t ; waiters : [ `Entry ] tdt Q .t }
14
15
15
16
type t = state Atomic .t
16
17
17
18
let create ?padded () = Multicore_magic. copy_as ?padded @@ Atomic. make Unlocked
18
- let locked_nothing = Locked { fiber = Fiber.Maybe. nothing; waiters = T Zero }
19
19
20
20
let rec unlock_as owner t backoff =
21
21
match Atomic. get t with
22
22
| Unlocked -> unlocked ()
23
- | Locked r as before ->
23
+ | Locked as before ->
24
+ if not (Atomic. compare_and_set t before Unlocked ) then
25
+ unlock_as owner t (Backoff. once backoff)
26
+ | Queued r as before ->
24
27
if Fiber.Maybe. equal r.fiber owner then
25
28
match r.waiters with
26
29
| T Zero ->
@@ -29,42 +32,72 @@ let rec unlock_as owner t backoff =
29
32
| T (One _ as q ) ->
30
33
let (Entry { trigger; fiber }) = Q. head q in
31
34
let waiters = Q. tail q in
32
- let after = Locked { fiber; waiters } in
35
+ let after = Queued { fiber; waiters } in
33
36
if Atomic. compare_and_set t before after then Trigger. signal trigger
34
37
else unlock_as owner t (Backoff. once backoff)
35
38
else not_owner ()
36
39
37
40
let [@ inline] unlock ?checked t =
38
- let owner = Fiber.Maybe. current_if checked in
39
- unlock_as owner t Backoff. default
41
+ match checked with
42
+ | Some false ->
43
+ if
44
+ (* The unlock operation will mutate the atomic location and will be
45
+ sequentially consistent. The fenceless get potentially allows us to
46
+ avoid performing a failed mutation attempt causing cache coherency
47
+ traffic and fenceless get here performs better on ARM. *)
48
+ Multicore_magic. fenceless_get t != Locked
49
+ || not (Atomic. compare_and_set t Locked Unlocked )
50
+ then unlock_as Fiber.Maybe. nothing t Backoff. default
51
+ | None | Some true ->
52
+ let owner = Fiber.Maybe. of_fiber (Fiber. current () ) in
53
+ unlock_as owner t Backoff. default
40
54
41
55
let rec cleanup_as (Entry entry_r as entry : [ `Entry ] tdt ) t backoff =
42
56
(* We have been canceled. If we are the owner, we must unlock the mutex.
43
57
Otherwise we must remove our entry from the queue. *)
44
58
match Atomic. get t with
45
- | Locked r as before -> begin
59
+ | Queued r as before -> begin
46
60
match r.waiters with
47
61
| T Zero -> unlock_as entry_r.fiber t backoff
48
62
| T (One _ as q ) ->
49
63
let waiters = Q. remove q entry in
50
64
if r.waiters == waiters then unlock_as entry_r.fiber t backoff
51
65
else
52
- let after = Locked { fiber = r.fiber; waiters } in
66
+ let after = Queued { fiber = r.fiber; waiters } in
53
67
if not (Atomic. compare_and_set t before after) then
54
68
cleanup_as entry t (Backoff. once backoff)
55
69
end
70
+ | Locked -> unlock_as entry_r.fiber t backoff
56
71
| Unlocked -> unlocked ()
57
72
58
73
let rec lock_as fiber t entry backoff =
59
74
match Atomic. get t with
60
75
| Unlocked as before ->
61
76
let after =
62
- if fiber == Fiber.Maybe. nothing then locked_nothing
63
- else Locked { fiber; waiters = T Zero }
77
+ if fiber == Fiber.Maybe. nothing then Locked
78
+ else Queued { fiber; waiters = T Zero }
64
79
in
65
80
if not (Atomic. compare_and_set t before after) then
66
81
lock_as fiber t entry (Backoff. once backoff)
67
- | Locked r as before ->
82
+ | Locked as before ->
83
+ let (Entry entry_r as entry : [ `Entry ] tdt ) =
84
+ match entry with
85
+ | Nothing ->
86
+ let trigger = Trigger. create () in
87
+ Entry { trigger; fiber }
88
+ | Entry _ as entry -> entry
89
+ in
90
+ let waiters = Q. singleton entry in
91
+ let after = Queued { fiber = Fiber.Maybe. nothing; waiters } in
92
+ if Atomic. compare_and_set t before after then begin
93
+ match Trigger. await entry_r.trigger with
94
+ | None -> ()
95
+ | Some (exn , bt ) ->
96
+ cleanup_as entry t Backoff. default;
97
+ Printexc. raise_with_backtrace exn bt
98
+ end
99
+ else lock_as fiber t entry (Backoff. once backoff)
100
+ | Queued r as before ->
68
101
if Fiber.Maybe. unequal r.fiber fiber then
69
102
let (Entry entry_r as entry : [ `Entry ] tdt ) =
70
103
match entry with
@@ -74,7 +107,7 @@ let rec lock_as fiber t entry backoff =
74
107
| Entry _ as entry -> entry
75
108
in
76
109
let waiters = Q. add r.waiters entry in
77
- let after = Locked { fiber = r.fiber; waiters } in
110
+ let after = Queued { fiber = r.fiber; waiters } in
78
111
if Atomic. compare_and_set t before after then begin
79
112
match Trigger. await entry_r.trigger with
80
113
| None -> ()
@@ -86,15 +119,27 @@ let rec lock_as fiber t entry backoff =
86
119
else owner ()
87
120
88
121
let [@ inline] lock ?checked t =
89
- let fiber = Fiber.Maybe. current_and_check_if checked in
90
- lock_as fiber t Nothing Backoff. default
122
+ match checked with
123
+ | Some false ->
124
+ if
125
+ (* The lock operation will mutate the atomic location and will be
126
+ sequentially consistent. The fenceless get potentially allows us to
127
+ avoid performing a failed mutation attempt causing cache coherency
128
+ traffic and fenceless get here performs better on ARM. *)
129
+ Multicore_magic. fenceless_get t != Unlocked
130
+ || not (Atomic. compare_and_set t Unlocked Locked )
131
+ then lock_as Fiber.Maybe. nothing t Nothing Backoff. default
132
+ | None | Some true ->
133
+ let fiber = Fiber. current () in
134
+ Fiber. check fiber;
135
+ lock_as (Fiber.Maybe. of_fiber fiber) t Nothing Backoff. default
91
136
92
137
let try_lock ?checked t =
93
138
let fiber = Fiber.Maybe. current_and_check_if checked in
94
139
Atomic. get t == Unlocked
95
140
&& Atomic. compare_and_set t Unlocked
96
- (if fiber == Fiber.Maybe. nothing then locked_nothing
97
- else Locked { fiber; waiters = T Zero })
141
+ (if fiber == Fiber.Maybe. nothing then Locked
142
+ else Queued { fiber; waiters = T Zero })
98
143
99
144
let protect ?checked t body =
100
145
let fiber = Fiber.Maybe. current_and_check_if checked in
0 commit comments