@@ -2,52 +2,78 @@ open Kcas
2
2
3
3
type 'a t = {
4
4
front : 'a Elems .t Loc .t ;
5
- middle : 'a Elems .t Loc .t ;
6
- back : 'a Elems .t Loc .t ;
5
+ back : 'a List_with_capacity .t Loc .t ;
6
+ middle : 'a List_with_capacity .t Loc .t ;
7
7
}
8
8
9
- let alloc ~front ~middle ~ back =
9
+ let alloc ~front ~back ~ middle =
10
10
(* We allocate locations in specific order to make most efficient use of the
11
11
splay-tree based transaction log. *)
12
12
let front = Loc. make ~padded: true front
13
- and middle = Loc. make ~padded: true middle
14
- and back = Loc. make ~padded: true back in
13
+ and back = Loc. make ~padded: true back
14
+ and middle = Loc. make ~padded: true middle in
15
15
Multicore_magic. copy_as_padded { back; middle; front }
16
16
17
- let create () = alloc ~front: Elems. empty ~middle: Elems. empty ~back: Elems. empty
17
+ let create ?(capacity = Int. max_int) () =
18
+ if capacity < 0 then invalid_arg " Queue.create: capacity must be non-negative" ;
19
+ let back = List_with_capacity. make_empty ~capacity in
20
+ alloc ~front: Elems. empty ~back ~middle: List_with_capacity. empty_unlimited
18
21
19
22
let copy q =
20
- let tx ~xt = (Xt. get ~xt q.front, Xt. get ~xt q.middle , Xt. get ~xt q.back ) in
21
- let front, middle, back = Xt. commit { tx } in
22
- alloc ~front ~middle ~back
23
+ let tx ~xt = (Xt. get ~xt q.front, Xt. get ~xt q.back , Xt. get ~xt q.middle ) in
24
+ let front, back, middle = Xt. commit { tx } in
25
+ alloc ~front ~back ~middle
23
26
24
27
module Xt = struct
25
28
let is_empty ~xt t =
26
29
(* We access locations in order of allocation to make most efficient use of
27
30
the splay-tree based transaction log. *)
28
31
Xt. get ~xt t.front == Elems. empty
29
- && Xt. get ~xt t.middle == Elems. empty
30
- && Xt. get ~xt t.back == Elems. empty
31
-
32
- let length ~xt { back; middle; front } =
33
- Elems. length (Xt. get ~xt front)
34
- + Elems. length (Xt. get ~xt middle)
35
- + Elems. length (Xt. get ~xt back)
36
-
37
- let add ~xt x q = Xt. modify ~xt q.back @@ Elems. cons x
32
+ && List_with_capacity. is_empty (Xt. get ~xt t.back)
33
+ && Xt. get ~xt t.middle == List_with_capacity. empty_unlimited
34
+
35
+ let length ~xt q =
36
+ Elems. length (Xt. get ~xt q.front)
37
+ + List_with_capacity. length (Xt. get ~xt q.back)
38
+ + List_with_capacity. length (Xt. get ~xt q.middle)
39
+
40
+ let try_add ~xt x q =
41
+ let lwc = Xt. update ~xt q.back (List_with_capacity. cons_safe x) in
42
+ let capacity = List_with_capacity. capacity lwc in
43
+ capacity = Int. max_int
44
+ ||
45
+ let back_length = List_with_capacity. length lwc in
46
+ back_length < List_with_capacity. limit lwc
47
+ ||
48
+ let other_length =
49
+ List_with_capacity. length (Xt. get ~xt q.middle)
50
+ + Elems. length (Xt. get ~xt q.front)
51
+ in
52
+ let limit = capacity - other_length in
53
+ back_length < limit
54
+ &&
55
+ (Xt. set ~xt q.back
56
+ (List_with_capacity. make ~capacity ~length: (back_length + 1 )
57
+ ~list: (x :: List_with_capacity. list lwc)
58
+ ~limit );
59
+ true )
60
+
61
+ let add ~xt x q = Retry. unless (try_add ~xt x q)
38
62
let push = add
39
63
40
64
(* * Cooperative helper to move elems from back to middle. *)
41
- let back_to_middle ~middle ~ back =
65
+ let back_to_middle ~back ~ middle =
42
66
let tx ~xt =
43
- let xs = Xt. exchange ~xt back Elems. empty in
44
- if xs == Elems. empty || Xt. exchange ~xt middle xs != Elems. empty then
45
- raise_notrace Exit
67
+ let xs = Xt. update ~xt back List_with_capacity. move in
68
+ if
69
+ List_with_capacity. length xs = 0
70
+ || Xt. exchange ~xt middle xs != List_with_capacity. empty_unlimited
71
+ then raise_notrace Exit
46
72
in
47
73
try Xt. commit { tx } with Exit -> ()
48
74
49
- let take_opt_finish ~xt front elems =
50
- let elems = Elems. rev elems in
75
+ let take_opt_finish ~xt front lwc =
76
+ let elems = List_with_capacity. to_rev_elems lwc in
51
77
Xt. set ~xt front (Elems. tl_safe elems);
52
78
Elems. hd_opt elems
53
79
@@ -58,17 +84,19 @@ module Xt = struct
58
84
else
59
85
let middle = t.middle and back = t.back in
60
86
if not (Xt. is_in_log ~xt middle || Xt. is_in_log ~xt back) then
61
- back_to_middle ~middle ~back ;
62
- let elems = Xt. exchange ~xt middle Elems. empty in
63
- if elems != Elems. empty then take_opt_finish ~xt front elems
87
+ back_to_middle ~back ~middle ;
88
+ let lwc = Xt. exchange ~xt middle List_with_capacity. empty_unlimited in
89
+ if lwc != List_with_capacity. empty_unlimited then
90
+ take_opt_finish ~xt front lwc
64
91
else
65
- let elems = Xt. exchange ~xt back Elems. empty in
66
- if elems != Elems. empty then take_opt_finish ~xt front elems else None
92
+ let lwc = Xt. update ~xt back List_with_capacity. move_last in
93
+ if List_with_capacity. length lwc <> 0 then take_opt_finish ~xt front lwc
94
+ else None
67
95
68
96
let take_blocking ~xt q = Xt. to_blocking ~xt (take_opt q)
69
97
70
- let peek_opt_finish ~xt front elems =
71
- let elems = Elems. rev elems in
98
+ let peek_opt_finish ~xt front lwc =
99
+ let elems = List_with_capacity. to_rev_elems lwc in
72
100
Xt. set ~xt front elems;
73
101
Elems. hd_opt elems
74
102
@@ -79,57 +107,72 @@ module Xt = struct
79
107
else
80
108
let middle = t.middle and back = t.back in
81
109
if not (Xt. is_in_log ~xt middle || Xt. is_in_log ~xt back) then
82
- back_to_middle ~middle ~back ;
83
- let elems = Xt. exchange ~xt middle Elems. empty in
84
- if elems != Elems. empty then peek_opt_finish ~xt front elems
110
+ back_to_middle ~back ~middle ;
111
+ let lwc = Xt. exchange ~xt middle List_with_capacity. empty_unlimited in
112
+ if lwc != List_with_capacity. empty_unlimited then
113
+ peek_opt_finish ~xt front lwc
85
114
else
86
- let elems = Xt. exchange ~xt back Elems. empty in
87
- if elems != Elems. empty then peek_opt_finish ~xt front elems else None
115
+ let lwc = Xt. update ~xt back List_with_capacity. move_last in
116
+ if List_with_capacity. length lwc <> 0 then peek_opt_finish ~xt front lwc
117
+ else None
88
118
89
119
let peek_blocking ~xt q = Xt. to_blocking ~xt (peek_opt q)
90
120
91
121
let clear ~xt t =
92
122
Xt. set ~xt t.front Elems. empty;
93
- Xt. set ~xt t.middle Elems. empty ;
94
- Xt. set ~xt t.back Elems. empty
123
+ Xt. modify ~xt t.back List_with_capacity. clear ;
124
+ Xt. set ~xt t.middle List_with_capacity. empty_unlimited
95
125
96
126
let swap ~xt q1 q2 =
97
127
let front = Xt. get ~xt q1.front
98
- and middle = Xt. get ~xt q1.middle
99
- and back = Xt. get ~xt q1.back in
128
+ and back = Xt. get ~xt q1.back
129
+ and middle = Xt. get ~xt q1.middle in
100
130
let front = Xt. exchange ~xt q2.front front
101
- and middle = Xt. exchange ~xt q2.middle middle
102
- and back = Xt. exchange ~xt q2.back back in
131
+ and back = Xt. exchange ~xt q2.back back
132
+ and middle = Xt. exchange ~xt q2.middle middle in
103
133
Xt. set ~xt q1.front front;
104
- Xt. set ~xt q1.middle middle ;
105
- Xt. set ~xt q1.back back
134
+ Xt. set ~xt q1.back back ;
135
+ Xt. set ~xt q1.middle middle
106
136
107
137
let seq_of ~front ~middle ~back =
108
138
(* Sequence construction is lazy, so this function is O(1). *)
109
139
Seq. empty
110
- |> Elems . rev_prepend_to_seq back
111
- |> Elems . rev_prepend_to_seq middle
140
+ |> List_with_capacity . rev_prepend_to_seq back
141
+ |> List_with_capacity . rev_prepend_to_seq middle
112
142
|> Elems. prepend_to_seq front
113
143
114
144
let to_seq ~xt t =
115
145
let front = Xt. get ~xt t.front
116
- and middle = Xt. get ~xt t.middle
117
- and back = Xt. get ~xt t.back in
146
+ and back = Xt. get ~xt t.back
147
+ and middle = Xt. get ~xt t.middle in
118
148
seq_of ~front ~middle ~back
119
149
120
150
let take_all ~xt t =
121
151
let front = Xt. exchange ~xt t.front Elems. empty
122
- and middle = Xt. exchange ~xt t.middle Elems. empty
123
- and back = Xt. exchange ~xt t.back Elems. empty in
152
+ and back = Xt. update ~xt t.back List_with_capacity. clear
153
+ and middle = Xt. exchange ~xt t.middle List_with_capacity. empty_unlimited in
124
154
seq_of ~front ~middle ~back
125
155
end
126
156
127
157
let is_empty q = Kcas.Xt. commit { tx = Xt. is_empty q }
128
158
let length q = Kcas.Xt. commit { tx = Xt. length q }
129
159
160
+ let try_add x q =
161
+ (* Fenceless is safe as we revert to a transaction in case we didn't update. *)
162
+ let lwc = Loc. fenceless_update q.back (List_with_capacity. cons_safe x) in
163
+ let capacity = List_with_capacity. capacity lwc in
164
+ capacity = Int. max_int
165
+ ||
166
+ let back_length = List_with_capacity. length lwc in
167
+ back_length < List_with_capacity. limit lwc
168
+ || Kcas.Xt. commit { tx = Xt. try_add x q }
169
+
130
170
let add x q =
131
- (* Fenceless is safe as we always update. *)
132
- Loc. fenceless_modify q.back @@ Elems. cons x
171
+ (* Fenceless is safe as we revert to a transaction in case we didn't update. *)
172
+ let lwc = Loc. fenceless_update q.back (List_with_capacity. cons_safe x) in
173
+ if List_with_capacity. capacity lwc <> Int. max_int then
174
+ if List_with_capacity. length lwc = List_with_capacity. limit lwc then
175
+ Kcas.Xt. commit { tx = Xt. add x q }
133
176
134
177
let push = add
135
178
0 commit comments