Skip to content

Commit 52b4555

Browse files
committed
Unix.time_limited_{read,write}: replace select with Polly
'select' has a hardcoded limit of 1024 file descriptors. Signed-off-by: Edwin Török <[email protected]>
1 parent 194ed37 commit 52b4555

File tree

4 files changed

+39
-13
lines changed

4 files changed

+39
-13
lines changed

dune-project

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
(fd-send-recv (>= 2.0.0))
9393
(odoc :with-doc)
9494
(xapi-stdext-pervasives (= :version))
95+
polly
9596
)
9697
)
9798

lib/xapi-stdext-unix/dune

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
(public_name xapi-stdext-unix)
44
(libraries
55
fd-send-recv
6+
polly
67
unix
78
xapi-stdext-pervasives)
89
(foreign_stubs

lib/xapi-stdext-unix/unixext.ml

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -501,21 +501,45 @@ let really_write_string fd string =
501501

502502
exception Timeout
503503

504+
let to_milliseconds ms = ms *. 1000. |> int_of_float
505+
506+
(* Allocating a new polly and waiting like this results in at least 3 system calls.
507+
An alternative for sockets would be to use [setsockopt], but that would need 3 system calls too:
508+
[fstat] to check that it is not a pipe (you'd risk getting stuck forever without [select/poll/epoll] there)
509+
[setsockopt_float] to set the timeout
510+
[clear_nonblock] to ensure the socket is non-blocking
511+
*)
512+
let with_polly kind fd f =
513+
let polly = Polly.create () in
514+
let finally () = Polly.close polly in
515+
Xapi_stdext_pervasives.Pervasiveext.finally
516+
(fun () ->
517+
Polly.add polly fd kind;
518+
let wait remaining_time =
519+
if remaining_time < 0. then raise Timeout;
520+
(* allow a timeout of 0 to check for current state without waiting *)
521+
let ready = Polly.wait polly 1 (to_milliseconds remaining_time) @@ fun _ event_on_fd _ ->
522+
assert (event_on_fd == fd);
523+
in
524+
if ready = 0 then raise Timeout
525+
in
526+
f wait fd
527+
) finally
528+
504529
(* Write as many bytes to a file descriptor as possible from data before a given clock time. *)
505530
(* Raises Timeout exception if the number of bytes written is less than the specified length. *)
506531
(* Writes into the file descriptor at the current cursor position. *)
507532
let time_limited_write_internal (write : Unix.file_descr -> 'a -> int -> int -> int) filedesc length data target_response_time =
533+
with_polly Polly.Events.out filedesc @@ fun wait filedesc ->
508534
let total_bytes_to_write = length in
509535
let bytes_written = ref 0 in
510536
let now = ref (Unix.gettimeofday()) in
511537
while !bytes_written < total_bytes_to_write && !now < target_response_time do
512538
let remaining_time = target_response_time -. !now in
513-
let (_, ready_to_write, _) = Unix.select [] [filedesc] [] remaining_time in (* Note: there is a possibility that the storage could go away after the select and before the write, so the write would block. *)
514-
if List.mem filedesc ready_to_write then begin
515-
let bytes_to_write = total_bytes_to_write - !bytes_written in
516-
let bytes = (try write filedesc data !bytes_written bytes_to_write with Unix.Unix_error(Unix.EAGAIN,_,_) | Unix.Unix_error(Unix.EWOULDBLOCK,_,_) -> 0) in (* write from buffer=data from offset=bytes_written, length=bytes_to_write *)
517-
bytes_written := bytes + !bytes_written;
518-
end;
539+
wait remaining_time;
540+
let bytes_to_write = total_bytes_to_write - !bytes_written in
541+
let bytes = (try write filedesc data !bytes_written bytes_to_write with Unix.Unix_error(Unix.EAGAIN,_,_) | Unix.Unix_error(Unix.EWOULDBLOCK,_,_) -> 0) in (* write from buffer=data from offset=bytes_written, length=bytes_to_write *)
542+
bytes_written := bytes + !bytes_written;
519543
now := Unix.gettimeofday()
520544
done;
521545
if !bytes_written = total_bytes_to_write then () else (* we ran out of time *) raise Timeout
@@ -531,19 +555,18 @@ let time_limited_write_substring filedesc length data target_response_time =
531555
(* Raises Timeout exception if the number of bytes read is less than the desired number. *)
532556
(* Reads from the file descriptor at the current cursor position. *)
533557
let time_limited_read filedesc length target_response_time =
558+
with_polly Polly.Events.inp filedesc @@ fun wait filedesc ->
534559
let total_bytes_to_read = length in
535560
let bytes_read = ref 0 in
536561
let buf = Bytes.make total_bytes_to_read '\000' in
537562
let now = ref (Unix.gettimeofday()) in
538563
while !bytes_read < total_bytes_to_read && !now < target_response_time do
539564
let remaining_time = target_response_time -. !now in
540-
let (ready_to_read, _, _) = Unix.select [filedesc] [] [] remaining_time in
541-
if List.mem filedesc ready_to_read then begin
542-
let bytes_to_read = total_bytes_to_read - !bytes_read in
543-
let bytes = (try Unix.read filedesc buf !bytes_read bytes_to_read with Unix.Unix_error(Unix.EAGAIN,_,_) | Unix.Unix_error(Unix.EWOULDBLOCK,_,_) -> 0) in (* read into buffer=buf from offset=bytes_read, length=bytes_to_read *)
544-
if bytes = 0 then raise End_of_file (* End of file has been reached *)
545-
else bytes_read := bytes + !bytes_read
546-
end;
565+
wait remaining_time;
566+
let bytes_to_read = total_bytes_to_read - !bytes_read in
567+
let bytes = (try Unix.read filedesc buf !bytes_read bytes_to_read with Unix.Unix_error(Unix.EAGAIN,_,_) | Unix.Unix_error(Unix.EWOULDBLOCK,_,_) -> 0) in (* read into buffer=buf from offset=bytes_read, length=bytes_to_read *)
568+
if bytes = 0 then raise End_of_file (* End of file has been reached *)
569+
else bytes_read := bytes + !bytes_read;
547570
now := Unix.gettimeofday()
548571
done;
549572
if !bytes_read = total_bytes_to_read then (Bytes.unsafe_to_string buf) else (* we ran out of time *) raise Timeout

xapi-stdext-unix.opam

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ depends: [
1313
"fd-send-recv" {>= "2.0.0"}
1414
"odoc" {with-doc}
1515
"xapi-stdext-pervasives" {= version}
16+
"polly"
1617
]
1718
build: [
1819
["dune" "subst"] {dev}

0 commit comments

Comments
 (0)