@@ -18,24 +18,33 @@ open Lwt.Infix
1818
1919let return = Lwt. return
2020
21- type block = Gnt .gntref * Cstruct .t
21+ let max_pages = 256
22+
23+ type block = {
24+ id : Cstruct .uint16 ;
25+ gref : Gnt .gntref ;
26+ data : Cstruct .t ;
27+ }
2228type t = {
2329 grant : Gnt .gntref -> Io_page .t -> unit ;
30+ mutable next_id : Cstruct .uint16 ;
2431 mutable blocks : block list ;
2532 mutable in_use : int ;
2633 mutable shutdown : bool ;
34+ avail : unit Lwt_condition .t ; (* Fires when free list becomes non-empty *)
2735}
2836
2937let page_size = Io_page. round_to_page_size 1
3038let block_size = page_size / 2
3139
32- let make grant = { grant; blocks = [] ; shutdown = false ; in_use = 0 }
40+ let make grant = { next_id = 0 ; grant; blocks = [] ; shutdown = false ; in_use = 0 ; avail = Lwt_condition. create () }
3341
3442let shutdown t =
3543 t.shutdown < - true ;
44+ Lwt_condition. broadcast t.avail () ; (* Wake anyone who's still waiting for free pages *)
3645 if t.in_use = 0 then (
37- t.blocks |> List. iter (fun ( gref , block ) ->
38- if block .Cstruct. off = 0 then (
46+ t.blocks |> List. iter (fun { id = _ ; gref; data} ->
47+ if data .Cstruct. off = 0 then (
3948 Gnt.Gntshr. end_access gref;
4049 Gnt.Gntshr. put gref;
4150 )
@@ -52,33 +61,44 @@ let alloc t =
5261 return (gnt, Io_page. to_cstruct page)
5362
5463let put t block =
64+ let was_empty = (t.blocks = [] ) in
5565 t.blocks < - block :: t.blocks;
5666 t.in_use < - t.in_use - 1 ;
67+ if was_empty then Lwt_condition. broadcast t.avail () ;
5768 if t.in_use = 0 && t.shutdown then shutdown t
5869
59- let use t fn =
70+ let use_block t fn block =
71+ let {id; gref; data} = block in
72+ t.in_use < - t.in_use + 1 ;
73+ Lwt. try_bind
74+ (fun () -> fn ~id gref data)
75+ (fun (_ , release as result ) ->
76+ Lwt. on_termination release (fun () -> put t block);
77+ return result
78+ )
79+ (fun ex -> put t block; Lwt. fail ex)
80+
81+ let rec use t fn =
6082 if t.shutdown then
6183 failwith " Shared_page_pool.use after shutdown" ;
62- begin match t.blocks with
84+ match t.blocks with
85+ | [] when t.next_id > = max_pages ->
86+ MProf.Trace. label " Shared_page_pool waiting for free" ;
87+ Lwt_condition. wait t.avail >> = fun () -> use t fn
6388 | [] ->
6489 (* Frames normally fit within 2048 bytes, so we split each page in half. *)
65- alloc t >> = fun (gntref , page ) ->
90+ alloc t >> = fun (gref , page ) ->
6691 let b1 = Cstruct. sub page 0 block_size in
6792 let b2 = Cstruct. shift page block_size in
68- t.blocks < - (gntref, b2) :: t.blocks;
69- return (gntref, b1)
93+ let id1 = t.next_id in
94+ let id2 = t.next_id + 1 in
95+ t.next_id < - t.next_id + 2 ;
96+ t.blocks < - {id = id2; gref; data = b2} :: t.blocks;
97+ Lwt_condition. broadcast t.avail () ;
98+ use_block t fn {id = id1; gref; data = b1}
7099 | hd :: tl ->
71100 t.blocks < - tl;
72- return hd
73- end >> = fun (gntref , block as grant ) ->
74- t.in_use < - t.in_use + 1 ;
75- Lwt. try_bind
76- (fun () -> fn gntref block)
77- (fun (_ , release as result ) ->
78- Lwt. on_termination release (fun () -> put t grant);
79- return result
80- )
81- (fun ex -> put t grant; Lwt. fail ex)
101+ use_block t fn hd
82102
83103let blocks_needed bytes =
84104 (bytes + block_size - 1 ) / block_size
0 commit comments