package eio_posix
Eio implementation for POSIX systems
Install
Dune Dependency
Authors
Maintainers
Sources
eio-0.10.tbz
sha256=390f7814507b8133d6c25e3a67a742d731c7ca66252b287b1fb0e3ad4d10eecc
sha512=9c0c9088b178df9799aaae9deb803a802228f1329cbe452479c90e80a13985d9c364ea86ee14e4e759133940f9f6065c7e8ece509d176fb1e347c5320f00a494
doc/src/eio_posix/low_level.ml.html
Source file low_level.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
open Eio.Std (* There are some things that should be improved here: - Blocking FDs (e.g. stdout) wait for the FD to become ready and then do a blocking operation. This might not succeed, and will block the whole domain in that case. Ideally, all blocking operations should happen in a sys-thread instead. - Various other operations, such as listing a directory, should also be done in a sys-thread to avoid high latencies in the main domain. *) type ty = Read | Write module Fd = Fd (* todo: keeping a pool of workers is probably faster *) let in_worker_thread = Eio_unix.run_in_systhread let await_readable fd = Fd.use_exn "await_readable" fd @@ fun fd -> Sched.enter @@ fun t k -> Sched.await_readable t k fd let await_writable fd = Fd.use_exn "await_writable" fd @@ fun fd -> Sched.enter @@ fun t k -> Sched.await_writable t k fd let rec do_nonblocking ty fn fd = Fiber.yield (); try fn fd with | Unix.Unix_error (EINTR, _, _) -> do_nonblocking ty fn fd (* Just in case *) | Unix.Unix_error((EAGAIN | EWOULDBLOCK), _, _) -> Sched.enter (fun t k -> match ty with | Read -> Sched.await_readable t k fd | Write -> Sched.await_writable t k fd ); do_nonblocking ty fn fd let read fd buf start len = if Fd.is_blocking fd then await_readable fd; Fd.use_exn "read" fd @@ fun fd -> do_nonblocking Read (fun fd -> Unix.read fd buf start len) fd let write fd buf start len = if Fd.is_blocking fd then await_writable fd; Fd.use_exn "write" fd @@ fun fd -> do_nonblocking Write (fun fd -> Unix.write fd buf start len) fd let sleep_until time = Sched.enter @@ fun t k -> Sched.await_timeout t k time let socket ~sw socket_domain socket_type protocol = Switch.check sw; let sock_unix = Unix.socket ~cloexec:true socket_domain socket_type protocol in Unix.set_nonblock sock_unix; Fd.of_unix ~sw ~blocking:false ~close_unix:true sock_unix let connect fd addr = try Fd.use_exn "connect" fd (fun fd -> Unix.connect fd addr) with | Unix.Unix_error ((EINTR | EAGAIN | EWOULDBLOCK | EINPROGRESS), _, _) -> await_writable fd; match Fd.use_exn "connect" fd Unix.getsockopt_error with | None -> () | Some code -> raise (Err.wrap code "connect-in-progress" "") let accept ~sw sock = Fd.use_exn "accept" sock @@ fun sock -> let client, addr = do_nonblocking Read (fun fd -> Switch.check sw; Unix.accept ~cloexec:true fd) sock in Unix.set_nonblock client; Fd.of_unix ~sw ~blocking:false ~close_unix:true client, addr let shutdown sock cmd = Fd.use_exn "shutdown" sock (fun fd -> Unix.shutdown fd cmd) external eio_send_msg : Unix.file_descr -> Unix.sockaddr option -> Cstruct.t array -> int = "caml_eio_posix_send_msg" external eio_recv_msg : Unix.file_descr -> Cstruct.t array -> Unix.sockaddr * int = "caml_eio_posix_recv_msg" let send_msg fd ?dst buf = Fd.use_exn "send_msg" fd @@ fun fd -> do_nonblocking Write (fun fd -> eio_send_msg fd dst buf) fd let recv_msg fd buf = Fd.use_exn "recv_msg" fd @@ fun fd -> do_nonblocking Read (fun fd -> eio_recv_msg fd buf) fd external eio_getrandom : Cstruct.buffer -> int -> int -> int = "caml_eio_posix_getrandom" let getrandom { Cstruct.buffer; off; len } = let rec loop n = if n = len then () else loop (n + eio_getrandom buffer (off + n) (len - n)) in in_worker_thread @@ fun () -> loop 0 let fstat fd = Fd.use_exn "fstat" fd Unix.LargeFile.fstat let lstat path = in_worker_thread @@ fun () -> Unix.LargeFile.lstat path let realpath path = in_worker_thread @@ fun () -> Unix.realpath path let read_entries h = let rec aux acc = match Unix.readdir h with | "." | ".." -> aux acc | leaf -> aux (leaf :: acc) | exception End_of_file -> Array.of_list acc in aux [] let readdir path = in_worker_thread @@ fun () -> let h = Unix.opendir path in match read_entries h with | r -> Unix.closedir h; r | exception ex -> let bt = Printexc.get_raw_backtrace () in Unix.closedir h; Printexc.raise_with_backtrace ex bt external eio_readv : Unix.file_descr -> Cstruct.t array -> int = "caml_eio_posix_readv" external eio_writev : Unix.file_descr -> Cstruct.t array -> int = "caml_eio_posix_writev" external eio_preadv : Unix.file_descr -> Cstruct.t array -> Optint.Int63.t -> int = "caml_eio_posix_preadv" external eio_pwritev : Unix.file_descr -> Cstruct.t array -> Optint.Int63.t -> int = "caml_eio_posix_pwritev" let readv fd bufs = if Fd.is_blocking fd then await_readable fd; Fd.use_exn "readv" fd @@ fun fd -> do_nonblocking Read (fun fd -> eio_readv fd bufs) fd let writev fd bufs = if Fd.is_blocking fd then await_writable fd; Fd.use_exn "writev" fd @@ fun fd -> do_nonblocking Write (fun fd -> eio_writev fd bufs) fd let preadv ~file_offset fd bufs = if Fd.is_blocking fd then await_readable fd; Fd.use_exn "preadv" fd @@ fun fd -> do_nonblocking Read (fun fd -> eio_preadv fd bufs file_offset) fd let pwritev ~file_offset fd bufs = if Fd.is_blocking fd then await_writable fd; Fd.use_exn "pwritev" fd @@ fun fd -> do_nonblocking Write (fun fd -> eio_pwritev fd bufs file_offset) fd module Open_flags = struct type t = int let rdonly = Config.o_rdonly let rdwr = Config.o_rdwr let wronly = Config.o_wronly let append = Config.o_append let cloexec = Config.o_cloexec let creat = Config.o_creat let directory = Config.o_directory let dsync = Config.o_dsync let excl = Config.o_excl let noctty = Config.o_noctty let nofollow = Config.o_nofollow let nonblock = Config.o_nonblock let sync = Config.o_sync let trunc = Config.o_trunc let resolve_beneath = Config.o_resolve_beneath let path = Config.o_path let empty = 0 let ( + ) = ( lor ) end let rec with_dirfd op dirfd fn = match dirfd with | None -> fn (Obj.magic Config.at_fdcwd : Unix.file_descr) | Some dirfd -> Fd.use_exn op dirfd fn | exception Unix.Unix_error(Unix.EINTR, _, "") -> with_dirfd op dirfd fn external eio_openat : Unix.file_descr -> string -> Open_flags.t -> int -> Unix.file_descr = "caml_eio_posix_openat" let openat ?dirfd ~sw ~mode path flags = with_dirfd "openat" dirfd @@ fun dirfd -> Switch.check sw; in_worker_thread (fun () -> eio_openat dirfd path Open_flags.(flags + cloexec + nonblock) mode) |> Fd.of_unix ~sw ~blocking:false ~close_unix:true external eio_mkdirat : Unix.file_descr -> string -> Unix.file_perm -> unit = "caml_eio_posix_mkdirat" let mkdir ?dirfd ~mode path = with_dirfd "mkdirat" dirfd @@ fun dirfd -> in_worker_thread @@ fun () -> eio_mkdirat dirfd path mode external eio_unlinkat : Unix.file_descr -> string -> bool -> unit = "caml_eio_posix_unlinkat" let unlink ?dirfd ~dir path = with_dirfd "unlink" dirfd @@ fun dirfd -> in_worker_thread @@ fun () -> eio_unlinkat dirfd path dir external eio_renameat : Unix.file_descr -> string -> Unix.file_descr -> string -> unit = "caml_eio_posix_renameat" let rename ?old_dir old_path ?new_dir new_path = with_dirfd "rename-old" old_dir @@ fun old_dir -> with_dirfd "rename-new" new_dir @@ fun new_dir -> in_worker_thread @@ fun () -> eio_renameat old_dir old_path new_dir new_path let pipe ~sw = let unix_r, unix_w = Unix.pipe ~cloexec:true () in let r = Fd.of_unix ~sw ~blocking:false ~close_unix:true unix_r in let w = Fd.of_unix ~sw ~blocking:false ~close_unix:true unix_w in Unix.set_nonblock unix_r; Unix.set_nonblock unix_w; r, w module Process = struct type t = { pid : int; exit_status : Unix.process_status Promise.t; } let exit_status t = t.exit_status let pid t = t.pid module Fork_action = Eio_unix.Private.Fork_action (* Read a (typically short) error message from a child process. *) let rec read_response fd = let buf = Bytes.create 256 in match read fd buf 0 (Bytes.length buf) with | 0 -> "" | n -> Bytes.sub_string buf 0 n ^ read_response fd let with_pipe fn = Switch.run @@ fun sw -> let r, w = pipe ~sw in fn r w let signal t signal = (* The lock here ensures we don't signal the PID after reaping it. *) Children.with_lock @@ fun () -> if not (Promise.is_resolved t.exit_status) then ( Unix.kill t.pid signal ) external eio_spawn : Unix.file_descr -> Eio_unix.Private.Fork_action.c_action list -> int = "caml_eio_posix_spawn" let spawn ~sw actions = with_pipe @@ fun errors_r errors_w -> Eio_unix.Private.Fork_action.with_actions actions @@ fun c_actions -> Switch.check sw; let t = (* We take the lock to ensure that the signal handler won't reap the process before we've registered it. *) Children.with_lock (fun () -> let pid = Fd.use_exn "errors-w" errors_w @@ fun errors_w -> eio_spawn errors_w c_actions in Fd.close errors_w; { pid; exit_status = Children.register pid } ) in let hook = Switch.on_release_cancellable sw (fun () -> signal t Sys.sigkill) in (* Removing the hook must be done from our own domain, not from the signal handler, so fork a fiber to deal with that. If the switch gets cancelled then this won't run, but then the [on_release] handler will run the hook soon anyway. *) Fiber.fork_daemon ~sw (fun () -> ignore (Promise.await t.exit_status : Unix.process_status); Switch.remove_hook hook; `Stop_daemon ); (* Check for errors starting the process. *) match read_response errors_r with | "" -> t (* Success! Execing the child closed [errors_w] and we got EOF. *) | err -> failwith err end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>