@@ -18,24 +18,33 @@ open Lwt.Infix
18
18
19
19
let return = Lwt. return
20
20
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
+ }
22
28
type t = {
23
29
grant : Gnt .gntref -> Io_page .t -> unit ;
30
+ mutable next_id : Cstruct .uint16 ;
24
31
mutable blocks : block list ;
25
32
mutable in_use : int ;
26
33
mutable shutdown : bool ;
34
+ avail : unit Lwt_condition .t ; (* Fires when free list becomes non-empty *)
27
35
}
28
36
29
37
let page_size = Io_page. round_to_page_size 1
30
38
let block_size = page_size / 2
31
39
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 () }
33
41
34
42
let shutdown t =
35
43
t.shutdown < - true ;
44
+ Lwt_condition. broadcast t.avail () ; (* Wake anyone who's still waiting for free pages *)
36
45
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 (
39
48
Gnt.Gntshr. end_access gref;
40
49
Gnt.Gntshr. put gref;
41
50
)
@@ -52,33 +61,44 @@ let alloc t =
52
61
return (gnt, Io_page. to_cstruct page)
53
62
54
63
let put t block =
64
+ let was_empty = (t.blocks = [] ) in
55
65
t.blocks < - block :: t.blocks;
56
66
t.in_use < - t.in_use - 1 ;
67
+ if was_empty then Lwt_condition. broadcast t.avail () ;
57
68
if t.in_use = 0 && t.shutdown then shutdown t
58
69
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 =
60
82
if t.shutdown then
61
83
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
63
88
| [] ->
64
89
(* 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 ) ->
66
91
let b1 = Cstruct. sub page 0 block_size in
67
92
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}
70
99
| hd :: tl ->
71
100
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
82
102
83
103
let blocks_needed bytes =
84
104
(bytes + block_size - 1 ) / block_size
0 commit comments