@@ -23,37 +23,13 @@ type rate_limit_data = {
2323}
2424[@@ warning "-69" ]
2525
26- type t = {
27- table : (string , rate_limit_data ) Hashtbl .t
28- ; mutable readers : int
29- ; readers_lock : Mutex .t (* protects readers count *)
30- ; table_lock : Mutex .t
31- (* held collectively by readers, exclusively by writers *)
32- }
33-
34- let with_lock = Xapi_stdext_threads.Threadext.Mutex. execute
26+ module StringMap = Map. Make (String )
3527
36- let with_read_lock t f =
37- with_lock t.readers_lock (fun () ->
38- t.readers < - t.readers + 1 ;
39- if t.readers = 1 then Mutex. lock t.table_lock
40- ) ;
41- Fun. protect f ~finally: (fun () ->
42- with_lock t.readers_lock (fun () ->
43- t.readers < - t.readers - 1 ;
44- if t.readers = 0 then Mutex. unlock t.table_lock
45- )
46- )
28+ type t = rate_limit_data StringMap .t Atomic .t
4729
48- let with_write_lock t f = with_lock t.table_lock f
30+ let with_lock = Xapi_stdext_threads.Threadext.Mutex. execute
4931
50- let create () =
51- {
52- table= Hashtbl. create 10
53- ; readers= 0
54- ; readers_lock= Mutex. create ()
55- ; table_lock= Mutex. create ()
56- }
32+ let create () = Atomic. make StringMap. empty
5733
5834(* The worker thread is responsible for calling the callback when the token
5935 amount becomes available *)
@@ -81,73 +57,70 @@ let rec worker_loop ~bucket ~process_queue ~process_queue_lock
8157(* TODO: Indicate failure reason - did we get invalid config or try to add an
8258 already present user_agent? *)
8359let add_bucket t ~user_agent ~burst_size ~fill_rate =
84- with_write_lock t (fun () ->
85- if Hashtbl. mem t.table user_agent then
60+ let map = Atomic. get t in
61+ if StringMap. mem user_agent map then
62+ false
63+ else
64+ match Token_bucket. create ~burst_size ~fill_rate with
65+ | Some bucket ->
66+ let process_queue = Queue. create () in
67+ let process_queue_lock = Mutex. create () in
68+ let worker_thread_cond = Condition. create () in
69+ let should_terminate = ref false in
70+ let worker_thread =
71+ Thread. create
72+ (fun () ->
73+ worker_loop ~bucket ~process_queue ~process_queue_lock
74+ ~worker_thread_cond ~should_terminate
75+ )
76+ ()
77+ in
78+ let data =
79+ {
80+ bucket
81+ ; process_queue
82+ ; process_queue_lock
83+ ; worker_thread_cond
84+ ; should_terminate
85+ ; worker_thread
86+ }
87+ in
88+ let updated_map = StringMap. add user_agent data map in
89+ Atomic. set t updated_map ; true
90+ | None ->
8691 false
87- else
88- match Token_bucket. create ~burst_size ~fill_rate with
89- | Some bucket ->
90- let process_queue = Queue. create () in
91- let process_queue_lock = Mutex. create () in
92- let worker_thread_cond = Condition. create () in
93- let should_terminate = ref false in
94- let worker_thread =
95- Thread. create
96- (fun () ->
97- worker_loop ~bucket ~process_queue ~process_queue_lock
98- ~worker_thread_cond ~should_terminate
99- )
100- ()
101- in
102- let data =
103- {
104- bucket
105- ; process_queue
106- ; process_queue_lock
107- ; worker_thread_cond
108- ; should_terminate
109- ; worker_thread
110- }
111- in
112- Hashtbl. add t.table user_agent data ;
113- true
114- | None ->
115- false
116- )
11792
11893let delete_bucket t ~user_agent =
119- with_write_lock t (fun () ->
120- match Hashtbl. find_opt t.table user_agent with
121- | None ->
122- ()
123- | Some data ->
124- Mutex. lock data.process_queue_lock ;
125- data.should_terminate := true ;
126- Condition. signal data.worker_thread_cond ;
127- Mutex. unlock data.process_queue_lock ;
128- Hashtbl. remove t.table user_agent
129- )
94+ let map = Atomic. get t in
95+ match StringMap. find_opt user_agent map with
96+ | None ->
97+ ()
98+ | Some data ->
99+ Mutex. lock data.process_queue_lock ;
100+ data.should_terminate := true ;
101+ Condition. signal data.worker_thread_cond ;
102+ Mutex. unlock data.process_queue_lock ;
103+ Atomic. set t (StringMap. remove user_agent map)
130104
131105let try_consume t ~user_agent amount =
132- with_read_lock t (fun () ->
133- match Hashtbl. find_opt t.table user_agent with
134- | None ->
135- false
136- | Some data ->
137- Token_bucket. consume data.bucket amount
138- )
106+ let map = Atomic. get t in
107+ match StringMap. find_opt user_agent map with
108+ | None ->
109+ false
110+ | Some data ->
111+ Token_bucket. consume data.bucket amount
139112
140113let peek t ~user_agent =
141- with_read_lock t (fun () ->
142- Option. map
143- (fun contents -> Token_bucket. peek contents.bucket)
144- (Hashtbl. find_opt t.table user_agent)
145- )
114+ let map = Atomic. get t in
115+ Option. map
116+ (fun contents -> Token_bucket. peek contents.bucket)
117+ (StringMap. find_opt user_agent map)
146118
147119(* The callback should return quickly - if it is a longer task it is
148120 responsible for creating a thread to do the task *)
149121let submit t ~user_agent ~callback amount =
150- match with_read_lock t (fun () -> Hashtbl. find_opt t.table user_agent) with
122+ let map = Atomic. get t in
123+ match StringMap. find_opt user_agent map with
151124 | None ->
152125 callback ()
153126 | Some {bucket; process_queue; process_queue_lock; worker_thread_cond; _} ->
0 commit comments