diff --git a/src/unix/lwt_unix.ml b/src/unix/lwt_unix.ml index 1a0351b72a..a40fc9d0f2 100644 --- a/src/unix/lwt_unix.ml +++ b/src/unix/lwt_unix.ml @@ -723,6 +723,15 @@ struct in io_vectors.prefix <- loop count io_vectors.prefix + let is_empty io_vectors = + _flatten io_vectors; + let rec loop = function + | [] -> true + | {length = 0; _}::rest -> loop rest + | _ -> false + in + loop io_vectors.prefix + external _stub_iov_max : unit -> int = "lwt_unix_iov_max" let system_limit = diff --git a/src/unix/lwt_unix.mli b/src/unix/lwt_unix.mli index b4b5cbaa4c..a1890abf94 100644 --- a/src/unix/lwt_unix.mli +++ b/src/unix/lwt_unix.mli @@ -415,6 +415,10 @@ sig (** [drop vs n] adjusts the I/O vector sequence [vs] so that it no longer includes its first [n] bytes. *) + val is_empty : t -> bool + (** [is_empty vs] is [true] if and only if [vs] has no I/O vectors, or all I/O + vectors in [vs] have zero bytes. *) + val system_limit : int option (** Some systems limit the number of I/O vectors that can be passed in a single call to their [writev] or [readv] system calls. On those systems, @@ -445,7 +449,15 @@ val writev : file_descr -> IO_vectors.t -> int Lwt.t underlying [writev] system call. Not implemented on Windows. It should be possible to implement, upon - request, for Windows sockets only. *) + request, for Windows sockets only. + + The behavior of [writev] when [vs] has zero slices depends on the system, + and may change in future versions of Lwt. On Linux, [writev] will succeed + and write zero bytes. On BSD (including macOS), [writev] will fail with + [Unix.Unix_error (Unix.EINVAL, "writev", ...)]. + + See {{:http://man7.org/linux/man-pages/man3/writev.3p.html} + [writev(3p)]}. *) val readable : file_descr -> bool (** Returns whether the given file descriptor is currently diff --git a/tests/unix/test_lwt_unix.ml b/tests/unix/test_lwt_unix.ml index 5b69dd4ce1..d143ee0920 100644 --- a/tests/unix/test_lwt_unix.ml +++ b/tests/unix/test_lwt_unix.ml @@ -229,8 +229,8 @@ let writev_tests = Lwt.return (bytes_written = data_length && blocking_matches) in - let reader read_fd expected_data = fun () -> - if expected_data = "" then + let reader read_fd ?(not_readable = false) expected_data = fun () -> + if not_readable then let readable = Lwt_unix.readable read_fd in Lwt_unix.close read_fd >>= fun () -> Lwt.return (not readable) @@ -285,7 +285,7 @@ let writev_tests = [writer write_fd io_vectors 4; reader read_fd "ooar"]); - test "writev: drop" ~only_if:(fun () -> not Sys.win32) + test "writev: drop, is_empty" ~only_if:(fun () -> not Sys.win32) (fun () -> let io_vectors = make_io_vectors @@ -296,11 +296,46 @@ let writev_tests = let read_fd, write_fd = Lwt_unix.pipe () in + let initially_empty = Lwt_unix.IO_vectors.is_empty io_vectors in + Lwt_unix.IO_vectors.drop io_vectors 4; + let empty_after_partial_drop = + Lwt_unix.IO_vectors.is_empty io_vectors in Lwt_list.for_all_s (fun t -> t ()) [writer write_fd io_vectors 5; - reader read_fd "arbaz"]); + reader read_fd "arbaz"] >>= fun io_correct -> + + Lwt_unix.IO_vectors.drop io_vectors 5; + let empty_after_exact_drop = Lwt_unix.IO_vectors.is_empty io_vectors in + + Lwt_unix.IO_vectors.drop io_vectors 100; + let empty_after_excess_drop = Lwt_unix.IO_vectors.is_empty io_vectors in + + Lwt.return + (not initially_empty && + not empty_after_partial_drop && + io_correct && + empty_after_exact_drop && + empty_after_excess_drop)); + + test "writev: degenerate vectors" ~only_if:(fun () -> not Sys.win32) + (fun () -> + let io_vectors = + make_io_vectors + [`Bytes ("foo", 0, 0); + `Bigarray ("bar", 0, 0)] + in + + let read_fd, write_fd = Lwt_unix.pipe () in + + let initially_empty = Lwt_unix.IO_vectors.is_empty io_vectors in + + Lwt_list.for_all_s (fun t -> t ()) + [writer write_fd io_vectors 0; + reader read_fd ""] >>= fun io_correct -> + + Lwt.return (initially_empty && io_correct)); test "writev: bad iovec" ~only_if:(fun () -> not Sys.win32) (fun () -> @@ -335,7 +370,7 @@ let writev_tests = writer negative_offset'; writer negative_length'; writer out_of_bounds'; - reader read_fd ""; + reader read_fd ~not_readable:true ""; close write_fd]); test "writev: iovecs exceeding limit" @@ -375,7 +410,11 @@ let writev_tests = Lwt_list.for_all_s (fun t -> t ()) [writer write_fd io_vectors 3; - reader read_fd "foo"]); + reader read_fd "foo"] >>= fun io_correct -> + + Lwt.return + (io_correct && + not (Lwt_unix.IO_vectors.is_empty io_vectors))); ] let suite =