Source file thread_table.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
type 'v bucket = Nil | Cons of int * 'v * 'v bucket

type 'v t = {
  mutable rehash : int;
  mutable buckets : 'v bucket array;
  mutable length : int;
}

let[@tail_mod_cons] rec remove_first removed k' = function
  | Nil -> Nil
  | Cons (k, v, kvs) ->
      if k == k' then begin
        removed := true;
        kvs
      end
      else Cons (k, v, remove_first removed k' kvs)

let[@inline] remove_first removed k' = function
  | Nil -> Nil
  | Cons (k, v, kvs) ->
      if k == k' then begin
        removed := true;
        kvs
      end
      else Cons (k, v, remove_first removed k' kvs)

let rec find k' = function
  | Nil -> raise_notrace Not_found
  | Cons (k, v, kvs) -> if k == k' then v else find k' kvs

let[@tail_mod_cons] rec filter bit chk = function
  | Nil -> Nil
  | Cons (k, v, kvs) ->
      if Mix.int k land bit = chk then Cons (k, v, filter bit chk kvs)
      else filter bit chk kvs

let[@inline] filter bit chk = function
  | Nil -> Nil
  | Cons (k, _, Nil) as kvs -> if Mix.int k land bit = chk then kvs else Nil
  | Cons (k, v, kvs) ->
      if Mix.int k land bit = chk then Cons (k, v, filter bit chk kvs)
      else filter bit chk kvs

let[@tail_mod_cons] rec append kvs tail =
  match kvs with Nil -> tail | Cons (k, v, kvs) -> Cons (k, v, append kvs tail)

let[@inline] append kvs tail =
  match kvs with Nil -> tail | Cons (k, v, kvs) -> Cons (k, v, append kvs tail)

let min_buckets = 4
and max_buckets_div_2 = (Sys.max_array_length + 1) asr 1

let create () = { rehash = 0; buckets = Array.make min_buckets Nil; length = 0 }
let length t = t.length

let find t k' =
  let h = Mix.int k' in
  let buckets = t.buckets in
  let n = Array.length buckets in
  let i = h land (n - 1) in
  find k' (Array.unsafe_get buckets i)

(* Below we use [@poll error] and [@inline never] to ensure that there are no
   safe-points where thread switches might occur during critical sections. *)

let[@poll error] [@inline never] update_buckets_atomically t old_buckets
    new_buckets =
  t.buckets == old_buckets
  && begin
       t.buckets <- new_buckets;
       t.rehash <- 0;
       true
     end

let rec maybe_rehash t =
  let old_buckets = t.buckets in
  let new_n = t.rehash in
  if new_n <> 0 then
    let old_n = Array.length old_buckets in
    let new_buckets = Array.make new_n Nil in
    if old_n * 2 = new_n then
      let new_bit = new_n lsr 1 in
      let rec loop i =
        if t.buckets == old_buckets then
          if old_n <= i then begin
            if not (update_buckets_atomically t old_buckets new_buckets) then
              maybe_rehash t
          end
          else begin
            let kvs = Array.unsafe_get old_buckets i in
            Array.unsafe_set new_buckets i (filter new_bit 0 kvs);
            Array.unsafe_set new_buckets (i lor new_bit)
              (filter new_bit new_bit kvs);
            loop (i + 1)
          end
        else maybe_rehash t
      in
      loop 0
    else if old_n = new_n * 2 then
      let old_bit = old_n lsr 1 in
      let rec loop i =
        if t.buckets == old_buckets then
          if new_n <= i then begin
            if not (update_buckets_atomically t old_buckets new_buckets) then
              maybe_rehash t
          end
          else begin
            Array.unsafe_set new_buckets i
              (append
                 (Array.unsafe_get old_buckets (i + old_bit))
                 (Array.unsafe_get old_buckets i));
            loop (i + 1)
          end
        else maybe_rehash t
      in
      loop 0
    else maybe_rehash t

let[@inline] maybe_rehash t = if t.rehash <> 0 then maybe_rehash t

let[@poll error] [@inline never] add_atomically t buckets n i before after =
  t.rehash = 0 && buckets == t.buckets
  && before == Array.unsafe_get buckets i
  && begin
       Array.unsafe_set buckets i after;
       let length = t.length + 1 in
       t.length <- length;
       if n < length && n < max_buckets_div_2 then t.rehash <- n * 2;
       true
     end

let rec add t k' v' =
  let h = Mix.int k' in
  maybe_rehash t;
  let buckets = t.buckets in
  let n = Array.length buckets in
  let i = h land (n - 1) in
  let before = Array.unsafe_get buckets i in
  let after = Cons (k', v', before) in
  if not (add_atomically t buckets n i before after) then add t k' v'

let[@poll error] [@inline never] remove_atomically t buckets n i before after
    removed =
  t.rehash = 0 && buckets == t.buckets
  && before == Array.unsafe_get buckets i
  && ((not !removed)
     || begin
          Array.unsafe_set buckets i after;
          let length = t.length - 1 in
          t.length <- length;
          if length * 4 < n && min_buckets < n then t.rehash <- n asr 1;
          true
        end)

let rec remove t k' =
  let h = Mix.int k' in
  let removed = ref false in
  maybe_rehash t;
  let buckets = t.buckets in
  let n = Array.length buckets in
  let i = h land (n - 1) in
  let before = Array.unsafe_get buckets i in
  let after = remove_first removed k' before in
  if not (remove_atomically t buckets n i before after removed) then remove t k'