From 085a0dcf28f50cfd4420511a2d027c0377db9735 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Tue, 14 May 2024 12:47:10 +0100 Subject: [PATCH 01/66] eio_linux: don't call submit immediately before wait It's quicker to do it in a single call. --- lib_eio_linux/sched.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib_eio_linux/sched.ml b/lib_eio_linux/sched.ml index b8894eb15..bc4dd8991 100644 --- a/lib_eio_linux/sched.ml +++ b/lib_eio_linux/sched.ml @@ -228,7 +228,6 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] = Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *) handle_complete st ~runnable result | None -> - ignore (submit uring : int); let timeout = match next_due with | `Wait_until time -> @@ -239,6 +238,7 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] = | `Nothing -> None in if not (Lf_queue.is_empty st.run_q) then ( + ignore (submit uring : int); Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *) schedule st ) else if timeout = None && Uring.active_ops uring = 0 then ( @@ -267,6 +267,7 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] = ) else ( (* Someone added a new job while we were setting [need_wakeup] to [true]. They might or might not have seen that, so we can't be sure they'll send an event. *) + ignore (submit uring : int); Atomic.set st.need_wakeup false; Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *) schedule st From e403d8ff7ffd4f6112d57cfd915bfa4dd44ac516 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Tue, 14 May 2024 10:27:20 +0100 Subject: [PATCH 02/66] eio_linux: don't record submit events when there's nothing to submit --- Dockerfile | 4 ++-- bench.Dockerfile | 4 ++-- dune-project | 2 +- eio_linux.opam | 2 +- lib_eio_linux/sched.ml | 5 ++++- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index a7e5e93f0..6e7f5e508 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -FROM ocaml/opam:debian-11-ocaml-5.1 +FROM ocaml/opam:debian-11-ocaml-5.2 # Make sure we're using opam-2.1: RUN sudo ln -sf /usr/bin/opam-2.1 /usr/bin/opam # Ensure opam-repository is up-to-date: -RUN cd opam-repository && git pull -q origin 0ac3fc79fd11ee365dd46119d43e9763cf57da52 && opam update +RUN cd opam-repository && git pull -q origin 97de3378749cf8d2d70a5d710d310e5cc17c9dea && opam update # Install utop for interactive use: RUN opam install utop fmt # Install Eio's dependencies (adding just the opam files first to help with caching): diff --git a/bench.Dockerfile b/bench.Dockerfile index 13edb9c2e..a6bf055bb 100644 --- a/bench.Dockerfile +++ b/bench.Dockerfile @@ -1,8 +1,8 @@ -FROM ocaml/opam:debian-11-ocaml-5.1 +FROM ocaml/opam:debian-11-ocaml-5.2 # Make sure we're using opam-2.1: RUN sudo ln -sf /usr/bin/opam-2.1 /usr/bin/opam # Ensure opam-repository is up-to-date: -RUN cd opam-repository && git pull -q origin 0ac3fc79fd11ee365dd46119d43e9763cf57da52 && opam update +RUN cd opam-repository && git pull -q origin 97de3378749cf8d2d70a5d710d310e5cc17c9dea && opam update # Install Eio's dependencies (adding just the opam files first to help with caching): RUN mkdir eio WORKDIR eio diff --git a/dune-project b/dune-project index 4d295cc44..0a291c02e 100644 --- a/dune-project +++ b/dune-project @@ -38,7 +38,7 @@ (logs (and (>= 0.7.0) :with-test)) (fmt (>= 0.8.9)) (cmdliner (and (>= 1.1.0) :with-test)) - (uring (>= 0.7)))) + (uring (>= 0.9)))) (package (name eio_posix) (allow_empty) ; Work-around for dune bug #6938 diff --git a/eio_linux.opam b/eio_linux.opam index 05b4f4f71..c4ab67004 100644 --- a/eio_linux.opam +++ b/eio_linux.opam @@ -16,7 +16,7 @@ depends: [ "logs" {>= "0.7.0" & with-test} "fmt" {>= "0.8.9"} "cmdliner" {>= "1.1.0" & with-test} - "uring" {>= "0.7"} + "uring" {>= "0.9"} "odoc" {with-doc} ] build: [ diff --git a/lib_eio_linux/sched.ml b/lib_eio_linux/sched.ml index bc4dd8991..723a9faa1 100644 --- a/lib_eio_linux/sched.ml +++ b/lib_eio_linux/sched.ml @@ -113,7 +113,10 @@ let enter op fn = Effect.perform (Enter fn) let submit uring = - Trace.with_span "submit" (fun () -> Uring.submit uring) + if Uring.sqe_ready uring > 0 then + Trace.with_span "submit" (fun () -> Uring.submit uring) + else + 0 let rec enqueue_job t fn = match fn () with From fffcf23801fe8332517e1c17e34c484f63f7d18f Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Tue, 21 May 2024 11:18:23 +0100 Subject: [PATCH 03/66] Add examples/fs showing how to walk a directory tree --- examples/fs/dune | 3 +++ examples/fs/main.ml | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 examples/fs/dune create mode 100644 examples/fs/main.ml diff --git a/examples/fs/dune b/examples/fs/dune new file mode 100644 index 000000000..fb95515c7 --- /dev/null +++ b/examples/fs/dune @@ -0,0 +1,3 @@ +(executable + (name main) + (libraries eio_main)) diff --git a/examples/fs/main.ml b/examples/fs/main.ml new file mode 100644 index 000000000..d91e29d89 --- /dev/null +++ b/examples/fs/main.ml @@ -0,0 +1,21 @@ +(* Walks the directory tree rooted at the current directory, + displaying all directory names (skipping hidden directories and `_build`). *) + +open Eio.Std + +let ( / ) = Eio.Path.( / ) + +let rec scan t = + match Eio.Path.kind ~follow:false t with + | `Directory -> + traceln "Visiting %a" Eio.Path.pp t; + Eio.Path.read_dir t |> List.iter (function + | "_build" -> () + | item when String.starts_with ~prefix:"." item -> () + | item -> scan (t / item) + ) + | _ -> () + +let () = + Eio_main.run @@ fun env -> + scan (Eio.Stdenv.cwd env) From 72bf01bfdfb9f0cdf227e272eee2b8c031d0ef94 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Tue, 21 May 2024 14:54:06 +0100 Subject: [PATCH 04/66] Prepare release --- CHANGES.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 6 +++--- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index abffd096b..1c12dcdb6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,47 @@ +## v1.1 + +New features: + +- Add `Eio.Path.symlink` (@patricoferris #715, reviewed by @talex5). + +- Add `Eio.Pool.use ~never_block` (@SGrondin #657, reviewed by @talex5). + +- Add `Eio.Time.Timeout.sleep` (@talex5 #726). + +Documentation: + +- Add `examples/fs` showing how to walk a directory tree (@talex5 #730). + +- README: explain that `read_all` reads until shutdown (@talex5 #717, reported by @Wenke-D). + +- Use long dash in README title (@lucperkins #718). + +Linux backend: + +- Require Linux >= 5.15 (@talex5 #720, reviewed by @SGrondin and @avsm). + Removes a work-around that required checking whether every flow was a tty. + +- Don't call submit immediately before wait (@talex5 #728). + This is slightly faster and makes the traces clearer. + +- Don't record submit events when there's nothing to submit (@talex5 #729). + Makes the traces a bit clearer. + +- Split flow into its own file (@talex5 #727). + +POSIX backend: + +- Add `_BSD_SOURCE` flag to fix build on OpenBSD (@prgbln #722). + +- Fix sandboxed path resolution on OpenBSD (@jebrosen #723, reviewed by @talex5). + OpenBSD uses `ELOOP` when opening a symlink with `O_NOFOLLOW`. + +Build and test: + +- Benchmarks: record uname, Eio backend, and number of cores (@talex5 #719). + +- Update to MDX 2.4.1 for OCaml 5.2 (@talex5 #712). + ## v1.0 New features: diff --git a/README.md b/README.md index 2511b1e73..cce5024da 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Eio replaces existing concurrency libraries such as Lwt * [Motivation](#motivation) * [Eio packages](#eio-packages) -* [Getting OCaml 5.1](#getting-ocaml-51) +* [Getting OCaml](#getting-ocaml) * [Getting Eio](#getting-eio) * [Running Eio](#running-eio) * [Testing with Mocks](#testing-with-mocks) @@ -92,7 +92,7 @@ See [Awesome Multicore OCaml][] for links to work migrating other projects to Ei - [Eio_main][] selects an appropriate backend (e.g. `eio_linux` or `eio_posix`), depending on your platform. - [Eio_js][] allows Eio code to run in the browser, using `js_of_ocaml`. -## Getting OCaml 5.1 +## Getting OCaml You'll need OCaml 5.1.0 or later. You can either install it yourself or build the included [Dockerfile](./Dockerfile). @@ -104,7 +104,7 @@ To install it yourself: 2. Use opam to install OCaml: ``` - opam switch create 5.1.1 + opam switch create 5.2.0 ``` ## Getting Eio From 68e50f86ffe4b9adecbfcd4cae5654ce70a33427 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Thu, 23 May 2024 11:36:16 +0100 Subject: [PATCH 05/66] eio_linux: add work-around for signals race This is a quick fix for https://github.com/ocaml-multicore/eio/issues/732. --- lib_eio_linux/sched.ml | 9 ++++++++- lib_eio_linux/tests/test.ml | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib_eio_linux/sched.ml b/lib_eio_linux/sched.ml index 723a9faa1..4d5f1a901 100644 --- a/lib_eio_linux/sched.ml +++ b/lib_eio_linux/sched.ml @@ -257,7 +257,14 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] = If [need_wakeup] is still [true], this is fine because we don't promise to do that. If [need_wakeup = false], a wake-up event will arrive and wake us up soon. *) Trace.suspend_domain Begin; - let result = Uring.wait ?timeout uring in + let result = + (* Hack: liburing automatically retries [io_uring_enter] if an + interrupt is received and no timeout is set. However, we need + to return to OCaml mode so any pending signal handlers can + run. See: https://github.com/ocaml-multicore/eio/issues/732 *) + let timeout = Option.value timeout ~default:1e9 in + Uring.wait ~timeout uring + in Trace.suspend_domain End; Atomic.set st.need_wakeup false; Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *) diff --git a/lib_eio_linux/tests/test.ml b/lib_eio_linux/tests/test.ml index 0a5f9c82d..7c7e31aad 100644 --- a/lib_eio_linux/tests/test.ml +++ b/lib_eio_linux/tests/test.ml @@ -130,7 +130,7 @@ let test_read_exact () = let ( / ) = Eio.Path.( / ) in let path = env#cwd / "test.data" in let msg = "hello" in - Eio.Path.save path ("!" ^ msg) ~create:(`Exclusive 0o600); + Eio.Path.save path ("!" ^ msg) ~create:(`Or_truncate 0o600); Switch.run @@ fun sw -> let fd = Eio_linux.Low_level.openat2 ~sw ~access:`R @@ -162,7 +162,7 @@ let test_statx () = Eio_linux.run ~queue_depth:4 @@ fun env -> let ( / ) = Eio.Path.( / ) in let path = env#cwd / "test2.data" in - Eio.Path.with_open_out path ~create:(`Exclusive 0o600) @@ fun file -> + Eio.Path.with_open_out path ~create:(`Or_truncate 0o600) @@ fun file -> Eio.Flow.copy_string "hello" file; let buf = Uring.Statx.create () in let test expected_len ~follow dir path = @@ -198,6 +198,19 @@ let test_statx () = ); () +(* Ensure that an OCaml signal handler will run, even if we're sleeping in liburing at the time. + The problem here is that [__sys_io_uring_enter2] doesn't return EINTR, because it did successfully + submit an item. This causes liburing to retry without giving our OCaml signal handler a chance to run. + Note: we can't run this test with a timeout because liburing does return in that case! *) +let test_signal_race () = + Eio_linux.run @@ fun _env -> + let cond = Eio.Condition.create () in + let handle _ = Eio.Condition.broadcast cond in + Sys.(set_signal sigalrm) (Signal_handle handle); + Fiber.both + (fun () -> Eio.Condition.await_no_mutex cond) + (fun () -> ignore (Unix.setitimer ITIMER_REAL { it_interval = 0.; it_value = 0.001 } : Unix.interval_timer_status)) + let () = let open Alcotest in run "eio_linux" [ @@ -211,5 +224,6 @@ let () = test_case "read_exact" `Quick test_read_exact; test_case "expose_backend" `Quick test_expose_backend; test_case "statx" `Quick test_statx; + test_case "signal_race" `Quick test_signal_race; ]; ] From 877af2ef3272b4b79aa62e5cc7ce7a81fa86e4a2 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Wed, 22 May 2024 10:54:07 +0200 Subject: [PATCH 06/66] Add Eio_unix.Net.import_socket_listening --- lib_eio/unix/net.ml | 5 +++++ lib_eio/unix/net.mli | 13 +++++++++++-- lib_eio_linux/eio_linux.ml | 4 ++++ lib_eio_posix/domain_mgr.ml | 5 +++++ lib_eio_windows/domain_mgr.ml | 5 +++++ tests/network.md | 23 +++++++++++++++++++++++ 6 files changed, 53 insertions(+), 2 deletions(-) diff --git a/lib_eio/unix/net.ml b/lib_eio/unix/net.ml index d32dabe3a..4a0fa298b 100644 --- a/lib_eio/unix/net.ml +++ b/lib_eio/unix/net.ml @@ -61,6 +61,7 @@ type t = [`Generic | `Unix] Eio.Net.ty r type _ Effect.t += | Import_socket_stream : Switch.t * bool * Unix.file_descr -> [`Unix_fd | stream_socket_ty] r Effect.t + | Import_socket_listening : Switch.t * bool * Unix.file_descr -> [`Unix_fd | listening_socket_ty] r Effect.t | Import_socket_datagram : Switch.t * bool * Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r Effect.t | Socketpair_stream : Switch.t * Unix.socket_domain * int -> ([`Unix_fd | stream_socket_ty] r * [`Unix_fd | stream_socket_ty] r) Effect.t @@ -68,11 +69,15 @@ type _ Effect.t += ([`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r) Effect.t let open_stream s = (s : _ stream_socket :> [< `Unix_fd | stream_socket_ty] r) +let open_listening s = (s : _ listening_socket :> [< `Unix_fd | listening_socket_ty] r) let open_datagram s = (s : _ datagram_socket :> [< `Unix_fd | datagram_socket_ty] r) let import_socket_stream ~sw ~close_unix fd = open_stream @@ Effect.perform (Import_socket_stream (sw, close_unix, fd)) +let import_socket_listening ~sw ~close_unix fd = + open_listening @@ Effect.perform (Import_socket_listening (sw, close_unix, fd)) + let import_socket_datagram ~sw ~close_unix fd = open_datagram @@ Effect.perform (Import_socket_datagram (sw, close_unix, fd)) diff --git a/lib_eio/unix/net.mli b/lib_eio/unix/net.mli index 16a5d3b0c..9ad834f3b 100644 --- a/lib_eio/unix/net.mli +++ b/lib_eio/unix/net.mli @@ -56,15 +56,22 @@ end (** {2 Creating or importing sockets} *) val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | stream_socket_ty] r -(** [import_socket_stream ~sw ~close_unix:true fd] is an Eio flow that uses [fd]. +(** [import_socket_stream ~sw ~close_unix fd] is an Eio flow that uses [fd]. It can be cast to e.g. {!source} for a one-way flow. The socket object will be closed when [sw] finishes. The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *) +val import_socket_listening : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | listening_socket_ty] r +(** [import_socket_listening ~sw ~close_unix fd] is an Eio listening socket that uses [fd]. + + The socket object will be closed when [sw] finishes. + + The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *) + val import_socket_datagram : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r -(** [import_socket_datagram ~sw ~close_unix:true fd] is an Eio datagram socket that uses [fd]. +(** [import_socket_datagram ~sw ~close_unix fd] is an Eio datagram socket that uses [fd]. The socket object will be closed when [sw] finishes. @@ -100,6 +107,8 @@ val getnameinfo : Eio.Net.Sockaddr.t -> (string * string) type _ Effect.t += | Import_socket_stream : Switch.t * bool * Unix.file_descr -> [`Unix_fd | stream_socket_ty] r Effect.t (** See {!import_socket_stream} *) + | Import_socket_listening : + Switch.t * bool * Unix.file_descr -> [`Unix_fd | listening_socket_ty] r Effect.t (** See {!import_socket_listening} *) | Import_socket_datagram : Switch.t * bool * Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r Effect.t (** See {!import_socket_datagram} *) | Socketpair_stream : Eio.Switch.t * Unix.socket_domain * int -> diff --git a/lib_eio_linux/eio_linux.ml b/lib_eio_linux/eio_linux.ml index 00a695098..ec9c8eb07 100644 --- a/lib_eio_linux/eio_linux.ml +++ b/lib_eio_linux/eio_linux.ml @@ -504,6 +504,10 @@ let run_event_loop (type a) ?fallback config (main : _ -> a) arg : a = let fd = Fd.of_unix ~sw ~seekable:false ~close_unix fd in continue k (Flow.of_fd fd :> _ Eio_unix.Net.stream_socket) ) + | Eio_unix.Net.Import_socket_listening (sw, close_unix, fd) -> Some (fun k -> + let fd = Fd.of_unix ~sw ~seekable:false ~close_unix fd in + continue k (listening_socket fd) + ) | Eio_unix.Net.Import_socket_datagram (sw, close_unix, fd) -> Some (fun k -> let fd = Fd.of_unix ~sw ~seekable:false ~close_unix fd in continue k (datagram_socket fd) diff --git a/lib_eio_posix/domain_mgr.ml b/lib_eio_posix/domain_mgr.ml index 0b49f4a25..f4bc98b9b 100644 --- a/lib_eio_posix/domain_mgr.ml +++ b/lib_eio_posix/domain_mgr.ml @@ -48,6 +48,11 @@ let run_event_loop fn x = Unix.set_nonblock unix_fd; continue k (Flow.of_fd fd :> _ Eio_unix.Net.stream_socket) ) + | Eio_unix.Net.Import_socket_listening (sw, close_unix, unix_fd) -> Some (fun k -> + let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in + Unix.set_nonblock unix_fd; + continue k (Net.listening_socket ~hook:Switch.null_hook fd) + ) | Eio_unix.Net.Import_socket_datagram (sw, close_unix, unix_fd) -> Some (fun k -> let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in Unix.set_nonblock unix_fd; diff --git a/lib_eio_windows/domain_mgr.ml b/lib_eio_windows/domain_mgr.ml index 5a63e78a9..24f036b31 100755 --- a/lib_eio_windows/domain_mgr.ml +++ b/lib_eio_windows/domain_mgr.ml @@ -48,6 +48,11 @@ let run_event_loop fn x = (try Unix.set_nonblock unix_fd with Unix.Unix_error (Unix.ENOTSOCK, _, _) -> ()); continue k (Flow.of_fd fd :> _ Eio_unix.Net.stream_socket) ) + | Eio_unix.Net.Import_socket_listening (sw, close_unix, unix_fd) -> Some (fun k -> + let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in + Unix.set_nonblock unix_fd; + continue k (Net.listening_socket ~hook:Switch.null_hook fd) + ) | Eio_unix.Net.Import_socket_datagram (sw, close_unix, unix_fd) -> Some (fun k -> let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in Unix.set_nonblock unix_fd; diff --git a/tests/network.md b/tests/network.md index eb83ceb7b..c22472925 100644 --- a/tests/network.md +++ b/tests/network.md @@ -373,6 +373,29 @@ Wrapping a Unix FD as an Eio stream socket: - : unit = () ``` +Wrapping a Unix FD as a listening Eio socket: + +```ocaml +# run @@ fun ~net sw -> + let l = Unix.(socket PF_INET SOCK_STREAM 0) in + Unix.bind l (Unix.ADDR_INET (Unix.inet_addr_loopback, 8082)); + Unix.listen l 40; + let l = Eio_unix.Net.import_socket_listening ~sw ~close_unix:true l in + Fiber.both + (fun () -> run_server ~sw l) + (fun () -> + run_client ~sw ~net ~addr:(`Tcp (Eio.Net.Ipaddr.V4.loopback, 8082)); + traceln "Client finished - cancelling server"; + raise Graceful_shutdown + );; ++Connecting to server... ++Server accepted connection from client ++Server received: "Hello from client" ++Client received: "Bye" ++Client finished - cancelling server +Exception: Graceful_shutdown. +``` + Wrapping a Unix FD as an datagram Eio socket: ```ocaml From 14fefcb49b23f00c8105bd1ccc44e263b559189e Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Tue, 28 May 2024 11:58:43 +0100 Subject: [PATCH 07/66] Prepare release --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 1c12dcdb6..357de52d3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,8 @@ New features: - Add `Eio.Pool.use ~never_block` (@SGrondin #657, reviewed by @talex5). +- Add `Eio_unix.Net.import_socket_listening` (@alyssais #733). + - Add `Eio.Time.Timeout.sleep` (@talex5 #726). Documentation: @@ -29,6 +31,8 @@ Linux backend: - Split flow into its own file (@talex5 #727). +- Add work-around for signals race (@talex5 #734). + POSIX backend: - Add `_BSD_SOURCE` flag to fix build on OpenBSD (@prgbln #722). From a68438f9e92afff955429eafeb265ce6d39b52a6 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Fri, 14 Jun 2024 11:30:09 +0100 Subject: [PATCH 08/66] Add example to `Buf_read.seq` documentation --- lib_eio/buf_read.mli | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib_eio/buf_read.mli b/lib_eio/buf_read.mli index 4701605cb..4ed9ac271 100644 --- a/lib_eio/buf_read.mli +++ b/lib_eio/buf_read.mli @@ -218,6 +218,13 @@ val seq : ?stop:bool parser -> 'a parser -> 'a Seq.t parser It is not necessary to consume all the elements of the sequence. + Example ([head 4] is a parser that takes 4 lines): + + {[ + let head n r = + r |> Buf_read.(seq line) |> Seq.take n |> List.of_seq + ]} + @param stop This is used before parsing each item. The sequence ends if this returns [true]. The default is {!at_end_of_input}. *) From 3de93835a932db372b72db46390265215907ffa4 Mon Sep 17 00:00:00 2001 From: Fabian Date: Mon, 17 Jun 2024 14:10:58 +0100 Subject: [PATCH 09/66] define struct clone_args for linux-lts versions that don't have it --- lib_eio_linux/eio_stubs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib_eio_linux/eio_stubs.c b/lib_eio_linux/eio_stubs.c index 2d2a02c66..4e4054104 100644 --- a/lib_eio_linux/eio_stubs.c +++ b/lib_eio_linux/eio_stubs.c @@ -39,7 +39,11 @@ #ifndef SYS_clone3 # define SYS_clone3 435 # define CLONE_PIDFD 0x00001000 -struct clone_args { +#endif + +// struct clone_args isn't defined in linux-lts headers, so define it here +// Note that this struct is versioned by size. See linux/sched.h for details +struct caml_eio_clone_args { uint64_t flags; uint64_t pidfd; uint64_t child_tid; @@ -48,11 +52,7 @@ struct clone_args { uint64_t stack; uint64_t stack_size; uint64_t tls; - uint64_t set_tid; - uint64_t set_tid_size; - uint64_t cgroup; }; -#endif // Make sure we have enough space for at least one entry. #define DIRENT_BUF_SIZE (PATH_MAX + sizeof(struct dirent64)) @@ -178,9 +178,9 @@ static int pidfd_open(pid_t pid, unsigned int flags) { /* Like clone3, but falls back to fork if not supported. Also, raises exceptions rather then returning an error. */ -static pid_t clone3_with_fallback(struct clone_args *cl_args) { +static pid_t clone3_with_fallback(struct caml_eio_clone_args *cl_args) { int *pidfd = (int *)(uintptr_t) cl_args->pidfd; - pid_t child_pid = syscall(SYS_clone3, cl_args, sizeof(struct clone_args)); + pid_t child_pid = syscall(SYS_clone3, cl_args, sizeof(struct caml_eio_clone_args)); if (child_pid >= 0) return child_pid; /* Success! */ @@ -216,7 +216,7 @@ CAMLprim value caml_eio_clone3(value v_errors, value v_actions) { CAMLlocal1(v_result); pid_t child_pid; int pidfd = -1; /* Is automatically close-on-exec */ - struct clone_args cl_args = { + struct caml_eio_clone_args cl_args = { .flags = CLONE_PIDFD, .pidfd = (uintptr_t) &pidfd, .exit_signal = SIGCHLD, /* Needed for wait4 to work if we exit before exec */ From 60a3c424d210d696d1cc2b778d5ee291f54419ce Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 19 Jun 2024 11:32:18 +0100 Subject: [PATCH 10/66] eio_windows: improve openat error handling --- lib_eio_windows/eio_windows_stubs.c | 2 +- lib_eio_windows/fs.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_eio_windows/eio_windows_stubs.c b/lib_eio_windows/eio_windows_stubs.c index d3b5be2af..4e26e21c5 100755 --- a/lib_eio_windows/eio_windows_stubs.c +++ b/lib_eio_windows/eio_windows_stubs.c @@ -237,7 +237,7 @@ CAMLprim value caml_eio_windows_unlinkat(value v_dirfd, value v_pathname, value if (!NT_SUCCESS(r)) { caml_win32_maperr(RtlNtStatusToDosError(r)); - uerror("openat", Nothing); + uerror("openat", v_pathname); } // Now close the file to delete it diff --git a/lib_eio_windows/fs.ml b/lib_eio_windows/fs.ml index c6e9d5f18..d877b6fb5 100755 --- a/lib_eio_windows/fs.ml +++ b/lib_eio_windows/fs.ml @@ -82,7 +82,7 @@ end = struct let dir = resolve t dir in Switch.run @@ fun sw -> let open Low_level in - let dirfd = Low_level.openat ~sw ~nofollow:true dir Flags.Open.(generic_read + synchronise) Flags.Disposition.(open_if) Flags.Create.(directory) in + let dirfd = Err.run (Low_level.openat ~sw ~nofollow:true dir Flags.Open.(generic_read + synchronise) Flags.Disposition.(open_if)) Flags.Create.(directory) in fn (Some dirfd) leaf ) ) else fn None path From e364ed634f4261e19c2ec2b9fd92d27052127649 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 19 Jun 2024 11:53:18 +0100 Subject: [PATCH 11/66] eio_windows: run the fs example in CI --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ea70241e7..a920ba0be 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,6 +57,7 @@ jobs: - run: opam exec -- dune build - run: opam exec -- dune runtest - run: opam exec -- dune exec -- ./examples/net/main.exe + - run: opam exec -- dune exec -- ./examples/fs/main.exe docker: runs-on: ubuntu-latest steps: From c6985861776b7fb24c295b36b40e82d7be83fb3c Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 19 Jun 2024 14:13:33 +0100 Subject: [PATCH 12/66] Eio.Path: always use "/" as separator path.mli says: > In Eio, the directory separator is always "/", even on Windows. However, `Path.(/)` used `Filename.concat` to create paths, which uses the native separator ("\" on Windows). --- lib_eio/path.ml | 17 ++++++++++++++--- tests/fs.md | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/lib_eio/path.ml b/lib_eio/path.ml index 0985fcb4b..37cd5ff09 100644 --- a/lib_eio/path.ml +++ b/lib_eio/path.ml @@ -1,11 +1,22 @@ type 'a t = 'a Fs.dir * Fs.path +(* Like [Filename.is_relative] but always using "/" as the separator. *) +let is_relative = function + | "" -> true + | x -> x.[0] <> '/' + +(* Like [Filename.concat] but always using "/" as the separator. *) +let concat a b = + let l = String.length a in + if l = 0 || a.[l - 1] = '/' then a ^ b + else a ^ "/" ^ b + let ( / ) (dir, p1) p2 = match p1, p2 with - | p1, "" -> (dir, Filename.concat p1 p2) - | _, p2 when not (Filename.is_relative p2) -> (dir, p2) + | p1, "" -> (dir, concat p1 p2) + | _, p2 when not (is_relative p2) -> (dir, p2) | ".", p2 -> (dir, p2) - | p1, p2 -> (dir, Filename.concat p1 p2) + | p1, p2 -> (dir, concat p1 p2) let pp f (Resource.T (t, ops), p) = let module X = (val (Resource.get ops Fs.Pi.Dir)) in diff --git a/tests/fs.md b/tests/fs.md index 810f9c7c0..446874ad3 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -985,3 +985,29 @@ Exception: Failure "Simulated error". +seek from end: 9 - : unit = () ``` + +# Extending paths + +```ocaml +# run @@ fun env -> + let base = fst env#cwd in + List.iter (fun (a, b) -> traceln "%S / %S = %S" a b (snd ((base, a) / b))) [ + "foo", "bar"; + "foo/", "bar"; + "foo", "/bar"; + "foo", ""; + "foo/", ""; + "", ""; + "", "bar"; + "/", ""; + ] ++"foo" / "bar" = "foo/bar" ++"foo/" / "bar" = "foo/bar" ++"foo" / "/bar" = "/bar" ++"foo" / "" = "foo/" ++"foo/" / "" = "foo/" ++"" / "" = "" ++"" / "bar" = "bar" ++"/" / "" = "/" +- : unit = () +``` From 0769ce3fd49c4bb57704b16b6af33abfc87c1ded Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Thu, 20 Jun 2024 09:37:40 +0100 Subject: [PATCH 13/66] Eio_unix.Net: make some return types more polymorphic This allows e.g. using the result of the `import_socket_stream` as a `stream_socket_ty` without needing a cast. --- lib_eio/unix/net.ml | 9 +++++---- lib_eio/unix/net.mli | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib_eio/unix/net.ml b/lib_eio/unix/net.ml index 4a0fa298b..11f4a173a 100644 --- a/lib_eio/unix/net.ml +++ b/lib_eio/unix/net.ml @@ -68,9 +68,9 @@ type _ Effect.t += | Socketpair_datagram : Switch.t * Unix.socket_domain * int -> ([`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r) Effect.t -let open_stream s = (s : _ stream_socket :> [< `Unix_fd | stream_socket_ty] r) -let open_listening s = (s : _ listening_socket :> [< `Unix_fd | listening_socket_ty] r) -let open_datagram s = (s : _ datagram_socket :> [< `Unix_fd | datagram_socket_ty] r) +let open_stream s = (s : [`Unix_fd | stream_socket_ty] r :> [< `Unix_fd | stream_socket_ty] r) +let open_listening s = (s : [`Unix_fd | listening_socket_ty] r :> [< `Unix_fd | listening_socket_ty] r) +let open_datagram s = (s : [`Unix_fd | datagram_socket_ty] r :> [< `Unix_fd | datagram_socket_ty] r) let import_socket_stream ~sw ~close_unix fd = open_stream @@ Effect.perform (Import_socket_stream (sw, close_unix, fd)) @@ -86,7 +86,8 @@ let socketpair_stream ~sw ?(domain=Unix.PF_UNIX) ?(protocol=0) () = (open_stream a, open_stream b) let socketpair_datagram ~sw ?(domain=Unix.PF_UNIX) ?(protocol=0) () = - Effect.perform (Socketpair_datagram (sw, domain, protocol)) + let a, b = Effect.perform (Socketpair_datagram (sw, domain, protocol)) in + (open_datagram a, open_datagram b) let fd socket = Option.get (Resource.fd_opt socket) diff --git a/lib_eio/unix/net.mli b/lib_eio/unix/net.mli index 9ad834f3b..7da1a54bd 100644 --- a/lib_eio/unix/net.mli +++ b/lib_eio/unix/net.mli @@ -55,7 +55,7 @@ end (** {2 Creating or importing sockets} *) -val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | stream_socket_ty] r +val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [< `Unix_fd | stream_socket_ty] r (** [import_socket_stream ~sw ~close_unix fd] is an Eio flow that uses [fd]. It can be cast to e.g. {!source} for a one-way flow. @@ -63,14 +63,14 @@ val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *) -val import_socket_listening : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | listening_socket_ty] r +val import_socket_listening : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [< `Unix_fd | listening_socket_ty] r (** [import_socket_listening ~sw ~close_unix fd] is an Eio listening socket that uses [fd]. The socket object will be closed when [sw] finishes. The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *) -val import_socket_datagram : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r +val import_socket_datagram : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [< `Unix_fd | datagram_socket_ty] r (** [import_socket_datagram ~sw ~close_unix fd] is an Eio datagram socket that uses [fd]. The socket object will be closed when [sw] finishes. @@ -82,7 +82,7 @@ val socketpair_stream : ?domain:Unix.socket_domain -> ?protocol:int -> unit -> - [`Unix_fd | stream_socket_ty] r * [`Unix_fd | stream_socket_ty] r + [< `Unix_fd | stream_socket_ty] r * [< `Unix_fd | stream_socket_ty] r (** [socketpair_stream ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other. This creates OS-level resources using [socketpair(2)]. @@ -93,7 +93,7 @@ val socketpair_datagram : ?domain:Unix.socket_domain -> ?protocol:int -> unit -> - [`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r + [< `Unix_fd | datagram_socket_ty] r * [< `Unix_fd | datagram_socket_ty] r (** [socketpair_datagram ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other. This creates OS-level resources using [socketpair(2)]. From c92704f5f0f2affb109b5b5189ec6e3a971595a0 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sat, 22 Jun 2024 16:27:53 +0100 Subject: [PATCH 14/66] examples/fs: show how to read files while scanning --- examples/fs/main.ml | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/examples/fs/main.ml b/examples/fs/main.ml index d91e29d89..ef5b4ab0a 100644 --- a/examples/fs/main.ml +++ b/examples/fs/main.ml @@ -1,21 +1,32 @@ -(* Walks the directory tree rooted at the current directory, - displaying all directory names (skipping hidden directories and `_build`). *) - -open Eio.Std +(* Walk the directory tree rooted at the current directory, + showing a summary for any .mli files. *) let ( / ) = Eio.Path.( / ) -let rec scan t = +let is_doc_comment = String.starts_with ~prefix:"(** " + +(* Print the first line of [t]'s doc-comment, if any *) +let scan_mli t f = + Eio.Path.with_lines t (fun lines -> + Seq.find is_doc_comment lines + |> Option.iter (fun line -> + let stop = String.index_from_opt line 4 '*' |> Option.value ~default:(String.length line) in + Format.fprintf f "%a: %s@." Eio.Path.pp t (String.sub line 4 (stop - 4)) + ) + ) + +(* Walk the tree rooted at [t] and scan any .mli files found. *) +let rec scan t f = match Eio.Path.kind ~follow:false t with | `Directory -> - traceln "Visiting %a" Eio.Path.pp t; Eio.Path.read_dir t |> List.iter (function - | "_build" -> () - | item when String.starts_with ~prefix:"." item -> () - | item -> scan (t / item) - ) + | "_build" | "_opam" -> () (* Don't examine these directories *) + | item when String.starts_with ~prefix:"." item -> () (* Skip hidden items *) + | item -> scan (t / item) f + ) + | `Regular_file when Filename.check_suffix (snd t) ".mli" -> scan_mli t f | _ -> () let () = Eio_main.run @@ fun env -> - scan (Eio.Stdenv.cwd env) + scan (Eio.Stdenv.cwd env) Format.std_formatter From 9e00b15ec004ab53f18b09ba42b269a0a78e7956 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Thu, 8 Aug 2024 15:43:06 +0100 Subject: [PATCH 15/66] Record trace event when spawning processes This can take quite a long time. --- lib_eio_linux/low_level.ml | 5 ++++- lib_eio_posix/low_level.ml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib_eio_linux/low_level.ml b/lib_eio_linux/low_level.ml index 1af99be23..2db0f2fe6 100644 --- a/lib_eio_linux/low_level.ml +++ b/lib_eio_linux/low_level.ml @@ -571,7 +571,10 @@ module Process = struct let exit_status, set_exit_status = Promise.create () in let t = Fd.use_exn "errors-w" errors_w @@ fun errors_w -> - let pid, pid_fd = eio_spawn errors_w c_actions in + let pid, pid_fd = + Eio.Private.Trace.with_span "spawn" @@ fun () -> + eio_spawn errors_w c_actions + in let pid_fd = Fd.of_unix ~sw ~seekable:false ~close_unix:true pid_fd in { pid; pid_fd; exit_status } in diff --git a/lib_eio_posix/low_level.ml b/lib_eio_posix/low_level.ml index e56ed24fb..6026993d1 100644 --- a/lib_eio_posix/low_level.ml +++ b/lib_eio_posix/low_level.ml @@ -558,6 +558,7 @@ module Process = struct let t = let pid = Fd.use_exn "errors-w" errors_w @@ fun errors_w -> + Eio.Private.Trace.with_span "spawn" @@ fun () -> eio_spawn errors_w c_actions in Fd.close errors_w; From b3f089a9787b929697ec8c5f5fc2d10545610cd6 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 4 Sep 2024 13:45:09 +0100 Subject: [PATCH 16/66] eio_linux: refactor fixed buffer code Instead of having separate Alloc, Alloc_or_wait and Free effects, the scheduler now provides a single Get effect to return itself, and the actual work is now done in the calling fiber. This is cleaner, and seems to be slightly faster too. Note that `alloc_fixed_or_wait` is currently not cancellable (it wasn't before either, but it's more obvious now). It would be possible to use DLS to store the scheduler rather than using an effect. However, the improvement in speed is minimal and there are some complications with sys-threads, so probably better to wait for OCaml to support thread-local-storage first. --- lib_eio/core/eio__core.ml | 1 + lib_eio/core/eio__core.mli | 1 + lib_eio_linux/low_level.ml | 33 +++++++++++++++++++++++++----- lib_eio_linux/sched.ml | 42 ++++++-------------------------------- 4 files changed, 36 insertions(+), 41 deletions(-) diff --git a/lib_eio/core/eio__core.ml b/lib_eio/core/eio__core.ml index 2c8f20536..3418d944d 100644 --- a/lib_eio/core/eio__core.ml +++ b/lib_eio/core/eio__core.ml @@ -7,6 +7,7 @@ module Private = struct module Suspend = Suspend module Cells = Cells module Broadcast = Broadcast + module Single_waiter = Single_waiter module Trace = Trace module Fiber_context = Cancel.Fiber_context module Debug = Debug diff --git a/lib_eio/core/eio__core.mli b/lib_eio/core/eio__core.mli index 65e2f41e4..734d9f1c7 100644 --- a/lib_eio/core/eio__core.mli +++ b/lib_eio/core/eio__core.mli @@ -606,6 +606,7 @@ module Private : sig module Cells = Cells module Broadcast = Broadcast + module Single_waiter = Single_waiter (** Every fiber has an associated context. *) module Fiber_context : sig diff --git a/lib_eio_linux/low_level.ml b/lib_eio_linux/low_level.ml index 2db0f2fe6..6967fb80a 100644 --- a/lib_eio_linux/low_level.ml +++ b/lib_eio_linux/low_level.ml @@ -207,11 +207,34 @@ let write ?file_offset:off fd buf len = raise @@ Err.wrap (Uring.error_of_errno res) "write" "" ) -let alloc_fixed () = Effect.perform Sched.Alloc - -let alloc_fixed_or_wait () = Effect.perform Sched.Alloc_or_wait - -let free_fixed buf = Effect.perform (Sched.Free buf) +let alloc_fixed () = + let s = Sched.get () in + match s.mem with + | None -> None + | Some mem -> + match Uring.Region.alloc mem with + | buf -> Some buf + | exception Uring.Region.No_space -> None + +let alloc_fixed_or_wait () = + let s = Sched.get () in + match s.mem with + | None -> failwith "No fixed buffer available" + | Some mem -> + match Uring.Region.alloc mem with + | buf -> buf + | exception Uring.Region.No_space -> + let id = Eio.Private.Trace.mint_id () in + let trigger = Eio.Private.Single_waiter.create () in + Queue.push trigger s.mem_q; + (* todo: remove protect; but needs to remove from queue on cancel *) + Eio.Private.Single_waiter.await_protect trigger "alloc_fixed_or_wait" id + +let free_fixed buf = + let s = Sched.get () in + match Queue.take_opt s.mem_q with + | None -> Uring.Region.free buf + | Some k -> Eio.Private.Single_waiter.wake k (Ok buf) let splice src ~dst ~len = Fd.use_exn "splice-src" src @@ fun src -> diff --git a/lib_eio_linux/sched.ml b/lib_eio_linux/sched.ml index 4d5f1a901..7d9e4b27b 100644 --- a/lib_eio_linux/sched.ml +++ b/lib_eio_linux/sched.ml @@ -50,7 +50,7 @@ type t = { uring: io_job Uring.t; mem: Uring.Region.t option; io_q: (t -> unit) Queue.t; (* waiting for room on [uring] *) - mem_q : Uring.Region.chunk Suspended.t Queue.t; + mem_q : Uring.Region.chunk Eio.Private.Single_waiter.t Queue.t; (* The queue of runnable fibers ready to be resumed. Note: other domains can also add work items here. *) run_q : runnable Lf_queue.t; @@ -74,9 +74,9 @@ type t = { type _ Effect.t += | Enter : (t -> 'a Suspended.t -> unit) -> 'a Effect.t | Cancel : io_job Uring.job -> unit Effect.t - | Alloc : Uring.Region.chunk option Effect.t - | Alloc_or_wait : Uring.Region.chunk Effect.t - | Free : Uring.Region.chunk -> unit Effect.t + | Get : t Effect.t + +let get () = Effect.perform Get let wake_buffer = let b = Bytes.create 8 in @@ -339,21 +339,6 @@ and complete_rw_req st ({len; cur_off; action; _} as req) res = | _, Exactly len -> Suspended.continue action len | n, Upto _ -> Suspended.continue action n -let alloc_buf_or_wait st k = - match st.mem with - | None -> Suspended.discontinue k (Failure "No fixed buffer available") - | Some mem -> - match Uring.Region.alloc mem with - | buf -> Suspended.continue k buf - | exception Uring.Region.No_space -> - Queue.push k st.mem_q; - schedule st - -let free_buf st buf = - match Queue.take_opt st.mem_q with - | None -> Uring.Region.free buf - | Some k -> enqueue_thread st k buf - let rec enqueue_poll_add fd poll_mask st action = Trace.log "poll_add"; let retry = with_cancel_hook ~action st (fun () -> @@ -411,8 +396,9 @@ let run ~extra_effects st main arg = Fiber_context.destroy fiber; Printexc.raise_with_backtrace ex (Printexc.get_raw_backtrace ()) ); - effc = fun (type a) (e : a Effect.t) -> + effc = fun (type a) (e : a Effect.t) : ((a, _) continuation -> _) option -> match e with + | Get -> Some (fun k -> continue k st) | Enter fn -> Some (fun k -> match Fiber_context.get_error fiber with | Some e -> discontinue k e @@ -467,22 +453,6 @@ let run ~extra_effects st main arg = Eio_unix.Private.Thread_pool.submit st.thread_pool ~ctx:fiber ~enqueue fn; schedule st ) - | Alloc -> Some (fun k -> - match st.mem with - | None -> continue k None - | Some mem -> - match Uring.Region.alloc mem with - | buf -> continue k (Some buf) - | exception Uring.Region.No_space -> continue k None - ) - | Alloc_or_wait -> Some (fun k -> - let k = { Suspended.k; fiber } in - alloc_buf_or_wait st k - ) - | Free buf -> Some (fun k -> - free_buf st buf; - continue k () - ) | e -> extra_effects.effc e } in From 175ccaf04cf861efc1c0d2549f8a8e2aedff91ae Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 4 Sep 2024 14:18:40 +0100 Subject: [PATCH 17/66] eio_linux: allow alloc_fixed_or_wait to be cancelled --- lib_eio/core/fiber.ml | 2 +- lib_eio/core/single_waiter.ml | 31 ++++++++++++-------- lib_eio/core/single_waiter.mli | 25 ++++++++++++++++ lib_eio/core/switch.ml | 2 +- lib_eio_linux/low_level.ml | 19 +++++++----- lib_eio_linux/sched.ml | 6 ++-- lib_eio_linux/tests/test.ml | 53 +++++++++++++++++++++++++++------- 7 files changed, 104 insertions(+), 34 deletions(-) create mode 100644 lib_eio/core/single_waiter.mli diff --git a/lib_eio/core/fiber.ml b/lib_eio/core/fiber.ml index a06f8f357..3113e8ccc 100644 --- a/lib_eio/core/fiber.ml +++ b/lib_eio/core/fiber.ml @@ -226,7 +226,7 @@ module List = struct let release t = t.free_fibers <- t.free_fibers + 1; - if t.free_fibers = 1 then Single_waiter.wake t.cond (Ok ()) + if t.free_fibers = 1 then Single_waiter.wake_if_sleeping t.cond let use t fn x = await_free t; diff --git a/lib_eio/core/single_waiter.ml b/lib_eio/core/single_waiter.ml index 6cc823eb6..dfd72d887 100644 --- a/lib_eio/core/single_waiter.ml +++ b/lib_eio/core/single_waiter.ml @@ -1,25 +1,32 @@ -(* Allows a single fiber to wait to be notified by another fiber in the same domain. - If multiple fibers need to wait at once, or the notification comes from another domain, - this can't be used. *) +type 'a state = + | Running + | Sleeping of (('a, exn) result -> unit) -type 'a t = { - mutable wake : ('a, exn) result -> unit; -} +type 'a t = 'a state ref -let create () = { wake = ignore } +let create () = ref Running -let wake t v = t.wake v +let wake t v = + match !t with + | Running -> false + | Sleeping fn -> + t := Running; + fn v; + true + +let wake_if_sleeping t = + ignore (wake t (Ok ()) : bool) let await t op id = let x = Suspend.enter op @@ fun ctx enqueue -> Cancel.Fiber_context.set_cancel_fn ctx (fun ex -> - t.wake <- ignore; + t := Running; enqueue (Error ex) ); - t.wake <- (fun x -> + t := Sleeping (fun x -> Cancel.Fiber_context.clear_cancel_fn ctx; - t.wake <- ignore; + t := Running; enqueue x ) in @@ -29,7 +36,7 @@ let await t op id = let await_protect t op id = let x = Suspend.enter_unchecked op @@ fun _ctx enqueue -> - t.wake <- (fun x -> t.wake <- ignore; enqueue x) + t := Sleeping (fun x -> t := Running; enqueue x) in Trace.get id; x diff --git a/lib_eio/core/single_waiter.mli b/lib_eio/core/single_waiter.mli new file mode 100644 index 000000000..d058ea30c --- /dev/null +++ b/lib_eio/core/single_waiter.mli @@ -0,0 +1,25 @@ +(** Allows a single fiber to wait to be notified by another fiber in the same domain. + If multiple fibers need to wait at once, or the notification comes from another domain, + this can't be used. *) + +type 'a t +(** A handle representing a fiber that might be sleeping. + It is either in the Running or Sleeping state. *) + +val create : unit -> 'a t +(** [create ()] is a new waiter, initially in the Running state. *) + +val wake : 'a t -> ('a, exn) result -> bool +(** [wake t v] resumes [t]'s fiber with value [v] and returns [true] if it was sleeping. + If [t] is Running then this just returns [false]. *) + +val wake_if_sleeping : unit t -> unit +(** [wake_if_sleeping] is [ignore (wake t (Ok ()))]. *) + +val await : 'a t -> string -> Trace.id -> 'a +(** [await t op id] suspends the calling fiber, changing [t]'s state to Sleeping. + If the fiber is cancelled, a cancel exception is raised. + [op] and [id] are used for tracing. *) + +val await_protect : 'a t -> string -> Trace.id -> 'a +(** [await_protect] is like {!await}, but the sleep cannot be cancelled. *) diff --git a/lib_eio/core/switch.ml b/lib_eio/core/switch.ml index 4cf13b129..f9bde2a15 100644 --- a/lib_eio/core/switch.ml +++ b/lib_eio/core/switch.ml @@ -72,7 +72,7 @@ let dec_fibers t = if t.daemon_fibers > 0 && t.fibers = t.daemon_fibers then Cancel.cancel t.cancel Exit; if t.fibers = 0 then - Single_waiter.wake t.waiter (Ok ()) + Single_waiter.wake_if_sleeping t.waiter let with_op t fn = inc_fibers t; diff --git a/lib_eio_linux/low_level.ml b/lib_eio_linux/low_level.ml index 6967fb80a..42e72bfa3 100644 --- a/lib_eio_linux/low_level.ml +++ b/lib_eio_linux/low_level.ml @@ -226,15 +226,20 @@ let alloc_fixed_or_wait () = | exception Uring.Region.No_space -> let id = Eio.Private.Trace.mint_id () in let trigger = Eio.Private.Single_waiter.create () in - Queue.push trigger s.mem_q; - (* todo: remove protect; but needs to remove from queue on cancel *) - Eio.Private.Single_waiter.await_protect trigger "alloc_fixed_or_wait" id - -let free_fixed buf = + let node = Lwt_dllist.add_r trigger s.mem_q in + try + Eio.Private.Single_waiter.await trigger "alloc_fixed_or_wait" id + with ex -> + Lwt_dllist.remove node; + raise ex + +let rec free_fixed buf = let s = Sched.get () in - match Queue.take_opt s.mem_q with + match Lwt_dllist.take_opt_l s.mem_q with | None -> Uring.Region.free buf - | Some k -> Eio.Private.Single_waiter.wake k (Ok buf) + | Some k -> + if not (Eio.Private.Single_waiter.wake k (Ok buf)) then + free_fixed buf (* [k] was already cancelled, but not yet removed from the queue *) let splice src ~dst ~len = Fd.use_exn "splice-src" src @@ fun src -> diff --git a/lib_eio_linux/sched.ml b/lib_eio_linux/sched.ml index 7d9e4b27b..80fc63f4a 100644 --- a/lib_eio_linux/sched.ml +++ b/lib_eio_linux/sched.ml @@ -50,7 +50,7 @@ type t = { uring: io_job Uring.t; mem: Uring.Region.t option; io_q: (t -> unit) Queue.t; (* waiting for room on [uring] *) - mem_q : Uring.Region.chunk Eio.Private.Single_waiter.t Queue.t; + mem_q : Uring.Region.chunk Eio.Private.Single_waiter.t Lwt_dllist.t; (* The queue of runnable fibers ready to be resumed. Note: other domains can also add work items here. *) run_q : runnable Lf_queue.t; @@ -247,7 +247,7 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] = ) else if timeout = None && Uring.active_ops uring = 0 then ( (* Nothing further can happen at this point. If there are no events in progress but also still no memory available, something has gone wrong! *) - assert (Queue.length mem_q = 0); + assert (Lwt_dllist.length mem_q = 0); Lf_queue.close st.run_q; (* Just to catch bugs if something tries to enqueue later *) `Exit_scheduler ) else ( @@ -536,7 +536,7 @@ let with_sched ?(fallback=no_fallback) config fn = Lf_queue.push run_q IO; let sleep_q = Zzz.create () in let io_q = Queue.create () in - let mem_q = Queue.create () in + let mem_q = Lwt_dllist.create () in with_eventfd @@ fun eventfd -> let thread_pool = Eio_unix.Private.Thread_pool.create ~sleep_q in fn { mem; uring; run_q; io_q; mem_q; eventfd; need_wakeup = Atomic.make false; sleep_q; thread_pool } diff --git a/lib_eio_linux/tests/test.ml b/lib_eio_linux/tests/test.ml index 7c7e31aad..238f909ad 100644 --- a/lib_eio_linux/tests/test.ml +++ b/lib_eio_linux/tests/test.ml @@ -211,19 +211,52 @@ let test_signal_race () = (fun () -> Eio.Condition.await_no_mutex cond) (fun () -> ignore (Unix.setitimer ITIMER_REAL { it_interval = 0.; it_value = 0.001 } : Unix.interval_timer_status)) +let test_alloc_fixed_or_wait () = + Eio_linux.run ~n_blocks:1 @@ fun _env -> + let block = Eio_linux.Low_level.alloc_fixed_or_wait () in + (* We have to wait for the block, but get cancelled while waiting. *) + begin + try + Fiber.both + (fun () -> ignore (Eio_linux.Low_level.alloc_fixed_or_wait () : Uring.Region.chunk)) + (fun () -> raise Exit); + with Exit -> () + end; + (* We have to wait for the block, and get it when the old one is freed. *) + Fiber.both + (fun () -> + let x = Eio_linux.Low_level.alloc_fixed_or_wait () in + Eio_linux.Low_level.free_fixed x + ) + (fun () -> + Eio_linux.Low_level.free_fixed block + ); + (* The old block is passed to the waiting fiber, but it's cancelled. *) + let block = Eio_linux.Low_level.alloc_fixed_or_wait () in + Fiber.both + (fun () -> + Fiber.first + (fun () -> ignore (Eio_linux.Low_level.alloc_fixed_or_wait ()); assert false) + (fun () -> ()) + ) + (fun () -> Eio_linux.Low_level.free_fixed block); + let block = Eio_linux.Low_level.alloc_fixed_or_wait () in + Eio_linux.Low_level.free_fixed block + let () = let open Alcotest in run "eio_linux" [ "io", [ - test_case "copy" `Quick test_copy; - test_case "direct_copy" `Quick test_direct_copy; - test_case "poll_add" `Quick test_poll_add; - test_case "poll_add_busy" `Quick test_poll_add_busy; - test_case "iovec" `Quick test_iovec; - test_case "no_sqe" `Quick test_no_sqe; - test_case "read_exact" `Quick test_read_exact; - test_case "expose_backend" `Quick test_expose_backend; - test_case "statx" `Quick test_statx; - test_case "signal_race" `Quick test_signal_race; + test_case "copy" `Quick test_copy; + test_case "direct_copy" `Quick test_direct_copy; + test_case "poll_add" `Quick test_poll_add; + test_case "poll_add_busy" `Quick test_poll_add_busy; + test_case "iovec" `Quick test_iovec; + test_case "no_sqe" `Quick test_no_sqe; + test_case "read_exact" `Quick test_read_exact; + test_case "expose_backend" `Quick test_expose_backend; + test_case "statx" `Quick test_statx; + test_case "signal_race" `Quick test_signal_race; + test_case "alloc-fixed-or-wait" `Quick test_alloc_fixed_or_wait; ]; ] From ee5f2c5771d4ce7c08478f7728ecd68704ccf18d Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Fri, 6 Sep 2024 16:58:29 +0100 Subject: [PATCH 18/66] eio_linux: avoid triggering a TSan warning TSan warns that setting `statx_works` races with users of it. This isn't really a problem, because we always set it to the same value, but it's distracting when looking for other bugs. Reported by Anil Madhavapeddy in #751. --- lib_eio_linux/sched.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib_eio_linux/sched.ml b/lib_eio_linux/sched.ml index 80fc63f4a..ad819245e 100644 --- a/lib_eio_linux/sched.ml +++ b/lib_eio_linux/sched.ml @@ -521,7 +521,10 @@ let with_sched ?(fallback=no_fallback) config fn = Uring.exit uring; fallback (`Msg "Linux >= 5.15 is required for io_uring support") ) else ( - statx_works := Uring.op_supported probe Uring.Op.msg_ring; + (* The reason for an if here is to make sure we only set it once, when + the first domain is starting. This is just to avoid a tsan warning. *) + if not !statx_works && Uring.op_supported probe Uring.Op.msg_ring then + statx_works := true; match let mem = let fixed_buf_len = block_size * n_blocks in From 339582de50b7a23cde7965242db438fba70f61f2 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 18 Sep 2024 11:51:57 +0100 Subject: [PATCH 19/66] Remove unused library eio.core doesn't use cstruct. --- lib_eio/core/dune | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_eio/core/dune b/lib_eio/core/dune index 184869bf7..3923c4091 100644 --- a/lib_eio/core/dune +++ b/lib_eio/core/dune @@ -1,4 +1,4 @@ (library (name eio__core) (public_name eio.core) - (libraries cstruct hmap lwt-dllist fmt optint domain-local-await eio.runtime_events)) + (libraries hmap lwt-dllist fmt optint domain-local-await eio.runtime_events)) From c95c2fe85d3460827400454d772d9f2627619c24 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 18 Sep 2024 11:59:58 +0100 Subject: [PATCH 20/66] Move Dla to eio.utils It doesn't need to be in eio.core, as nothing else there depends on it. --- lib_eio/core/dune | 2 +- lib_eio/core/eio__core.ml | 2 -- lib_eio/core/eio__core.mli | 4 ---- lib_eio/mock/backend.ml | 2 +- lib_eio/{core => utils}/dla.ml | 4 ++-- lib_eio/utils/dla.mli | 1 + lib_eio/utils/dune | 2 +- lib_eio/utils/eio_utils.ml | 1 + lib_eio_linux/sched.ml | 2 +- lib_eio_posix/sched.ml | 2 +- lib_eio_windows/sched.ml | 2 +- 11 files changed, 10 insertions(+), 14 deletions(-) rename lib_eio/{core => utils}/dla.ml (81%) create mode 100644 lib_eio/utils/dla.mli diff --git a/lib_eio/core/dune b/lib_eio/core/dune index 3923c4091..4bda7916d 100644 --- a/lib_eio/core/dune +++ b/lib_eio/core/dune @@ -1,4 +1,4 @@ (library (name eio__core) (public_name eio.core) - (libraries hmap lwt-dllist fmt optint domain-local-await eio.runtime_events)) + (libraries hmap lwt-dllist fmt optint eio.runtime_events)) diff --git a/lib_eio/core/eio__core.ml b/lib_eio/core/eio__core.ml index 3418d944d..54795859c 100644 --- a/lib_eio/core/eio__core.ml +++ b/lib_eio/core/eio__core.ml @@ -19,6 +19,4 @@ module Private = struct | Fork = Fiber.Fork | Get_context = Cancel.Get_context end - - module Dla = Dla end diff --git a/lib_eio/core/eio__core.mli b/lib_eio/core/eio__core.mli index 734d9f1c7..d51cafe43 100644 --- a/lib_eio/core/eio__core.mli +++ b/lib_eio/core/eio__core.mli @@ -782,8 +782,4 @@ module Private : sig val v : t (** Backends should use this for {!Eio.Stdenv.debug}. *) end - - module Dla : sig - val prepare_for_await : unit -> Domain_local_await.t - end end diff --git a/lib_eio/mock/backend.ml b/lib_eio/mock/backend.ml index f3daa4167..847cc92aa 100644 --- a/lib_eio/mock/backend.ml +++ b/lib_eio/mock/backend.ml @@ -105,7 +105,7 @@ let run_full main = let result = ref None in let `Exit_scheduler = Domain_local_await.using - ~prepare_for_await:Eio.Private.Dla.prepare_for_await + ~prepare_for_await:Eio_utils.Dla.prepare_for_await ~while_running:(fun () -> fork ~new_fiber (fun () -> result := Some (main stdenv))) in match !result with diff --git a/lib_eio/core/dla.ml b/lib_eio/utils/dla.ml similarity index 81% rename from lib_eio/core/dla.ml rename to lib_eio/utils/dla.ml index cb72e9f2e..a8699150c 100644 --- a/lib_eio/core/dla.ml +++ b/lib_eio/utils/dla.ml @@ -7,10 +7,10 @@ let prepare_for_await () = | _ -> () and await () = if Atomic.get state != `Released then - Suspend.enter "domain-local-await" @@ fun ctx enqueue -> + Eio.Private.Suspend.enter "domain-local-await" @@ fun ctx enqueue -> let awaiting = `Awaiting enqueue in if Atomic.compare_and_set state `Init awaiting then ( - Cancel.Fiber_context.set_cancel_fn ctx (fun ex -> + Eio.Private.Fiber_context.set_cancel_fn ctx (fun ex -> if Atomic.compare_and_set state awaiting `Released then ( enqueue (Error ex) ) diff --git a/lib_eio/utils/dla.mli b/lib_eio/utils/dla.mli new file mode 100644 index 000000000..6bc14429f --- /dev/null +++ b/lib_eio/utils/dla.mli @@ -0,0 +1 @@ +val prepare_for_await : unit -> Domain_local_await.t diff --git a/lib_eio/utils/dune b/lib_eio/utils/dune index 4ad6dcae5..1cf3a3654 100644 --- a/lib_eio/utils/dune +++ b/lib_eio/utils/dune @@ -1,4 +1,4 @@ (library (name eio_utils) (public_name eio.utils) - (libraries eio psq fmt optint)) + (libraries eio psq fmt optint domain-local-await)) diff --git a/lib_eio/utils/eio_utils.ml b/lib_eio/utils/eio_utils.ml index 0ed17de9f..8597e9fc5 100644 --- a/lib_eio/utils/eio_utils.ml +++ b/lib_eio/utils/eio_utils.ml @@ -5,3 +5,4 @@ module Lf_queue = Lf_queue module Suspended = Suspended module Zzz = Zzz +module Dla = Dla diff --git a/lib_eio_linux/sched.ml b/lib_eio_linux/sched.ml index ad819245e..3abd4be37 100644 --- a/lib_eio_linux/sched.ml +++ b/lib_eio_linux/sched.ml @@ -460,7 +460,7 @@ let run ~extra_effects st main arg = let `Exit_scheduler = let new_fiber = Fiber_context.make_root () in Domain_local_await.using - ~prepare_for_await:Eio.Private.Dla.prepare_for_await + ~prepare_for_await:Eio_utils.Dla.prepare_for_await ~while_running:(fun () -> fork ~new_fiber (fun () -> Switch.run_protected ~name:"eio_linux" (fun sw -> diff --git a/lib_eio_posix/sched.ml b/lib_eio_posix/sched.ml index a0de826c7..23dae62a2 100644 --- a/lib_eio_posix/sched.ml +++ b/lib_eio_posix/sched.ml @@ -379,7 +379,7 @@ let run ~extra_effects t main x = let `Exit_scheduler = let new_fiber = Fiber_context.make_root () in Domain_local_await.using - ~prepare_for_await:Eio.Private.Dla.prepare_for_await + ~prepare_for_await:Eio_utils.Dla.prepare_for_await ~while_running:(fun () -> fork ~new_fiber (fun () -> Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () -> diff --git a/lib_eio_windows/sched.ml b/lib_eio_windows/sched.ml index 46a0ba650..fdf6e408f 100755 --- a/lib_eio_windows/sched.ml +++ b/lib_eio_windows/sched.ml @@ -370,7 +370,7 @@ let run ~extra_effects t main x = let `Exit_scheduler = let new_fiber = Fiber_context.make_root () in Domain_local_await.using - ~prepare_for_await:Eio.Private.Dla.prepare_for_await + ~prepare_for_await:Eio_utils.Dla.prepare_for_await ~while_running:(fun () -> fork ~new_fiber (fun () -> Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () -> From ecf63d45f18a7f3de985197ae71ae5566eb32d4f Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sun, 29 Sep 2024 13:13:26 +0100 Subject: [PATCH 21/66] eio_windows: unregister FDs on cancel This was fixed in cc19aa160626f5b for `eio_posix`, but not for `eio_windows`. The error looks like: exception Unix.Unix_error(Unix.ENOTSOCK, "select", "") --- lib_eio_posix/test/dune | 4 ++-- lib_eio_posix/test/test_await.ml | 25 ++++++++++++++++++++++ lib_eio_windows/sched.ml | 4 ++++ lib_eio_windows/test/test.ml | 36 ++++++++++++++++++++++++++++++-- 4 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 lib_eio_posix/test/test_await.ml diff --git a/lib_eio_posix/test/dune b/lib_eio_posix/test/dune index e6bf775b3..0fe02fffa 100644 --- a/lib_eio_posix/test/dune +++ b/lib_eio_posix/test/dune @@ -3,8 +3,8 @@ (enabled_if (= %{os_type} "Unix")) (deps (package eio_posix))) -(test - (name open_beneath) +(tests + (names open_beneath test_await) (package eio_posix) (build_if (= %{os_type} "Unix")) (libraries eio_posix)) diff --git a/lib_eio_posix/test/test_await.ml b/lib_eio_posix/test/test_await.ml new file mode 100644 index 000000000..d5533e631 --- /dev/null +++ b/lib_eio_posix/test/test_await.ml @@ -0,0 +1,25 @@ +open Eio.Std + +let () = + Eio_posix.run @@ fun _ -> + let a, b = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in + (* Start awaiting readable/writable state, but cancel immediately. *) + try + Eio.Cancel.sub (fun cc -> + Fiber.all [ + (fun () -> Eio_unix.await_readable a); + (fun () -> Eio_unix.await_writable b); + (fun () -> Eio.Cancel.cancel cc Exit); + ]; + assert false + ) + with Eio.Cancel.Cancelled _ -> + (* Now wait for something else. Will fail if the old FDs are still being waited on. *) + let c, d = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in + Unix.close a; + Unix.close b; + Fiber.first + (fun () -> Eio_unix.await_readable c) + (fun () -> Eio_unix.await_writable d); + Unix.close c; + Unix.close d diff --git a/lib_eio_windows/sched.ml b/lib_eio_windows/sched.ml index fdf6e408f..6b504d0d2 100755 --- a/lib_eio_windows/sched.ml +++ b/lib_eio_windows/sched.ml @@ -271,6 +271,8 @@ let await_readable t (k : unit Suspended.t) fd = if was_empty then update t waiters fd; Fiber_context.set_cancel_fn k.fiber (fun ex -> Lwt_dllist.remove node; + if Lwt_dllist.is_empty waiters.read then + update t waiters fd; t.active_ops <- t.active_ops - 1; enqueue_failed_thread t k ex ); @@ -287,6 +289,8 @@ let await_writable t (k : unit Suspended.t) fd = if was_empty then update t waiters fd; Fiber_context.set_cancel_fn k.fiber (fun ex -> Lwt_dllist.remove node; + if Lwt_dllist.is_empty waiters.write then + update t waiters fd; t.active_ops <- t.active_ops - 1; enqueue_failed_thread t k ex ); diff --git a/lib_eio_windows/test/test.ml b/lib_eio_windows/test/test.ml index 9d865beb2..a0c12eedd 100755 --- a/lib_eio_windows/test/test.ml +++ b/lib_eio_windows/test/test.ml @@ -1,3 +1,5 @@ +open Eio.Std + module Timeout = struct let test clock () = let t0 = Unix.gettimeofday () in @@ -48,6 +50,35 @@ module Dla = struct ] end +module Await_fd = struct + let test_cancel () = + let a, b = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in + (* Start awaiting readable/writable state, but cancel immediately. *) + try + Eio.Cancel.sub (fun cc -> + Fiber.all [ + (fun () -> Eio_unix.await_readable a); + (fun () -> Eio_unix.await_writable b); + (fun () -> Eio.Cancel.cancel cc Exit); + ]; + assert false + ) + with Eio.Cancel.Cancelled _ -> + (* Now wait for something else. Will fail if the old FDs are still being waited on. *) + let c, d = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in + Unix.close a; + Unix.close b; + Fiber.first + (fun () -> Eio_unix.await_readable c) + (fun () -> Eio_unix.await_writable d); + Unix.close c; + Unix.close d + + let tests = [ + "cancel", `Quick, test_cancel; + ] +end + let () = Eio_windows.run @@ fun env -> @@ -56,5 +87,6 @@ let () = "fs", Test_fs.tests env; "timeout", Timeout.tests env; "random", Random.tests env; - "dla", Dla.tests - ] \ No newline at end of file + "dla", Dla.tests; + "await", Await_fd.tests; + ] From c1b4a3a17423d621503f0f0dc5f351ff0ff382b3 Mon Sep 17 00:00:00 2001 From: Patrick Ferris Date: Sun, 6 Oct 2024 11:28:52 +0100 Subject: [PATCH 22/66] Add advice about using AI for code generation --- HACKING.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/HACKING.md b/HACKING.md index 906d8772f..13aa67990 100644 --- a/HACKING.md +++ b/HACKING.md @@ -135,4 +135,15 @@ Try to avoid making unnecessary changes; this makes review harder and clutters u `ocamlformat` may be useful to get badly messed up code to a baseline unformatted state, from which human formatting can be added where needed. +## AI-generated Code + +Contributing to Eio should not be done _solely_ using "AI tools" such as ChatGPT. This is for a few reasons: + +1. **It obfuscates how you think**. Purely AI-generated code tells us little about how you think and the problems you might be having. This makes it harder to provide good feedback on PRs and issues. +2. **It is often more work to review**. Particularly for the OCaml ecosystem and libraries like Eio, it seems that these tools are not very good and generate a lot of believable code that is in actual fact completely wrong. PR comments and the code submitted with them can say completely different things. +3. **It is a grey area for licensing**. Models like ChatGPT have been trained on lots of code with different licenses and has been known to simply copy code as an answer to a prompt. We would like to avoid this headache as best we can. + +Use AI tools, if you wish, to help you understand OCaml and Eio. Do not offload all of the work of a PR or a comment to these tools. + [dscheck]: https://github.com/ocaml-multicore/dscheck + From 2fc47fb402ac637836e55f3f8af20791d2258a8b Mon Sep 17 00:00:00 2001 From: Patrick Ferris Date: Thu, 17 Oct 2024 19:43:32 +0100 Subject: [PATCH 23/66] Make fork_action.h a public_header --- lib_eio/unix/dune | 1 + 1 file changed, 1 insertion(+) diff --git a/lib_eio/unix/dune b/lib_eio/unix/dune index d067e815c..e8afa88d8 100644 --- a/lib_eio/unix/dune +++ b/lib_eio/unix/dune @@ -1,6 +1,7 @@ (library (name eio_unix) (public_name eio.unix) + (public_headers include/fork_action.h) (foreign_stubs (language c) (include_dirs include) From b9bba299a21e03a622b08eecc542aff7ebaa1f50 Mon Sep 17 00:00:00 2001 From: Onah_Anthony <124398763+create2000@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:42:36 +0100 Subject: [PATCH 24/66] Check if windows has_symlink for tests (#771) Symlinking is privileged operation on Windows, so we check if the running user can make symlinks before running tests that require them. --- lib_eio_windows/test/test_fs.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib_eio_windows/test/test_fs.ml b/lib_eio_windows/test/test_fs.ml index 75435927b..9140d64d6 100755 --- a/lib_eio_windows/test/test_fs.ml +++ b/lib_eio_windows/test/test_fs.ml @@ -158,6 +158,9 @@ let test_symlink env () = Unix.mkdir "another" 0o700; print_endline @@ Unix.realpath "to-subdir" |} *) + if not (Unix.has_symlink ()) then + Printf.printf "Skipping test_symlink on systems that don't support symlinks.\n" + else let cwd = Eio.Stdenv.cwd env in try_mkdir (cwd / "sandbox"); Unix.symlink ~to_dir:true ".." "sandbox\\to-root"; @@ -277,4 +280,5 @@ let tests env = [ "unlink", `Quick, test_unlink env; "failing-unlink", `Quick, try_failing_unlink env; "rmdir", `Quick, test_remove_dir env; + "mkdirs", `Quick, test_mkdirs env; ] \ No newline at end of file From df2a1201111f703118cf9e93e7051bccbbc0d859 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 24 Oct 2024 15:02:52 +0300 Subject: [PATCH 25/66] implementing the chmod function in eio lib --- .gitignore | 1 + lib_eio/fs.ml | 1 + lib_eio/path.ml | 14 +++++++++++--- lib_eio/path.mli | 5 +++++ lib_eio_linux/eio_linux.ml | 7 +++++-- lib_eio_linux/eio_stubs.c | 14 ++++++++++++++ lib_eio_linux/low_level.ml | 14 ++++++++++++-- lib_eio_linux/low_level.mli | 3 +++ lib_eio_posix/eio_posix_stubs.c | 14 ++++++++++++++ lib_eio_posix/fs.ml | 3 +++ lib_eio_posix/low_level.ml | 9 +++++++++ lib_eio_posix/low_level.mli | 2 ++ lib_eio_windows/eio_windows_stubs.c | 1 + 13 files changed, 81 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 5f1f74725..4fb256dab 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ _opam .ocamlformat .*.swp *.install +.vscode diff --git a/lib_eio/fs.ml b/lib_eio/fs.ml index 34505c9f1..791f9566e 100644 --- a/lib_eio/fs.ml +++ b/lib_eio/fs.ml @@ -71,6 +71,7 @@ module Pi = struct val rename : t -> path -> _ dir -> path -> unit val read_link : t -> path -> string val symlink : link_to:path -> t -> path -> unit + val chmod : t -> follow:bool -> perm:File.Unix_perm.t -> path -> unit val pp : t Fmt.t val native : t -> string -> string option end diff --git a/lib_eio/path.ml b/lib_eio/path.ml index 37cd5ff09..97f79b2e5 100644 --- a/lib_eio/path.ml +++ b/lib_eio/path.ml @@ -214,9 +214,17 @@ let symlink ~link_to source = let (Resource.T (dir, ops), path) = source in let module X = (val (Resource.get ops Fs.Pi.Dir)) in try X.symlink dir path ~link_to - with Exn.Io _ as ex -> - let bt = Printexc.get_raw_backtrace () in - Exn.reraise_with_context ex bt "creating symlink %a -> %s" pp source link_to +with Exn.Io _ as ex -> + let bt = Printexc.get_raw_backtrace () in + Exn.reraise_with_context ex bt "creating symlink %a -> %s" pp source link_to + + let chmod ~follow ~perm t = + let (Resource.T (dir, ops), path) = t in + let module X = (val (Resource.get ops Fs.Pi.Dir)) in + try X.chmod dir ~follow ~perm path + with Exn.Io _ as ex -> + let bt = Printexc.get_raw_backtrace () in + Exn.reraise_with_context ex bt "chmoding file %a" pp t let rec mkdirs ?(exists_ok=false) ~perm t = (* Check parent exists first. *) diff --git a/lib_eio/path.mli b/lib_eio/path.mli index d147f5454..78b972dd4 100644 --- a/lib_eio/path.mli +++ b/lib_eio/path.mli @@ -217,3 +217,8 @@ val symlink : link_to:string -> _ t -> unit {[ Eio.Path.symlink (dir / "current") ~link_to:"version-1.0" ]} *) + +val chmod : follow:bool -> perm:int -> _ t -> unit +(** [chmod ~follow ~perm t] allows you to change the file mode bits. + + @param follow If [true] and [t] is a symlink then change the file mode bits target. *) \ No newline at end of file diff --git a/lib_eio_linux/eio_linux.ml b/lib_eio_linux/eio_linux.ml index ec9c8eb07..d5b5178c6 100644 --- a/lib_eio_linux/eio_linux.ml +++ b/lib_eio_linux/eio_linux.ml @@ -452,8 +452,11 @@ end = struct | Some fd2 -> Low_level.rename t.fd old_path fd2 new_path | None -> raise (Unix.Unix_error (Unix.EXDEV, "rename-dst", new_path)) - let symlink ~link_to t path = - Low_level.symlink ~link_to t.fd path +let symlink ~link_to t path = + Low_level.symlink ~link_to t.fd path + +let chmod t ~follow ~perm path = + Low_level.chmod t.fd ~follow ~perm path let pp f t = Fmt.string f (String.escaped t.label) diff --git a/lib_eio_linux/eio_stubs.c b/lib_eio_linux/eio_stubs.c index 4e4054104..88d757fac 100644 --- a/lib_eio_linux/eio_stubs.c +++ b/lib_eio_linux/eio_stubs.c @@ -115,6 +115,20 @@ CAMLprim value caml_eio_symlinkat(value v_old_path, value v_new_fd, value v_new_ CAMLreturn(Val_unit); } +CAMLprim value caml_eio_fchmodat(value v_fd, value v_path, value v_mode, value v_flags) { + CAMLparam1(v_path); + char *path; + int ret; + caml_unix_check_path(v_path, "fchmodat"); + path = caml_stat_strdup(String_val(v_path)); + caml_enter_blocking_section(); + ret = fchmodat(Int_val(v_fd), path, Int_val(v_mode), Int_val(v_flags)); + caml_leave_blocking_section(); + caml_stat_free(path); + if (ret == -1) uerror("fchmodat", v_path); + CAMLreturn(Val_unit); +} + CAMLprim value caml_eio_getrandom(value v_ba, value v_off, value v_len) { CAMLparam1(v_ba); ssize_t ret; diff --git a/lib_eio_linux/low_level.ml b/lib_eio_linux/low_level.ml index 42e72bfa3..95e9e696d 100644 --- a/lib_eio_linux/low_level.ml +++ b/lib_eio_linux/low_level.ml @@ -360,6 +360,8 @@ external eio_renameat : Unix.file_descr -> string -> Unix.file_descr -> string - external eio_symlinkat : string -> Unix.file_descr -> string -> unit = "caml_eio_symlinkat" +external eio_fchmodat : Unix.file_descr -> string -> int -> int -> unit = "caml_eio_fchmodat" + external eio_getrandom : Cstruct.buffer -> int -> int -> int = "caml_eio_getrandom" external eio_getdents : Unix.file_descr -> string list = "caml_eio_getdents" @@ -484,8 +486,16 @@ let symlink ~link_to dir path = with_parent_dir "symlinkat-new" dir path @@ fun parent leaf -> try eio_symlinkat link_to parent leaf - with Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap_fs code name arg - + with Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap_fs code name arg + + let chmod ~follow ~perm dir path = + let module X = Uring.Statx in + with_parent_dir "chmodat" dir path @@ fun parent leaf -> + let flags = if follow then 0 else (* at_symlink_nofollow *) 0x100 in + try + eio_fchmodat parent leaf perm flags + with Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap_fs code name arg + let shutdown socket command = try Fd.use_exn "shutdown" socket @@ fun fd -> diff --git a/lib_eio_linux/low_level.mli b/lib_eio_linux/low_level.mli index a3736e161..12339e3e7 100644 --- a/lib_eio_linux/low_level.mli +++ b/lib_eio_linux/low_level.mli @@ -153,6 +153,9 @@ val rename : dir_fd -> string -> dir_fd -> string -> unit val symlink : link_to:string -> dir_fd -> string -> unit (** [symlink ~link_to dir path] creates a new symlink at [dir / path] pointing to [link_to]. *) +val chmod : follow:bool -> perm:int -> dir_fd -> string -> unit +(** [chmod ~follow ~perm dir path] sets the permissions of [dir / path]. *) + val pipe : sw:Switch.t -> fd * fd (** [pipe ~sw] returns a pair [r, w] with the readable and writeable ends of a new pipe. *) diff --git a/lib_eio_posix/eio_posix_stubs.c b/lib_eio_posix/eio_posix_stubs.c index a3895f48c..4f6913300 100644 --- a/lib_eio_posix/eio_posix_stubs.c +++ b/lib_eio_posix/eio_posix_stubs.c @@ -405,6 +405,20 @@ CAMLprim value caml_eio_posix_symlinkat(value v_old_path, value v_new_fd, value CAMLreturn(Val_unit); } +CAMLprim value caml_eio_posix_fchmodat(value v_fd, value v_path, value v_mode, value v_flags) { + CAMLparam1(v_path); + char *path; + int ret; + caml_unix_check_path(v_path, "fchmodat"); + path = caml_stat_strdup(String_val(v_path)); + caml_enter_blocking_section(); + ret = fchmodat(Int_val(v_fd), path, Int_val(v_mode), Int_val(v_flags)); + caml_leave_blocking_section(); + caml_stat_free_preserving_errno(path); + if (ret == -1) uerror("fchmodat", v_path); + CAMLreturn(Val_unit); +} + CAMLprim value caml_eio_posix_spawn(value v_errors, value v_actions) { CAMLparam1(v_actions); pid_t child_pid; diff --git a/lib_eio_posix/fs.ml b/lib_eio_posix/fs.ml index 1a20523a0..c55447575 100644 --- a/lib_eio_posix/fs.ml +++ b/lib_eio_posix/fs.ml @@ -97,6 +97,9 @@ end = struct let symlink ~link_to t path = Err.run (Low_level.symlink ~link_to t.fd) path + let chmod t ~follow ~perm path = + Err.run (Low_level.chmod ~follow ~mode:perm t.fd) path + let open_dir t ~sw path = let flags = Low_level.Open_flags.(rdonly + directory +? path) in let fd = Err.run (Low_level.openat ~sw ~mode:0 t.fd path) flags in diff --git a/lib_eio_posix/low_level.ml b/lib_eio_posix/low_level.ml index 6026993d1..0f76e99bd 100644 --- a/lib_eio_posix/low_level.ml +++ b/lib_eio_posix/low_level.ml @@ -423,6 +423,15 @@ let symlink ~link_to new_dir new_path = let new_dir = Option.value new_dir ~default:at_fdcwd in eio_symlinkat link_to new_dir new_path +external eio_fchmodat : Unix.file_descr -> string -> int -> int -> unit = "caml_eio_posix_fchmodat" + +let chmod ~follow ~mode dir path = + in_worker_thread "chmod" @@ fun () -> + Resolve.with_parent "chmod" dir path @@ fun dir path -> + let new_dir = Option.value dir ~default:at_fdcwd in + let flags = if follow then 0 else Config.at_symlink_nofollow in + eio_fchmodat new_dir path mode flags + let read_link dirfd path = in_worker_thread "read_link" @@ fun () -> Resolve.with_parent "read_link" dirfd path @@ fun dirfd path -> diff --git a/lib_eio_posix/low_level.mli b/lib_eio_posix/low_level.mli index 69efe7207..00ec3af0d 100644 --- a/lib_eio_posix/low_level.mli +++ b/lib_eio_posix/low_level.mli @@ -81,6 +81,8 @@ val rename : dir_fd -> string -> dir_fd -> string -> unit val symlink : link_to:string -> dir_fd -> string -> unit (** [symlink ~link_to dir path] will create a new symlink at [dir / path] linking to [link_to]. *) + + val chmod : follow:bool -> mode:int -> dir_fd -> string -> unit val readdir : dir_fd -> string -> string array diff --git a/lib_eio_windows/eio_windows_stubs.c b/lib_eio_windows/eio_windows_stubs.c index 4e26e21c5..c3f85edd8 100755 --- a/lib_eio_windows/eio_windows_stubs.c +++ b/lib_eio_windows/eio_windows_stubs.c @@ -257,6 +257,7 @@ CAMLprim value caml_eio_windows_symlinkat(value v_old_path, value v_new_fd, valu uerror("symlinkat is not supported on windows yet", Nothing); } + CAMLprim value caml_eio_windows_spawn(value v_errors, value v_actions) { uerror("processes are not supported on windows yet", Nothing); From a16a43928c29c7e246acd664939484cd6c2b786e Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 24 Oct 2024 15:07:45 +0300 Subject: [PATCH 26/66] commit --- lib_eio/path.mli | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib_eio/path.mli b/lib_eio/path.mli index 78b972dd4..a4603fb26 100644 --- a/lib_eio/path.mli +++ b/lib_eio/path.mli @@ -219,6 +219,7 @@ val symlink : link_to:string -> _ t -> unit ]} *) val chmod : follow:bool -> perm:int -> _ t -> unit -(** [chmod ~follow ~perm t] allows you to change the file mode bits. - - @param follow If [true] and [t] is a symlink then change the file mode bits target. *) \ No newline at end of file +(** [chmod ~follow ~perm t] changes the permissions of [t] to [perm]. + + If [follow = true], the permissions of the target of a symlink are changed. + Otherwise, the permissions of the symlink itself are changed. *) \ No newline at end of file From 4bc7526c64324cfc2016aaab90cbdccb750b201b Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 24 Oct 2024 15:12:38 +0300 Subject: [PATCH 27/66] Update eio_linux.ml --- lib_eio_linux/eio_linux.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib_eio_linux/eio_linux.ml b/lib_eio_linux/eio_linux.ml index d5b5178c6..3af48540e 100644 --- a/lib_eio_linux/eio_linux.ml +++ b/lib_eio_linux/eio_linux.ml @@ -452,11 +452,11 @@ end = struct | Some fd2 -> Low_level.rename t.fd old_path fd2 new_path | None -> raise (Unix.Unix_error (Unix.EXDEV, "rename-dst", new_path)) -let symlink ~link_to t path = - Low_level.symlink ~link_to t.fd path - -let chmod t ~follow ~perm path = - Low_level.chmod t.fd ~follow ~perm path + let symlink ~link_to t path = + Low_level.symlink ~link_to t.fd path + + let chmod t ~follow ~perm path = + Low_level.chmod t.fd ~follow ~perm path let pp f t = Fmt.string f (String.escaped t.label) From 68f1ab98a878074e826246130a15c404f04677bf Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Fri, 25 Oct 2024 22:30:57 +0300 Subject: [PATCH 28/66] COMMIT --- examples/chmod/dune | 3 +++ examples/chmod/main.ml | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 examples/chmod/dune create mode 100644 examples/chmod/main.ml diff --git a/examples/chmod/dune b/examples/chmod/dune new file mode 100644 index 000000000..c1c960747 --- /dev/null +++ b/examples/chmod/dune @@ -0,0 +1,3 @@ +(executable + (name main) + (libraries eio eio_linux unix)) \ No newline at end of file diff --git a/examples/chmod/main.ml b/examples/chmod/main.ml new file mode 100644 index 000000000..77c752f5b --- /dev/null +++ b/examples/chmod/main.ml @@ -0,0 +1,30 @@ +open Eio.Std + +let get_permissions path env = + let fs = Eio.Stdenv.fs env in + + let full_path = Eio.Path.(fs / path) in + + let stats = Eio.Path.stat ~follow:true full_path in + stats.perm + +let test_chmod env = + Switch.run @@ fun sw -> + let fs = Eio.Stdenv.fs env in + let tmp_dir = Eio.Path.(fs / "tmp") in + let file_path = Eio.Path.(tmp_dir / "test_file") in + Eio.Path.(open_out ~sw ~create:(`Exclusive 0o644) file_path) |> Eio.Flow.close; + + let permissions = get_permissions (Eio.Path.native_exn file_path) env in + Printf.printf "Permissions: %o\n" permissions; + + Eio.Path.chmod ~follow:true ~perm:0o600 file_path; + + let new_permissions = get_permissions (Eio.Path.native_exn file_path) env in + Printf.printf "New Permissions: %o\n" new_permissions; + () + + +let () = + Eio_linux.run @@ fun env -> + test_chmod env From 5980592f7d97c8e1f4947475add6dc195e56c61f Mon Sep 17 00:00:00 2001 From: webbunivAdmin Date: Fri, 25 Oct 2024 23:39:53 +0300 Subject: [PATCH 29/66] COMMIT --- examples/chmod/dune | 2 +- examples/chmod/main.ml | 4 ++-- tmp/test_file | 0 3 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 tmp/test_file diff --git a/examples/chmod/dune b/examples/chmod/dune index c1c960747..cc9484120 100644 --- a/examples/chmod/dune +++ b/examples/chmod/dune @@ -1,3 +1,3 @@ (executable (name main) - (libraries eio eio_linux unix)) \ No newline at end of file + (libraries eio eio_main unix)) \ No newline at end of file diff --git a/examples/chmod/main.ml b/examples/chmod/main.ml index 77c752f5b..9da9563d6 100644 --- a/examples/chmod/main.ml +++ b/examples/chmod/main.ml @@ -13,7 +13,7 @@ let test_chmod env = let fs = Eio.Stdenv.fs env in let tmp_dir = Eio.Path.(fs / "tmp") in let file_path = Eio.Path.(tmp_dir / "test_file") in - Eio.Path.(open_out ~sw ~create:(`Exclusive 0o644) file_path) |> Eio.Flow.close; + Eio.Path.(open_out ~sw ~create:(`If_missing 0o600) file_path) |> Eio.Flow.close; let permissions = get_permissions (Eio.Path.native_exn file_path) env in Printf.printf "Permissions: %o\n" permissions; @@ -26,5 +26,5 @@ let test_chmod env = let () = - Eio_linux.run @@ fun env -> + Eio_main.run @@ fun env -> test_chmod env diff --git a/tmp/test_file b/tmp/test_file new file mode 100644 index 000000000..e69de29bb From ace1f49192c1d20a8112cc777747c39e59103408 Mon Sep 17 00:00:00 2001 From: webbunivAdmin Date: Fri, 25 Oct 2024 23:45:44 +0300 Subject: [PATCH 30/66] Update main.ml Co-Authored-By: Patrick Ferris --- examples/chmod/main.ml | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/chmod/main.ml b/examples/chmod/main.ml index 9da9563d6..39e47d13a 100644 --- a/examples/chmod/main.ml +++ b/examples/chmod/main.ml @@ -2,9 +2,7 @@ open Eio.Std let get_permissions path env = let fs = Eio.Stdenv.fs env in - let full_path = Eio.Path.(fs / path) in - let stats = Eio.Path.stat ~follow:true full_path in stats.perm @@ -24,7 +22,6 @@ let test_chmod env = Printf.printf "New Permissions: %o\n" new_permissions; () - let () = Eio_main.run @@ fun env -> test_chmod env From a294b64798680290c3cfa8182e3ea84a9f1dbbdc Mon Sep 17 00:00:00 2001 From: webbunivAdmin Date: Sat, 26 Oct 2024 00:22:01 +0300 Subject: [PATCH 31/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/fs.md b/tests/fs.md index 446874ad3..a2c23cf77 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -94,6 +94,11 @@ let try_symlink ~link_to path = match Path.symlink ~link_to path with | s -> traceln "symlink %a -> %S" Path.pp path link_to | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex + +let try_chmod ~perm path = + match Eio.Path.chmod ~perm path with + | () -> Eio.traceln "chmod %o -> %a" perm Eio.Path.pp path + | exception ex -> Eio.traceln "@[%a@]" Eio.Exn.pp ex ``` # Basic test cases @@ -228,7 +233,7 @@ Appending to an existing file: - : unit = () ``` -Creating directories with nesting, symlinks, etc: +Creating directories with nesting, symlinks, chmond, etc: ```ocaml # run ~clear:["to-subdir"; "to-root"; "dangle"] @@ fun env -> @@ -242,6 +247,10 @@ Creating directories with nesting, symlinks, etc: try_mkdir (cwd / "../foo"); try_mkdir (cwd / "to-subdir"); try_mkdir (cwd / "dangle/foo"); + try_chmod ~perm:0o777 (cwd / "to-root"); + try_chmod ~perm:0o777 (cwd / "to-subdir"); + try_chmod ~perm:0o777 (cwd / "dangle"); + try_chmod ~perm:0o777 (cwd / "dangle/foo"); ();; +mkdir -> ok +mkdir -> ok From 94fae9ebbf14127064b34e0ad40c4128dbc31a4f Mon Sep 17 00:00:00 2001 From: webbunivAdmin Date: Sat, 26 Oct 2024 00:25:43 +0300 Subject: [PATCH 32/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index a2c23cf77..a5914eaf1 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -247,10 +247,6 @@ Creating directories with nesting, symlinks, chmond, etc: try_mkdir (cwd / "../foo"); try_mkdir (cwd / "to-subdir"); try_mkdir (cwd / "dangle/foo"); - try_chmod ~perm:0o777 (cwd / "to-root"); - try_chmod ~perm:0o777 (cwd / "to-subdir"); - try_chmod ~perm:0o777 (cwd / "dangle"); - try_chmod ~perm:0o777 (cwd / "dangle/foo"); ();; +mkdir -> ok +mkdir -> ok From 112a26178dde3324535a603a10c5d658aed8cc21 Mon Sep 17 00:00:00 2001 From: webbunivAdmin Date: Sat, 26 Oct 2024 00:32:12 +0300 Subject: [PATCH 33/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index a5914eaf1..6f586ad6d 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -95,10 +95,10 @@ let try_symlink ~link_to path = | s -> traceln "symlink %a -> %S" Path.pp path link_to | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex -let try_chmod ~perm path = +(* let try_chmod ~perm path = match Eio.Path.chmod ~perm path with | () -> Eio.traceln "chmod %o -> %a" perm Eio.Path.pp path - | exception ex -> Eio.traceln "@[%a@]" Eio.Exn.pp ex + | exception ex -> Eio.traceln "@[%a@]" Eio.Exn.pp ex *) ``` # Basic test cases From dc85be09bbcebb4dac1a512fca21eae37ed729b3 Mon Sep 17 00:00:00 2001 From: webbunivAdmin Date: Sat, 26 Oct 2024 00:36:59 +0300 Subject: [PATCH 34/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index 6f586ad6d..ff8131f05 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -95,10 +95,10 @@ let try_symlink ~link_to path = | s -> traceln "symlink %a -> %S" Path.pp path link_to | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex -(* let try_chmod ~perm path = +let try_chmod ~perm path = match Eio.Path.chmod ~perm path with - | () -> Eio.traceln "chmod %o -> %a" perm Eio.Path.pp path - | exception ex -> Eio.traceln "@[%a@]" Eio.Exn.pp ex *) + | () -> Eio.traceln "Permissions changed to %o for %a" perm Eio.Path.pp path + | exception ex -> Eio.traceln "Failed to change permissions: %a" Eio.Exn.pp ex ``` # Basic test cases @@ -233,7 +233,7 @@ Appending to an existing file: - : unit = () ``` -Creating directories with nesting, symlinks, chmond, etc: +Creating directories with nesting, symlinks, etc: ```ocaml # run ~clear:["to-subdir"; "to-root"; "dangle"] @@ fun env -> From dc5a0d8424a490820c74c39065910a7ac50bda86 Mon Sep 17 00:00:00 2001 From: webbunivAdmin Date: Sat, 26 Oct 2024 00:38:42 +0300 Subject: [PATCH 35/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index ff8131f05..a868e075d 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -95,10 +95,6 @@ let try_symlink ~link_to path = | s -> traceln "symlink %a -> %S" Path.pp path link_to | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex -let try_chmod ~perm path = - match Eio.Path.chmod ~perm path with - | () -> Eio.traceln "Permissions changed to %o for %a" perm Eio.Path.pp path - | exception ex -> Eio.traceln "Failed to change permissions: %a" Eio.Exn.pp ex ``` # Basic test cases From e425842a8aa2ceed5da60b1ad3f836ea4e62f7a7 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Mon, 28 Oct 2024 13:43:09 +0300 Subject: [PATCH 36/66] tests on the ``chmod`` for eio Co-Authored-By: Patrick Ferris --- examples/chmod/dune | 3 --- examples/chmod/main.ml | 27 --------------------------- tests/fs.md | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 30 deletions(-) delete mode 100644 examples/chmod/dune delete mode 100644 examples/chmod/main.ml diff --git a/examples/chmod/dune b/examples/chmod/dune deleted file mode 100644 index cc9484120..000000000 --- a/examples/chmod/dune +++ /dev/null @@ -1,3 +0,0 @@ -(executable - (name main) - (libraries eio eio_main unix)) \ No newline at end of file diff --git a/examples/chmod/main.ml b/examples/chmod/main.ml deleted file mode 100644 index 39e47d13a..000000000 --- a/examples/chmod/main.ml +++ /dev/null @@ -1,27 +0,0 @@ -open Eio.Std - -let get_permissions path env = - let fs = Eio.Stdenv.fs env in - let full_path = Eio.Path.(fs / path) in - let stats = Eio.Path.stat ~follow:true full_path in - stats.perm - -let test_chmod env = - Switch.run @@ fun sw -> - let fs = Eio.Stdenv.fs env in - let tmp_dir = Eio.Path.(fs / "tmp") in - let file_path = Eio.Path.(tmp_dir / "test_file") in - Eio.Path.(open_out ~sw ~create:(`If_missing 0o600) file_path) |> Eio.Flow.close; - - let permissions = get_permissions (Eio.Path.native_exn file_path) env in - Printf.printf "Permissions: %o\n" permissions; - - Eio.Path.chmod ~follow:true ~perm:0o600 file_path; - - let new_permissions = get_permissions (Eio.Path.native_exn file_path) env in - Printf.printf "New Permissions: %o\n" new_permissions; - () - -let () = - Eio_main.run @@ fun env -> - test_chmod env diff --git a/tests/fs.md b/tests/fs.md index a868e075d..740553c2a 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -95,6 +95,11 @@ let try_symlink ~link_to path = | s -> traceln "symlink %a -> %S" Path.pp path link_to | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex +let try_chmod ~perm path = + match Eio.Path.chmod ~perm path with + | () -> Eio.traceln "chmod %o -> %a" perm Path.pp path + | exception ex -> Eio.traceln "@[%a@]" Eio.Exn.pp ex + ``` # Basic test cases @@ -1012,3 +1017,33 @@ Exception: Failure "Simulated error". +"/" / "" = "/" - : unit = () ``` +# Testing ``chmod`` + +```ocaml +let chmod_test_directory () = + run ~clear:["test-dir"] @@ fun env -> + let cwd = Eio.Stdenv.cwd env in + let dir_path = cwd / "test-dir" in + + try_mkdir dir_path; + + try_chmod ~perm:0o700 dir_path; + let dir_stat = Unix.stat "test-dir" in + Printf.printf "Directory permissions after chmod 0o700: %o\n" dir_stat.st_perm; + assert (dir_stat.st_perm = 0o700); + + try_chmod ~perm:0o755 dir_path; + let dir_stat = Unix.stat "test-dir" in + Printf.printf "Directory permissions after chmod 0o755: %o\n" dir_stat.st_perm; + assert (dir_stat.st_perm = 0o755); + + Unix.rmdir "test-dir"; + () + +let () = + Printf.printf "Running chmod tests...\n"; + chmod_test_file (); + chmod_test_directory (); + Printf.printf "chmod tests completed successfully.\n" + +``` \ No newline at end of file From e21891760079b3e6d8cd5a4c65a35e88202f4688 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Mon, 28 Oct 2024 13:49:13 +0300 Subject: [PATCH 37/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index 740553c2a..cd3ecad8f 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -18,7 +18,6 @@ open Eio.Std let ( / ) = Path.( / ) let run ?clear:(paths = []) fn = - Eio_main.run @@ fun env -> let cwd = Eio.Stdenv.cwd env in List.iter (fun p -> Eio.Path.rmtree ~missing_ok:true (cwd / p)) paths; fn env @@ -99,7 +98,6 @@ let try_chmod ~perm path = match Eio.Path.chmod ~perm path with | () -> Eio.traceln "chmod %o -> %a" perm Path.pp path | exception ex -> Eio.traceln "@[%a@]" Eio.Exn.pp ex - ``` # Basic test cases @@ -1017,33 +1015,3 @@ Exception: Failure "Simulated error". +"/" / "" = "/" - : unit = () ``` -# Testing ``chmod`` - -```ocaml -let chmod_test_directory () = - run ~clear:["test-dir"] @@ fun env -> - let cwd = Eio.Stdenv.cwd env in - let dir_path = cwd / "test-dir" in - - try_mkdir dir_path; - - try_chmod ~perm:0o700 dir_path; - let dir_stat = Unix.stat "test-dir" in - Printf.printf "Directory permissions after chmod 0o700: %o\n" dir_stat.st_perm; - assert (dir_stat.st_perm = 0o700); - - try_chmod ~perm:0o755 dir_path; - let dir_stat = Unix.stat "test-dir" in - Printf.printf "Directory permissions after chmod 0o755: %o\n" dir_stat.st_perm; - assert (dir_stat.st_perm = 0o755); - - Unix.rmdir "test-dir"; - () - -let () = - Printf.printf "Running chmod tests...\n"; - chmod_test_file (); - chmod_test_directory (); - Printf.printf "chmod tests completed successfully.\n" - -``` \ No newline at end of file From 171915155868b0c397d9b51cc3241e826b1ee178 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Mon, 28 Oct 2024 14:35:14 +0300 Subject: [PATCH 38/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/fs.md b/tests/fs.md index cd3ecad8f..5a3151220 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -95,9 +95,10 @@ let try_symlink ~link_to path = | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex let try_chmod ~perm path = - match Eio.Path.chmod ~perm path with + match Path.chmod ~perm path with | () -> Eio.traceln "chmod %o -> %a" perm Path.pp path | exception ex -> Eio.traceln "@[%a@]" Eio.Exn.pp ex + ``` # Basic test cases @@ -848,6 +849,7 @@ Unconfined: - : unit = () ``` + # read_link ```ocaml @@ -1015,3 +1017,23 @@ Exception: Failure "Simulated error". +"/" / "" = "/" - : unit = () ``` + +```ocaml +run ~clear:["stat_subdir2"; "symlink"; "broken-symlink"; "parent-symlink"] @@ fun env -> + let cwd = Eio.Stdenv.cwd env in + Switch.run @@ fun sw -> + Path.mkdir (cwd / "stat_subdir2"); + Path.symlink ~link_to:"stat_subdir2" (cwd / "symlink"); + Path.symlink ~link_to:"missing" (cwd / "broken-symlink"); + try_stat (cwd / "stat_subdir2"); + try_stat (cwd / "symlink"); + try_stat (cwd / "broken-symlink"); + try_chmod ~perm:0o777 (cwd / "stat_subdir2"); + try_stat cwd; + try_stat (cwd / ".."); + try_stat (cwd / "stat_subdir2/.."); + Path.symlink ~link_to:".." (cwd / "parent-symlink"); + try_stat (cwd / "parent-symlink"); + try_stat (cwd / "missing1" / "missing2"); + (); +``` From 662792a1807f24e4c4b2c02699ed3348f5171fa3 Mon Sep 17 00:00:00 2001 From: webbunivAdmin Date: Sun, 17 Nov 2024 11:16:12 +0300 Subject: [PATCH 39/66] Consolidate updates to fs.md --- lib_eio/path.ml | 23 +++++----- lib_eio_linux/low_level.ml | 4 +- lib_eio_posix/low_level.mli | 2 +- lib_eio_windows/eio_windows_stubs.c | 3 ++ lib_eio_windows/fs.ml | 5 +++ tests/fs.md | 66 ++++++++++++++++++----------- tmp/test_file | 0 7 files changed, 64 insertions(+), 39 deletions(-) delete mode 100644 tmp/test_file diff --git a/lib_eio/path.ml b/lib_eio/path.ml index 97f79b2e5..d428a64c4 100644 --- a/lib_eio/path.ml +++ b/lib_eio/path.ml @@ -214,17 +214,18 @@ let symlink ~link_to source = let (Resource.T (dir, ops), path) = source in let module X = (val (Resource.get ops Fs.Pi.Dir)) in try X.symlink dir path ~link_to -with Exn.Io _ as ex -> - let bt = Printexc.get_raw_backtrace () in - Exn.reraise_with_context ex bt "creating symlink %a -> %s" pp source link_to - - let chmod ~follow ~perm t = - let (Resource.T (dir, ops), path) = t in - let module X = (val (Resource.get ops Fs.Pi.Dir)) in - try X.chmod dir ~follow ~perm path - with Exn.Io _ as ex -> - let bt = Printexc.get_raw_backtrace () in - Exn.reraise_with_context ex bt "chmoding file %a" pp t + with Exn.Io _ as ex -> + let bt = Printexc.get_raw_backtrace () in + Exn.reraise_with_context ex bt "creating symlink %a -> %s" pp source link_to + +let chmod ~follow ~perm t = + let (Resource.T (dir, ops), path) = t in + let module X = (val (Resource.get ops Fs.Pi.Dir)) in + try + X.chmod dir ~follow ~perm path + with Exn.Io _ as ex -> + let bt = Printexc.get_raw_backtrace () in + Exn.reraise_with_context ex bt "chmoding file %a" pp t let rec mkdirs ?(exists_ok=false) ~perm t = (* Check parent exists first. *) diff --git a/lib_eio_linux/low_level.ml b/lib_eio_linux/low_level.ml index 95e9e696d..4ca81a186 100644 --- a/lib_eio_linux/low_level.ml +++ b/lib_eio_linux/low_level.ml @@ -486,12 +486,12 @@ let symlink ~link_to dir path = with_parent_dir "symlinkat-new" dir path @@ fun parent leaf -> try eio_symlinkat link_to parent leaf - with Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap_fs code name arg + with Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap_fs code name arg let chmod ~follow ~perm dir path = let module X = Uring.Statx in with_parent_dir "chmodat" dir path @@ fun parent leaf -> - let flags = if follow then 0 else (* at_symlink_nofollow *) 0x100 in + let flags = if follow then 0 else 0x100 in try eio_fchmodat parent leaf perm flags with Unix.Unix_error (code, name, arg) -> raise @@ Err.wrap_fs code name arg diff --git a/lib_eio_posix/low_level.mli b/lib_eio_posix/low_level.mli index 00ec3af0d..3d172a36a 100644 --- a/lib_eio_posix/low_level.mli +++ b/lib_eio_posix/low_level.mli @@ -82,7 +82,7 @@ val symlink : link_to:string -> dir_fd -> string -> unit (** [symlink ~link_to dir path] will create a new symlink at [dir / path] linking to [link_to]. *) - val chmod : follow:bool -> mode:int -> dir_fd -> string -> unit +val chmod : follow:bool -> mode:int -> dir_fd -> string -> unit val readdir : dir_fd -> string -> string array diff --git a/lib_eio_windows/eio_windows_stubs.c b/lib_eio_windows/eio_windows_stubs.c index c3f85edd8..ffd2deb28 100755 --- a/lib_eio_windows/eio_windows_stubs.c +++ b/lib_eio_windows/eio_windows_stubs.c @@ -257,6 +257,9 @@ CAMLprim value caml_eio_windows_symlinkat(value v_old_path, value v_new_fd, valu uerror("symlinkat is not supported on windows yet", Nothing); } +CAMLprim value eio_windows_chmod(value path, value perm) { + caml_failwith("chmod is not implemented on Windows"); +} CAMLprim value caml_eio_windows_spawn(value v_errors, value v_actions) { diff --git a/lib_eio_windows/fs.ml b/lib_eio_windows/fs.ml index d877b6fb5..18c2e6cfa 100755 --- a/lib_eio_windows/fs.ml +++ b/lib_eio_windows/fs.ml @@ -89,6 +89,11 @@ end = struct let v ~label ~sandbox dir_path = { dir_path; sandbox; label; closed = false } + let chmod (_t : t) ~(follow : bool) ~(perm : int) (_path : string) : unit = + ignore (follow); + ignore (perm); + failwith "chmod not implemented on Windows yet" + (* Sandboxes use [O_NOFOLLOW] when opening files ([resolve] already removed any symlinks). This avoids a race where symlink might be added after [realpath] returns. TODO: Emulate [O_NOFOLLOW] here. *) diff --git a/tests/fs.md b/tests/fs.md index 5a3151220..a134fe6ad 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -14,10 +14,12 @@ module Path = Eio.Path let () = Eio.Exn.Backend.show := false open Eio.Std +open Eio.Exn let ( / ) = Path.( / ) let run ?clear:(paths = []) fn = + Eio_main.run @@ fun env -> let cwd = Eio.Stdenv.cwd env in List.iter (fun p -> Eio.Path.rmtree ~missing_ok:true (cwd / p)) paths; fn env @@ -76,12 +78,13 @@ let chdir path = traceln "chdir %S" path; Unix.chdir path -let try_stat path = +let try_stat ?(info_type=`Kind) path = let stat ~follow = - match Eio.Path.stat ~follow path with - | info -> Fmt.str "@[%a@]" Eio.File.Stat.pp_kind info.kind - | exception Eio.Io (e, _) -> Fmt.str "@[%a@]" Eio.Exn.pp_err e - in + match Eio.Path.stat ~follow path, info_type with + | info, `Perm -> Fmt.str "@[%o@]" info.perm + | info, `Kind -> Fmt.str "@[%a@]" Eio.File.Stat.pp_kind info.kind + | exception Eio.Io (e, _) -> Fmt.str "@[%a@]" Eio.Exn.pp_err e + in let a = stat ~follow:false in let b = stat ~follow:true in if a = b then @@ -94,10 +97,10 @@ let try_symlink ~link_to path = | s -> traceln "symlink %a -> %S" Path.pp path link_to | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex -let try_chmod ~perm path = - match Path.chmod ~perm path with - | () -> Eio.traceln "chmod %o -> %a" perm Path.pp path - | exception ex -> Eio.traceln "@[%a@]" Eio.Exn.pp ex +let try_chmod path ~follow ~perm = + match Eio.Path.chmod ~follow path ~perm with + | () -> traceln "chmod %a to %o -> ok" Path.pp path perm + | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex ``` @@ -834,6 +837,7 @@ Unconfined: try_stat cwd; try_stat (cwd / ".."); try_stat (cwd / "stat_subdir2/.."); + Path.symlink ~link_to:".." (cwd / "parent-symlink"); try_stat (cwd / "parent-symlink"); try_stat (cwd / "missing1" / "missing2"); @@ -849,7 +853,6 @@ Unconfined: - : unit = () ``` - # read_link ```ocaml @@ -1019,21 +1022,34 @@ Exception: Failure "Simulated error". ``` ```ocaml -run ~clear:["stat_subdir2"; "symlink"; "broken-symlink"; "parent-symlink"] @@ fun env -> +# run ~clear:["test-file"] @@ fun env -> let cwd = Eio.Stdenv.cwd env in Switch.run @@ fun sw -> - Path.mkdir (cwd / "stat_subdir2"); - Path.symlink ~link_to:"stat_subdir2" (cwd / "symlink"); - Path.symlink ~link_to:"missing" (cwd / "broken-symlink"); - try_stat (cwd / "stat_subdir2"); - try_stat (cwd / "symlink"); - try_stat (cwd / "broken-symlink"); - try_chmod ~perm:0o777 (cwd / "stat_subdir2"); - try_stat cwd; - try_stat (cwd / ".."); - try_stat (cwd / "stat_subdir2/.."); - Path.symlink ~link_to:".." (cwd / "parent-symlink"); - try_stat (cwd / "parent-symlink"); - try_stat (cwd / "missing1" / "missing2"); - (); + + let file_path = cwd / "test-file" in + Path.save ~create:(`Exclusive 0o644) file_path "test data"; + traceln "+create with permissions 0o644 -> ok"; + + let initial_perm = (Path.stat ~follow:true file_path).perm in + traceln "+ initial permissions = %o" initial_perm; + assert (initial_perm = 0o644); + + try_chmod ~follow:true ~perm:0o400 file_path; + + try_stat ~info_type:`Perm file_path; + + try_chmod ~follow:true ~perm:0o600 file_path; + try_stat ~info_type:`Perm file_path; + + Eio.Path.unlink file_path; + traceln "+unlink -> ok"; + () +++create with permissions 0o644 -> ok +++ initial permissions = 644 ++chmod to 400 -> ok ++ -> 400 ++chmod to 600 -> ok ++ -> 600 +++unlink -> ok +- : unit = () ``` diff --git a/tmp/test_file b/tmp/test_file deleted file mode 100644 index e69de29bb..000000000 From 651ebd5ef10ffc6dc5cca561501bdc4c1f6cc047 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Tue, 29 Oct 2024 17:33:25 +0300 Subject: [PATCH 40/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/fs.md b/tests/fs.md index a134fe6ad..4c369b6b0 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -101,7 +101,6 @@ let try_chmod path ~follow ~perm = match Eio.Path.chmod ~follow path ~perm with | () -> traceln "chmod %a to %o -> ok" Path.pp path perm | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - ``` # Basic test cases From ba943ff682ecb0bcd3c78a223301faa98fe6564e Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Tue, 29 Oct 2024 19:11:52 +0300 Subject: [PATCH 41/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fs.md b/tests/fs.md index 4c369b6b0..a134fe6ad 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -101,6 +101,7 @@ let try_chmod path ~follow ~perm = match Eio.Path.chmod ~follow path ~perm with | () -> traceln "chmod %a to %o -> ok" Path.pp path perm | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex + ``` # Basic test cases From 0bb124d83b3217ce6bf99932aedd5feae9662ef7 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 11:11:50 +0300 Subject: [PATCH 42/66] Update fs.ml Co-Authored-By: Patrick Ferris --- lib_eio_windows/fs.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib_eio_windows/fs.ml b/lib_eio_windows/fs.ml index 18c2e6cfa..b6df8d48f 100755 --- a/lib_eio_windows/fs.ml +++ b/lib_eio_windows/fs.ml @@ -181,6 +181,9 @@ end = struct with_parent_dir t path @@ fun dirfd path -> Err.run (Low_level.symlink ~link_to dirfd) path + let chmod t ~follow ~perm path = + failwith "TODO: Implement Windows chmod functionality" + let close t = t.closed <- true let open_dir t ~sw path = From 75732f175a9bf361eca5a86fe9d7766562f17650 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 12:34:58 +0300 Subject: [PATCH 43/66] trying to remove some bugs from the tests Co-Authored-By: Patrick Ferris --- lib_eio_windows/fs.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib_eio_windows/fs.ml b/lib_eio_windows/fs.ml index b6df8d48f..4ff42868c 100755 --- a/lib_eio_windows/fs.ml +++ b/lib_eio_windows/fs.ml @@ -181,9 +181,7 @@ end = struct with_parent_dir t path @@ fun dirfd path -> Err.run (Low_level.symlink ~link_to dirfd) path - let chmod t ~follow ~perm path = - failwith "TODO: Implement Windows chmod functionality" - + let close t = t.closed <- true let open_dir t ~sw path = From 02225be65cc133c70d1b6b0a638c11242a92aed2 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 13:33:41 +0300 Subject: [PATCH 44/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fs.md b/tests/fs.md index a134fe6ad..5575ecc36 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -831,6 +831,7 @@ Unconfined: try_mkdir (cwd / "stat_subdir2"); Path.symlink ~link_to:"stat_subdir2" (cwd / "symlink"); Path.symlink ~link_to:"missing" (cwd / "broken-symlink"); + try_chmod (cwd / "stat_subdir2") ~follow:true ~perm:0o755; try_stat (cwd / "stat_subdir2"); try_stat (cwd / "symlink"); try_stat (cwd / "broken-symlink"); From 9390eb4f5ed107db0e384fa4f07c483d4026d2ad Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 13:35:11 +0300 Subject: [PATCH 45/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/fs.md b/tests/fs.md index 5575ecc36..a134fe6ad 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -831,7 +831,6 @@ Unconfined: try_mkdir (cwd / "stat_subdir2"); Path.symlink ~link_to:"stat_subdir2" (cwd / "symlink"); Path.symlink ~link_to:"missing" (cwd / "broken-symlink"); - try_chmod (cwd / "stat_subdir2") ~follow:true ~perm:0o755; try_stat (cwd / "stat_subdir2"); try_stat (cwd / "symlink"); try_stat (cwd / "broken-symlink"); From 17a20aab673ba3f465c331e4df0e67f098f7fe56 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 13:40:37 +0300 Subject: [PATCH 46/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/fs.md b/tests/fs.md index a134fe6ad..470365155 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -838,6 +838,9 @@ Unconfined: try_stat (cwd / ".."); try_stat (cwd / "stat_subdir2/.."); + try_chmod cwd ~follow:true ~perm:0o755; + try_stat cwd; + Path.symlink ~link_to:".." (cwd / "parent-symlink"); try_stat (cwd / "parent-symlink"); try_stat (cwd / "missing1" / "missing2"); From e54ce95b1f7d43b42b8859f9670198968fe21138 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 16:05:53 +0300 Subject: [PATCH 47/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index 470365155..a134fe6ad 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -838,9 +838,6 @@ Unconfined: try_stat (cwd / ".."); try_stat (cwd / "stat_subdir2/.."); - try_chmod cwd ~follow:true ~perm:0o755; - try_stat cwd; - Path.symlink ~link_to:".." (cwd / "parent-symlink"); try_stat (cwd / "parent-symlink"); try_stat (cwd / "missing1" / "missing2"); From 84cc303869933b83a56a79869a7443f2f195c858 Mon Sep 17 00:00:00 2001 From: webbunivAdmin Date: Sat, 2 Nov 2024 11:06:30 +0300 Subject: [PATCH 48/66] Update fs.md --- tests/fs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fs.md b/tests/fs.md index a134fe6ad..436fd1fd9 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -102,6 +102,7 @@ let try_chmod path ~follow ~perm = | () -> traceln "chmod %a to %o -> ok" Path.pp path perm | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex + ``` # Basic test cases From 04843a7ece40df4244856ff5e7be60499865da3d Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Mon, 4 Nov 2024 12:00:48 +0300 Subject: [PATCH 49/66] try_chmod implementation --- tests/fs.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index 436fd1fd9..fad97cd32 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -97,10 +97,10 @@ let try_symlink ~link_to path = | s -> traceln "symlink %a -> %S" Path.pp path link_to | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex -let try_chmod path ~follow ~perm = - match Eio.Path.chmod ~follow path ~perm with - | () -> traceln "chmod %a to %o -> ok" Path.pp path perm - | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex +let try_chmod ~follow ~perm path = + Path.chmod ~follow ~perm path; + traceln "Chmod applied on %s with permissions %o" path perm + ``` From 34e7ac46b993a99151ea22e0c368bb2e6a4182f0 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Mon, 4 Nov 2024 13:58:10 +0300 Subject: [PATCH 50/66] following the comments --- lib_eio_windows/fs.ml | 1 - tests/fs.md | 2 -- 2 files changed, 3 deletions(-) diff --git a/lib_eio_windows/fs.ml b/lib_eio_windows/fs.ml index 4ff42868c..18c2e6cfa 100755 --- a/lib_eio_windows/fs.ml +++ b/lib_eio_windows/fs.ml @@ -181,7 +181,6 @@ end = struct with_parent_dir t path @@ fun dirfd path -> Err.run (Low_level.symlink ~link_to dirfd) path - let close t = t.closed <- true let open_dir t ~sw path = diff --git a/tests/fs.md b/tests/fs.md index fad97cd32..722ce8b8d 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -101,8 +101,6 @@ let try_chmod ~follow ~perm path = Path.chmod ~follow ~perm path; traceln "Chmod applied on %s with permissions %o" path perm - - ``` # Basic test cases From 650088b2eb2c2a7f05429b0164489f5125525634 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Mon, 4 Nov 2024 14:05:20 +0300 Subject: [PATCH 51/66] Update fs.md --- tests/fs.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index 722ce8b8d..ebf982785 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -98,8 +98,13 @@ let try_symlink ~link_to path = | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex let try_chmod ~follow ~perm path = - Path.chmod ~follow ~perm path; - traceln "Chmod applied on %s with permissions %o" path perm + try + Path.chmod ~follow ~perm path; + traceln "Chmod applied on %a with permissions %o" Eio.Path.pp path perm + with + | Exn.Io _ as ex -> + let bt = Printexc.get_raw_backtrace () in + Exn.reraise_with_context ex bt "Error applying chmod on %a with permissions %o" Eio.Path.pp path perm ``` From cd4fb2b7bf6d20afbdace6199ec2b8ee6c987132 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Mon, 4 Nov 2024 14:37:03 +0300 Subject: [PATCH 52/66] Update fs.md --- tests/fs.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index ebf982785..28ef81057 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -102,9 +102,11 @@ let try_chmod ~follow ~perm path = Path.chmod ~follow ~perm path; traceln "Chmod applied on %a with permissions %o" Eio.Path.pp path perm with - | Exn.Io _ as ex -> + | Eio.Io (ex, _) -> (* Handle Eio.IO exceptions directly *) let bt = Printexc.get_raw_backtrace () in - Exn.reraise_with_context ex bt "Error applying chmod on %a with permissions %o" Eio.Path.pp path perm + Printexc.raise_with_backtrace ex bt (* Reraise the exception with context *) + |> Fmt.failwith "Error applying chmod on %a with permissions %o" Eio.Path.pp path perm + ``` From ce4adee8d3ea28cf53117bfb3afbff84a7d94b02 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Mon, 4 Nov 2024 14:49:55 +0300 Subject: [PATCH 53/66] Update fs.md --- tests/fs.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index 28ef81057..436fd1fd9 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -97,15 +97,10 @@ let try_symlink ~link_to path = | s -> traceln "symlink %a -> %S" Path.pp path link_to | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex -let try_chmod ~follow ~perm path = - try - Path.chmod ~follow ~perm path; - traceln "Chmod applied on %a with permissions %o" Eio.Path.pp path perm - with - | Eio.Io (ex, _) -> (* Handle Eio.IO exceptions directly *) - let bt = Printexc.get_raw_backtrace () in - Printexc.raise_with_backtrace ex bt (* Reraise the exception with context *) - |> Fmt.failwith "Error applying chmod on %a with permissions %o" Eio.Path.pp path perm +let try_chmod path ~follow ~perm = + match Eio.Path.chmod ~follow path ~perm with + | () -> traceln "chmod %a to %o -> ok" Path.pp path perm + | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex ``` From e9b4c363dbfeeb3f33c74fdfb11fde6c091749f3 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Mon, 4 Nov 2024 15:03:34 +0300 Subject: [PATCH 54/66] Update fs.md --- tests/fs.md | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/fs.md b/tests/fs.md index 436fd1fd9..a134fe6ad 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -102,7 +102,6 @@ let try_chmod path ~follow ~perm = | () -> traceln "chmod %a to %o -> ok" Path.pp path perm | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - ``` # Basic test cases From 97d92200587d0da9a9bd706290aa9a0b2cf47f8d Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Tue, 29 Oct 2024 17:33:25 +0300 Subject: [PATCH 55/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/fs.md b/tests/fs.md index a134fe6ad..4c369b6b0 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -101,7 +101,6 @@ let try_chmod path ~follow ~perm = match Eio.Path.chmod ~follow path ~perm with | () -> traceln "chmod %a to %o -> ok" Path.pp path perm | exception ex -> traceln "@[%a@]" Eio.Exn.pp ex - ``` # Basic test cases From e051c6e58b03c93b6e2b5ea4342898b10bb5e129 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 11:11:50 +0300 Subject: [PATCH 56/66] Update fs.ml Co-Authored-By: Patrick Ferris --- lib_eio_windows/fs.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib_eio_windows/fs.ml b/lib_eio_windows/fs.ml index 18c2e6cfa..b6df8d48f 100755 --- a/lib_eio_windows/fs.ml +++ b/lib_eio_windows/fs.ml @@ -181,6 +181,9 @@ end = struct with_parent_dir t path @@ fun dirfd path -> Err.run (Low_level.symlink ~link_to dirfd) path + let chmod t ~follow ~perm path = + failwith "TODO: Implement Windows chmod functionality" + let close t = t.closed <- true let open_dir t ~sw path = From a0e5da2d68175f2eaf8d958e9296718d0ed75def Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 12:34:58 +0300 Subject: [PATCH 57/66] trying to remove some bugs from the tests Co-Authored-By: Patrick Ferris --- lib_eio_windows/fs.ml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib_eio_windows/fs.ml b/lib_eio_windows/fs.ml index b6df8d48f..4ff42868c 100755 --- a/lib_eio_windows/fs.ml +++ b/lib_eio_windows/fs.ml @@ -181,9 +181,7 @@ end = struct with_parent_dir t path @@ fun dirfd path -> Err.run (Low_level.symlink ~link_to dirfd) path - let chmod t ~follow ~perm path = - failwith "TODO: Implement Windows chmod functionality" - + let close t = t.closed <- true let open_dir t ~sw path = From e6c086100a5a970beffc8253df61fa2e9df1a6ad Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 13:33:41 +0300 Subject: [PATCH 58/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fs.md b/tests/fs.md index 4c369b6b0..dff54746c 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -830,6 +830,7 @@ Unconfined: try_mkdir (cwd / "stat_subdir2"); Path.symlink ~link_to:"stat_subdir2" (cwd / "symlink"); Path.symlink ~link_to:"missing" (cwd / "broken-symlink"); + try_chmod (cwd / "stat_subdir2") ~follow:true ~perm:0o755; try_stat (cwd / "stat_subdir2"); try_stat (cwd / "symlink"); try_stat (cwd / "broken-symlink"); From 7fb0485e5587fa58d24a3e92f2df2c0f52c9a987 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 13:35:11 +0300 Subject: [PATCH 59/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/fs.md b/tests/fs.md index dff54746c..4c369b6b0 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -830,7 +830,6 @@ Unconfined: try_mkdir (cwd / "stat_subdir2"); Path.symlink ~link_to:"stat_subdir2" (cwd / "symlink"); Path.symlink ~link_to:"missing" (cwd / "broken-symlink"); - try_chmod (cwd / "stat_subdir2") ~follow:true ~perm:0o755; try_stat (cwd / "stat_subdir2"); try_stat (cwd / "symlink"); try_stat (cwd / "broken-symlink"); From f477b32e79c5b5b5a0f963cfb94858d406aab89d Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 13:40:37 +0300 Subject: [PATCH 60/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/fs.md b/tests/fs.md index 4c369b6b0..891f58fd5 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -837,6 +837,9 @@ Unconfined: try_stat (cwd / ".."); try_stat (cwd / "stat_subdir2/.."); + try_chmod cwd ~follow:true ~perm:0o755; + try_stat cwd; + Path.symlink ~link_to:".." (cwd / "parent-symlink"); try_stat (cwd / "parent-symlink"); try_stat (cwd / "missing1" / "missing2"); From 0393837e527f6abf397b9fb6fdea73c791fc5bcf Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 31 Oct 2024 16:05:53 +0300 Subject: [PATCH 61/66] Update fs.md Co-Authored-By: Patrick Ferris --- tests/fs.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index 891f58fd5..4c369b6b0 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -837,9 +837,6 @@ Unconfined: try_stat (cwd / ".."); try_stat (cwd / "stat_subdir2/.."); - try_chmod cwd ~follow:true ~perm:0o755; - try_stat cwd; - Path.symlink ~link_to:".." (cwd / "parent-symlink"); try_stat (cwd / "parent-symlink"); try_stat (cwd / "missing1" / "missing2"); From 55e7f8d79d3094f3a3b877178132840182ef730d Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Mon, 4 Nov 2024 13:58:10 +0300 Subject: [PATCH 62/66] following the comments --- lib_eio_windows/fs.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/lib_eio_windows/fs.ml b/lib_eio_windows/fs.ml index 4ff42868c..18c2e6cfa 100755 --- a/lib_eio_windows/fs.ml +++ b/lib_eio_windows/fs.ml @@ -181,7 +181,6 @@ end = struct with_parent_dir t path @@ fun dirfd path -> Err.run (Low_level.symlink ~link_to dirfd) path - let close t = t.closed <- true let open_dir t ~sw path = From 65689dc102f8699e32366f0fcd5e53c0aa5c822b Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sat, 16 Nov 2024 10:12:59 +0000 Subject: [PATCH 63/66] Preserve backtraces in fork_daemon and fork_promise_exn If the fiber fails, record the backtrace when failing the switch. `fork` itself already did this, but `fork_daemon` and `fork_promise_exn` didn't. --- lib_eio/core/fiber.ml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib_eio/core/fiber.ml b/lib_eio/core/fiber.ml index 3113e8ccc..dbf6cfd46 100644 --- a/lib_eio/core/fiber.ml +++ b/lib_eio/core/fiber.ml @@ -39,7 +39,8 @@ let fork_daemon ~sw f = (* The daemon was cancelled because all non-daemon fibers are finished. *) () | exception ex -> - Switch.fail sw ex; (* The [with_daemon] ensures this will succeed *) + let bt = Printexc.get_raw_backtrace () in + Switch.fail ~bt sw ex; (* The [with_daemon] ensures this will succeed *) ) (* else the fiber should report the error to [sw], but [sw] is failed anyway *) let fork_promise ~sw f = @@ -65,7 +66,8 @@ let fork_promise_exn ~sw f = match Switch.with_op sw f with | x -> Promise.resolve r x | exception ex -> - Switch.fail sw ex (* The [with_op] ensures this will succeed *) + let bt = Printexc.get_raw_backtrace () in + Switch.fail ~bt sw ex (* The [with_op] ensures this will succeed *) ); p From 0c79cbcb5d8c3af5213adf8394e487fdd62fda47 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 21 Nov 2024 13:49:58 +0300 Subject: [PATCH 64/66] to resolve the conflicts --- tests/fs.md | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/tests/fs.md b/tests/fs.md index 4c369b6b0..288e9eb3e 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -1019,36 +1019,3 @@ Exception: Failure "Simulated error". +"/" / "" = "/" - : unit = () ``` - -```ocaml -# run ~clear:["test-file"] @@ fun env -> - let cwd = Eio.Stdenv.cwd env in - Switch.run @@ fun sw -> - - let file_path = cwd / "test-file" in - Path.save ~create:(`Exclusive 0o644) file_path "test data"; - traceln "+create with permissions 0o644 -> ok"; - - let initial_perm = (Path.stat ~follow:true file_path).perm in - traceln "+ initial permissions = %o" initial_perm; - assert (initial_perm = 0o644); - - try_chmod ~follow:true ~perm:0o400 file_path; - - try_stat ~info_type:`Perm file_path; - - try_chmod ~follow:true ~perm:0o600 file_path; - try_stat ~info_type:`Perm file_path; - - Eio.Path.unlink file_path; - traceln "+unlink -> ok"; - () -++create with permissions 0o644 -> ok -++ initial permissions = 644 -+chmod to 400 -> ok -+ -> 400 -+chmod to 600 -> ok -+ -> 600 -++unlink -> ok -- : unit = () -``` From a060da7443ff3f814c7e2f95c1acdcfaccbdbec5 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 21 Nov 2024 13:51:05 +0300 Subject: [PATCH 65/66] now they are set --- tests/fs.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/fs.md b/tests/fs.md index 288e9eb3e..5b24fc122 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -1019,3 +1019,36 @@ Exception: Failure "Simulated error". +"/" / "" = "/" - : unit = () ``` + +```ocaml +# run ~clear:["test-file"] @@ fun env -> + let cwd = Eio.Stdenv.cwd env in + Switch.run @@ fun sw -> + + let file_path = cwd / "test-file" in + Path.save ~create:(`Exclusive 0o644) file_path "test data"; + traceln "+create with permissions 0o644 -> ok"; + + let initial_perm = (Path.stat ~follow:true file_path).perm in + traceln "+ initial permissions = %o" initial_perm; + assert (initial_perm = 0o644); + + try_chmod ~follow:true ~perm:0o400 file_path; + + try_stat ~info_type:`Perm file_path; + + try_chmod ~follow:true ~perm:0o600 file_path; + try_stat ~info_type:`Perm file_path; + + Eio.Path.unlink file_path; + traceln "+unlink -> ok"; + () +++create with permissions 0o644 -> ok +++ initial permissions = 644 ++chmod to 400 -> ok ++ -> 400 ++chmod to 600 -> ok ++ -> 600 +++unlink -> ok +- : unit = () +``` \ No newline at end of file From 38b1f77f7a4f73016968f6b7c26043fe48c5554a Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 21 Nov 2024 13:53:56 +0300 Subject: [PATCH 66/66] putint the new line --- tests/fs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fs.md b/tests/fs.md index 5b24fc122..4c369b6b0 100644 --- a/tests/fs.md +++ b/tests/fs.md @@ -1051,4 +1051,4 @@ Exception: Failure "Simulated error". + -> 600 ++unlink -> ok - : unit = () -``` \ No newline at end of file +```