1- let default_capacity = 4096
2- let spinlock_iterations = 16
1+ module Array = struct
2+ include Array
3+ let get = unsafe_get
4+ let set = unsafe_set
5+ end
36
4- type 'a cell =
5- | Empty
6- | Tombstone
7- | Value of 'a
7+ let default_capacity = 512
88
99type 'a s =
10- { buffer : 'a cell Atomic .t array
10+ { status : int Atomic .t array
11+ ; buffer : 'a array
1112 ; head : int Atomic .t
1213 ; tail : int Atomic .t
1314 ; rest : 'a s option Atomic .t
@@ -16,36 +17,40 @@ type 'a s =
1617type 'a t =
1718 { first : 'a s Atomic .t
1819 ; last : 'a s Atomic .t
20+ ; dummy : 'a
1921 }
2022
21- let make_s ~capacity () =
23+ let pack_size = Sys. int_size / 2
24+
25+ let make_s ~capacity ~dummy =
2226 { head = Atomic. make 0
2327 ; tail = Atomic. make (- 1 )
24- ; buffer = Array. init capacity (fun _ -> Atomic. make Empty )
28+ ; buffer = Array. make capacity dummy
29+ ; status = Array. init (1 + capacity / pack_size) (fun _ -> Atomic. make 0 )
2530 ; rest = Atomic. make None
2631 }
2732
28- let make ?(capacity = default_capacity) () =
29- let s = make_s ~capacity () in
33+ let make ?(capacity = default_capacity) ~ dummy () =
34+ let s = make_s ~capacity ~dummy in
3035 { first = Atomic. make s
3136 ; last = Atomic. make s
37+ ; dummy
3238 }
3339
3440let rec gift_rest t some_s =
35- if Atomic. compare_and_set t.rest None some_s
36- then ()
37- else follow_rest t some_s
41+ if not (Atomic. compare_and_set t.rest None some_s)
42+ then follow_rest t some_s
3843
3944and follow_rest t some_s =
4045 match Atomic. get t.rest with
4146 | None -> gift_rest t some_s
4247 | Some t -> follow_rest t some_s
4348
44- let force_rest t =
49+ let force_rest ~ dummy t =
4550 match Atomic. get t.rest with
4651 | Some s -> s
4752 | None ->
48- let s = make_s ~capacity: (Array. length t.buffer) () in
53+ let s = make_s ~capacity: (Array. length t.buffer) ~dummy in
4954 let some_s = Some s in
5055 if Atomic. compare_and_set t.rest None some_s
5156 then s
@@ -55,44 +60,45 @@ let force_rest t =
5560 gift_rest rest some_s ;
5661 rest
5762
58- let rec push_s t x =
63+ let mark t i =
64+ let status = t.status.(i / pack_size) in
65+ let shift = 2 * (i mod pack_size) in
66+ let status = Atomic. fetch_and_add status (1 lsl shift) in
67+ (status lsr shift) land 1 = 0
68+
69+ let rec push_s ~dummy t x =
5970 let i = Atomic. fetch_and_add t.tail 1 in
6071 if i < 0
61- then (let _ = force_rest t in push_s t x)
72+ then (let _ = force_rest ~dummy t in push_s ~dummy t x)
6273 else if i > = Array. length t.buffer
6374 then false
6475 else begin
65- let cell = Array. unsafe_get t.buffer i in
66- match Atomic. get cell with
67- | Empty ->
68- if Atomic. compare_and_set cell Empty (Value x)
69- then true
70- else begin
71- assert (Atomic. get cell = Tombstone ) ;
72- push_s t x
73- end
74- | Tombstone ->
75- push_s t x
76- | Value _ -> assert false
76+ t.buffer.(i) < - x ;
77+ if mark t i
78+ then true
79+ else begin
80+ t.buffer.(i) < - dummy ;
81+ let hd = Atomic. get t.head in
82+ let _ : bool = Atomic. compare_and_set t.tail (i + 1 ) (hd + 1 ) in
83+ push_s ~dummy t x
84+ end
7785 end
7886
79- let rec push t x =
80- let last = Atomic. get t.last in
81- if push_s last x
82- then ()
83- else begin
84- let rest = force_rest last in
85- let _ : bool = Atomic. compare_and_set t.last last rest in
87+ let rec push ({ last ; dummy ; _ } as t ) x =
88+ let last_s = Atomic. get last in
89+ if not (push_s ~dummy last_s x)
90+ then begin
91+ let rest = force_rest ~dummy last_s in
92+ let _ : bool = Atomic. compare_and_set last last_s rest in
8693 push t x
8794 end
8895
89-
9096type 'a pop_result =
9197 | Is_empty
9298 | Wait_for_it
9399 | Pop of 'a
94100
95- let rec pop_s t =
101+ let rec pop_s ~ dummy t =
96102 let current_head = Atomic. get t.head in
97103 if current_head > = Array. length t.buffer
98104 then Is_empty
@@ -102,38 +108,17 @@ let rec pop_s t =
102108 let i = Atomic. fetch_and_add t.head 1 in
103109 if i > = Array. length t.buffer
104110 then Is_empty
105- else
106- let cell = Array. unsafe_get t.buffer i in
107- if i > = Atomic. get t.tail
108- then tombstone t cell
109- else spinlock ~retry: spinlock_iterations t cell
110-
111- and tombstone t cell =
112- if Atomic. compare_and_set cell Empty Tombstone
113- then pop_s t
114- else begin match Atomic. get cell with
115- | (Value v ) as old ->
116- assert (Atomic. compare_and_set cell old Tombstone ) ;
117- Pop v
118- | _ -> assert false
119- end
120-
121- and spinlock ~retry t cell =
122- match Atomic. get cell with
123- | (Value v ) as old ->
124- assert (Atomic. compare_and_set cell old Tombstone ) ;
111+ else if mark t i
112+ then pop_s ~dummy t
113+ else begin
114+ let v = t.buffer.(i) in
115+ t.buffer.(i) < - dummy ;
125116 Pop v
126- | Empty when retry < = 0 ->
127- tombstone t cell
128- | Empty ->
129- Domain. cpu_relax () ;
130- spinlock ~retry: (retry - 1 ) t cell
131- | Tombstone ->
132- assert false
117+ end
133118
134119let rec pop t =
135120 let first = Atomic. get t.first in
136- match pop_s first with
121+ match pop_s ~dummy: t.dummy first with
137122 | Pop v -> Some v
138123 | Wait_for_it -> None
139124 | Is_empty ->
0 commit comments