From 6c14ffd68be586e48fa4293df76338db32a28c12 Mon Sep 17 00:00:00 2001 From: David Allsopp Date: Tue, 30 Sep 2025 15:17:46 +0100 Subject: [PATCH 1/2] Use proposed Unix.fdopen --- README.md | 2 +- tests/main.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2888173c..79828d9c 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ with the same meaning as the return code from the corresponding system call (`op ```ocaml # let fd = if result < 0 then failwith ("Error: " ^ string_of_int result); - (Obj.magic result : Unix.file_descr);; + Unix.fdopen result;; val fd : Unix.file_descr = ``` diff --git a/tests/main.md b/tests/main.md index 51da6ac5..f1e0719a 100644 --- a/tests/main.md +++ b/tests/main.md @@ -137,7 +137,7 @@ val t : [ `Open ] Uring.t = # let token, fd = let token, fd = consume t in assert (fd >= 0); - token, (Obj.magic fd : Unix.file_descr);; + token, Unix.fdopen fd;; val token : [ `Open ] = `Open val fd : Unix.file_descr = @@ -173,7 +173,7 @@ val t : [ `Create ] Uring.t = # let token, fd = let token, fd = consume t in assert (fd >= 0); - token, (Obj.magic fd : Unix.file_descr);; + token, Unix.fdopen fd;; val token : [ `Create ] = `Create val fd : Unix.file_descr = @@ -255,7 +255,7 @@ Now using `~fd`: # let token, fd = let token, fd = consume t in assert (fd >= 0); - token, (Obj.magic fd : Unix.file_descr);; + token, Unix.fdopen fd;; val token : [ `Open_path | `Statx ] = `Open_path val fd : Unix.file_descr = @@ -303,7 +303,7 @@ val t : [ `Get_path ] Uring.t = traceln "Submitted %d" (Uring.submit t); let `Get_path, fd = consume t in if fd >= 0 then ( - let fd : Unix.file_descr = Obj.magic fd in + let fd = Unix.fdopen fd in Unix.close fd; traceln "Opened %S OK" path ) else ( From e3ff4750a6c7fba31781d969d375321c78bbe77b Mon Sep 17 00:00:00 2001 From: David Allsopp Date: Tue, 30 Sep 2025 18:18:53 +0100 Subject: [PATCH 2/2] Move Unix-based processing properly into C Which stops the absuse of the proposed Unix.fdopen --- README.md | 5 +++-- lib/uring/uring.ml | 3 +++ lib/uring/uring.mli | 4 ++++ lib/uring/uring_stubs.c | 23 +++++++++++++++++++++++ tests/main.md | 38 ++++++++++++++++++-------------------- 5 files changed, 51 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 79828d9c..f7d1fea6 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,9 @@ with the same meaning as the return code from the corresponding system call (`op ```ocaml # let fd = - if result < 0 then failwith ("Error: " ^ string_of_int result); - Unix.fdopen result;; + match Uring.file_descr_of_result result with + | Error errno -> failwith ("Error: " ^ Unix.error_message errno) + | Ok fd -> fd;; val fd : Unix.file_descr = ``` diff --git a/lib/uring/uring.ml b/lib/uring/uring.ml index ae5124dc..120940be 100644 --- a/lib/uring/uring.ml +++ b/lib/uring/uring.ml @@ -352,6 +352,7 @@ module Uring = struct external wait_cqe : t -> cqe_option = "ocaml_uring_wait_cqe" external wait_cqe_timeout : float -> t -> cqe_option = "ocaml_uring_wait_cqe_timeout" external peek_cqe : t -> cqe_option = "ocaml_uring_peek_cqe" + external file_descr_of_result : int -> (Unix.file_descr, Unix.error) result = "ocaml_uring_file_descr_of_result" external error_of_errno : int -> Unix.error = "ocaml_uring_error_of_errno" external register_eventfd : t -> Unix.file_descr -> unit = "ocaml_uring_register_eventfd" @@ -629,6 +630,8 @@ let get_cqe_nonblocking t = let peek = get_cqe_nonblocking +let file_descr_of_result = Uring.file_descr_of_result + let register_eventfd t fd = check t; Uring.register_eventfd t.uring fd diff --git a/lib/uring/uring.mli b/lib/uring/uring.mli index 55d431bc..ce3a1bce 100644 --- a/lib/uring/uring.mli +++ b/lib/uring/uring.mli @@ -860,6 +860,10 @@ val get_cqe_nonblocking : 'a t -> 'a completion_option val peek : 'a t -> 'a completion_option [@@deprecated "Renamed to Uring.get_cqe_nonblocking"] +val file_descr_of_result : int -> (Unix.file_descr, Unix.error) result +(** Converts the result of syscalls which return either a file_descriptor or + -errno to either a file descriptor or a result. *) + val register_eventfd : 'a t -> Unix.file_descr -> unit (** [register_eventfd t fd] will register an eventfd to the the uring [t]. diff --git a/lib/uring/uring_stubs.c b/lib/uring/uring_stubs.c index 3ae351aa..ce91a2e3 100644 --- a/lib/uring/uring_stubs.c +++ b/lib/uring/uring_stubs.c @@ -1031,6 +1031,29 @@ value ocaml_uring_error_of_errno(value v_errno) { return unix_error_of_code(Int_val(v_errno)); } +#ifndef CAML_UNIX_FILE_DESCR_API +#ifdef _WIN32 +#error "Unix-specific treatment of Unix.file_descr" +#else +#define caml_unix_file_descr_of_fd(fd) Val_int(fd) +#endif /* #ifdef _WIN32 */ +#endif /* #ifndef CAML_UNIX_FILE_DESCR_API */ + +value ocaml_uring_file_descr_of_result(value v_result) +{ + CAMLparam0(); + CAMLlocal2(result, val); + if (Int_val(v_result) < 0) { + val = unix_error_of_code(-Int_val(v_result)); + result = caml_alloc_small(1, 1); + } else { + val = caml_unix_file_descr_of_fd(Int_val(v_result)); + result = caml_alloc_small(1, 0); + } + Field(result, 0) = val; + CAMLreturn(result); +} + #define Probe_val(v) (*((struct io_uring_probe **) Data_custom_val(v))) static void finalize_probe(value v) { diff --git a/tests/main.md b/tests/main.md index f1e0719a..339862e6 100644 --- a/tests/main.md +++ b/tests/main.md @@ -21,6 +21,14 @@ let rec consume t = | Some { data; result } -> (data, result) | None -> consume t +let rec consume_fd t = + match Uring.wait ~timeout:1. t with + | None -> consume_fd t + | Some { data; result } -> + match Uring.file_descr_of_result result with + | Ok fd -> data, fd + | Error _ -> assert false + let traceln fmt = Format.printf (fmt ^^ "@.") ``` @@ -134,10 +142,7 @@ val t : [ `Open ] Uring.t = # Uring.submit t;;; - : int = 1 -# let token, fd = - let token, fd = consume t in - assert (fd >= 0); - token, Unix.fdopen fd;; +# let token, fd = consume_fd t;; val token : [ `Open ] = `Open val fd : Unix.file_descr = @@ -170,10 +175,7 @@ val t : [ `Create ] Uring.t = # Uring.submit t;; - : int = 1 -# let token, fd = - let token, fd = consume t in - assert (fd >= 0); - token, Unix.fdopen fd;; +# let token, fd = consume_fd t;; val token : [ `Create ] = `Create val fd : Unix.file_descr = @@ -252,10 +254,7 @@ Now using `~fd`: # Uring.submit t;; - : int = 1 -# let token, fd = - let token, fd = consume t in - assert (fd >= 0); - token, Unix.fdopen fd;; +# let token, fd = consume_fd t;; val token : [ `Open_path | `Statx ] = `Open_path val fd : Unix.file_descr = @@ -301,14 +300,13 @@ val t : [ `Get_path ] Uring.t = path `Get_path)); traceln "Submitted %d" (Uring.submit t); - let `Get_path, fd = consume t in - if fd >= 0 then ( - let fd = Unix.fdopen fd in - Unix.close fd; - traceln "Opened %S OK" path - ) else ( - raise (Unix.Unix_error (Uring.error_of_errno fd, "openat2", path)) - );; + let `Get_path, result = consume t in + match Uring.file_descr_of_result result with + | Ok fd -> + Unix.close fd; + traceln "Opened %S OK" path + | Error errno -> + raise (Unix.Unix_error (errno, "openat2", path));; val get : resolve:Uring.Resolve.t -> string -> unit = # get ~resolve:Uring.Resolve.empty ".";;