···130130 (iovec, len, buffers)
131131end
132132133133+(* Used for the sendmsg/recvmsg calls. Liburing doesn't support sendto/recvfrom at the time of writing. *)
134134+module Msghdr = struct
135135+ type msghdr
136136+ type t = msghdr * Sockaddr.t * Iovec.t
137137+ external make_msghdr : Sockaddr.t -> Iovec.t-> msghdr = "ocaml_uring_make_msghdr"
138138+139139+ let get_sockaddr (_, addr, _) = addr
140140+141141+ let create_with_addr addr buffs =
142142+ let iovs = Iovec.make buffs in
143143+ make_msghdr addr iovs, addr, iovs
144144+145145+ let create buffs =
146146+ create_with_addr (Sockaddr.create ()) buffs
147147+end
148148+133149type 'a job = 'a Heap.entry
134150135151module Uring = struct
···157173 external submit_accept : t -> id -> Unix.file_descr -> Sockaddr.t -> bool = "ocaml_uring_submit_accept" [@@noalloc]
158174 external submit_cancel : t -> id -> id -> bool = "ocaml_uring_submit_cancel" [@@noalloc]
159175 external submit_openat2 : t -> id -> Unix.file_descr -> Open_how.t -> bool = "ocaml_uring_submit_openat2" [@@noalloc]
176176+ external submit_send_msg : t -> id -> Unix.file_descr -> Msghdr.t -> bool = "ocaml_uring_submit_send_msg" [@@noalloc]
177177+ external submit_recv_msg : t -> id -> Unix.file_descr -> Msghdr.t -> bool = "ocaml_uring_submit_recv_msg" [@@noalloc]
160178161179 type cqe_option = private
162180 | Cqe_none
···314332315333let accept t fd addr user_data =
316334 with_id_full t (fun id -> Uring.submit_accept t.uring id fd addr) user_data ~extra_data:addr
335335+336336+let send_msg t fd addr buffers user_data =
337337+ let addr = Sockaddr.of_unix addr in
338338+ let msghdr = Msghdr.create_with_addr addr buffers in
339339+ with_id_full t (fun id -> Uring.submit_send_msg t.uring id fd msghdr) user_data ~extra_data:msghdr
340340+341341+let recv_msg t fd msghdr user_data =
342342+ with_id_full t (fun id -> Uring.submit_recv_msg t.uring id fd msghdr) user_data ~extra_data:msghdr
317343318344let cancel t job user_data =
319345 ignore (Heap.ptr job : Uring.id); (* Check it's still valid *)
+22
lib/uring/uring.mli
···199199 if [job] had already completed by the time the kernel processed the cancellation request.
200200 @raise Invalid_argument if the job has already been returned by e.g. {!wait}. *)
201201202202+module Msghdr : sig
203203+ type t
204204+205205+ val create : Cstruct.t list -> t
206206+ (** [create buffs] makes a new [msghdr] using the [buffs]
207207+ for the underlying [iovec]. A dummy socket address is used
208208+ and will be filled when data is received.*)
209209+210210+ val get_sockaddr : t -> Sockaddr.t
211211+ (** [get_sockaddr t] gets the socket address from [t]. When used
212212+ with {!recv_msg} the socket will only be the sender address once the message
213213+ is received, until then the address will be a dummy address. *)
214214+end
215215+216216+val send_msg : 'a t -> Unix.file_descr -> Unix.sockaddr -> Cstruct.t list -> 'a -> 'a job option
217217+(** [send_msg t fd addr buffs d] will submit a [sendmsg(2)] request. The [Msghdr] will be constructed
218218+ from the address ([addr]) and the buffers ([buffs]). *)
219219+220220+val recv_msg : 'a t -> Unix.file_descr -> Msghdr.t -> 'a -> 'a job option
221221+(** [recv_msg t fd msghdr d] will submit a [recvmsg(2)] request. If the request is
222222+ successful then the [msghdr] will contain the sender address and the data sent. *)
223223+202224(** {2 Submitting operations} *)
203225204226val submit : 'a t -> int
+70
lib/uring/uring_stubs.c
···405405 CAMLreturn(v_sockaddr);
406406}
407407408408+#define Msghdr_val(v) (*((struct msghdr **) Data_custom_val(v)))
409409+410410+static void finalize_msghdr(value v) {
411411+ caml_stat_free(Msghdr_val(v));
412412+ Msghdr_val(v) = NULL;
413413+}
414414+415415+static struct custom_operations msghdr_ops = {
416416+ "uring.msghdr_ops",
417417+ finalize_msghdr,
418418+ custom_compare_default,
419419+ custom_hash_default,
420420+ custom_serialize_default,
421421+ custom_deserialize_default,
422422+ custom_compare_ext_default,
423423+ custom_fixed_length_default
424424+};
425425+426426+// v_sockaddr and v_iov must not be freed before the msghdr as it contains pointers to them
427427+value
428428+ocaml_uring_make_msghdr(value v_sockaddr, value v_iov) {
429429+ CAMLparam2(v_sockaddr, v_iov);
430430+ CAMLlocal1(v);
431431+ struct msghdr *msg;
432432+ struct iovec *iovs = Iovec_val(Field(v_iov, 0));
433433+ int iovs_len = Int_val(Field(v_iov, 1));
434434+ // Allocate a pointer on the OCaml heap for the msghdr
435435+ v = caml_alloc_custom_mem(&msghdr_ops, sizeof(struct msghdr *), sizeof(struct msghdr));
436436+ Msghdr_val(v) = NULL;
437437+ msg = (struct msghdr *) caml_stat_alloc(sizeof(struct msghdr));
438438+ // The msghdr must zero-ed to avoid unwanted errors
439439+ memset(msg, 0, sizeof(struct msghdr));
440440+ Msghdr_val(v) = msg;
441441+ struct sock_addr_data *addr = Sock_addr_val(v_sockaddr);
442442+ // Store the address and iovec data in the message
443443+ msg->msg_name = &(addr->sock_addr_addr);
444444+ msg->msg_namelen = sizeof(addr->sock_addr_addr);
445445+ msg->msg_iov = iovs;
446446+ msg->msg_iovlen = iovs_len;
447447+ CAMLreturn(v);
448448+}
449449+408450// v_sockaddr must not be GC'd while the call is in progress
409451value
410452ocaml_uring_submit_connect(value v_uring, value v_id, value v_fd, value v_sockaddr) {
···415457 sqe = io_uring_get_sqe(ring);
416458 if (!sqe) CAMLreturn(Val_false);
417459 io_uring_prep_connect(sqe, Int_val(v_fd), &(addr->sock_addr_addr.s_gen), addr->sock_addr_len);
460460+ io_uring_sqe_set_data(sqe, (void *)Long_val(v_id));
461461+ CAMLreturn(Val_true);
462462+}
463463+464464+// v_msghdr must not be GC'd while the call is in progress
465465+value
466466+ocaml_uring_submit_send_msg(value v_uring, value v_id, value v_fd, value v_msghdr) {
467467+ CAMLparam2(v_uring, v_msghdr);
468468+ struct io_uring *ring = Ring_val(v_uring);
469469+ struct msghdr *msg = Msghdr_val(Field(v_msghdr, 0));
470470+ struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
471471+ if (!sqe) CAMLreturn(Val_false);
472472+ dprintf("submit_sendmsg\n");
473473+ io_uring_prep_sendmsg(sqe, Int_val(v_fd), msg, 0);
474474+ io_uring_sqe_set_data(sqe, (void *)Long_val(v_id));
475475+ CAMLreturn(Val_true);
476476+}
477477+478478+// v_msghdr must not be GC'd while the call is in progress
479479+value
480480+ocaml_uring_submit_recv_msg(value v_uring, value v_id, value v_fd, value v_msghdr) {
481481+ CAMLparam2(v_uring, v_msghdr);
482482+ struct io_uring *ring = Ring_val(v_uring);
483483+ struct msghdr *msg = Msghdr_val(Field(v_msghdr, 0));
484484+ struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
485485+ if (!sqe) CAMLreturn(Val_false);
486486+ dprintf("submit_recvmsg:msghdr %p: registering iobuf base %p len %lu\n", msg, msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len);
487487+ io_uring_prep_recvmsg(sqe, Int_val(v_fd), msg, 0);
418488 io_uring_sqe_set_data(sqe, (void *)Long_val(v_id));
419489 CAMLreturn(Val_true);
420490}