From 4f4493c4a327bf89b11a2cb4696078412983c165 Mon Sep 17 00:00:00 2001 From: Vincent Balat Date: Tue, 6 Jun 2023 10:57:50 +0200 Subject: [PATCH 01/62] Adopt OCaml's code of conduct --- CODE_OF_CONDUCT.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..738eab51f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,12 @@ +# Code of Conduct + +This project has adopted the [OCaml Code of Conduct](https://github.com/ocaml/code-of-conduct/blob/main/CODE_OF_CONDUCT.md). + +# Enforcement + +This project follows the OCaml Code of Conduct [enforcement policy](https://github.com/ocaml/code-of-conduct/blob/main/CODE_OF_CONDUCT.md#enforcement). + +To report any violations, please contact Jérôme +Vouillon, Raphael Proust, Vincent Balat, Hugo Heuzard and Gabriel +Radanne at +(or some of them individually). From 890b19af04dd00301cd185730699f732847bbd41 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 21 Jun 2023 16:37:19 +0100 Subject: [PATCH 02/62] On OCaml 5, use SA_ONSTACK to avoid corrupting memory Fixes #981. --- src/unix/lwt_unix_stubs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/unix/lwt_unix_stubs.c b/src/unix/lwt_unix_stubs.c index bfbae6443..ae5a0ba69 100644 --- a/src/unix/lwt_unix_stubs.c +++ b/src/unix/lwt_unix_stubs.c @@ -840,7 +840,11 @@ CAMLprim value lwt_unix_set_signal(value val_signum, value val_notification) { } #else sa.sa_handler = handle_signal; +#if OCAML_VERSION >= 50000 + sa.sa_flags = SA_ONSTACK; +#else sa.sa_flags = 0; +#endif sigemptyset(&sa.sa_mask); if (sigaction(signum, &sa, NULL) == -1) { signal_notifications[signum] = -1; From 69fcca7fcc80db342d5c3921a904baa66aa8c9c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Vouillon?= Date: Thu, 22 Jun 2023 13:46:05 +0200 Subject: [PATCH 03/62] Update CODE_OF_CONDUCT.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raphaël Proust --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 738eab51f..553b62f33 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -7,6 +7,6 @@ This project has adopted the [OCaml Code of Conduct](https://github.com/ocaml/co This project follows the OCaml Code of Conduct [enforcement policy](https://github.com/ocaml/code-of-conduct/blob/main/CODE_OF_CONDUCT.md#enforcement). To report any violations, please contact Jérôme -Vouillon, Raphael Proust, Vincent Balat, Hugo Heuzard and Gabriel +Vouillon, Raphaël Proust, Vincent Balat, Hugo Heuzard and Gabriel Radanne at (or some of them individually). From d4367052c1be8372d33661c01749086e56af623c Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Thu, 22 Jun 2023 09:56:02 +0100 Subject: [PATCH 04/62] Lwt_engine: allow external signal handlers Before installing a signal handler, Lwt now checks first with the engine, which may now prevent Lwt from installing its handler and instead forward signals from some external handler. This allows e.g. sharing the SIGCHLD handler with Eio. --- src/unix/lwt_engine.ml | 3 +++ src/unix/lwt_engine.mli | 8 ++++++++ src/unix/lwt_unix.cppo.ml | 11 +++++++++-- src/unix/lwt_unix.cppo.mli | 8 ++++++++ src/unix/lwt_unix_stubs.c | 14 ++++++++++++-- 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/unix/lwt_engine.ml b/src/unix/lwt_engine.ml index 78fbffb6f..0e2cf7e6c 100644 --- a/src/unix/lwt_engine.ml +++ b/src/unix/lwt_engine.ml @@ -108,6 +108,8 @@ class virtual abstract = object(self) method timer_count = Lwt_sequence.length timers method fork = () + + method forwards_signal (_signum:int) = false end class type t = object @@ -433,6 +435,7 @@ let readable_count () = !current#readable_count let writable_count () = !current#writable_count let timer_count () = !current#timer_count let fork () = !current#fork +let forwards_signal n = !current#forwards_signal n module Versioned = struct diff --git a/src/unix/lwt_engine.mli b/src/unix/lwt_engine.mli index 4b77908b8..bd696c9bf 100644 --- a/src/unix/lwt_engine.mli +++ b/src/unix/lwt_engine.mli @@ -53,6 +53,13 @@ val fake_io : Unix.file_descr -> unit val fork : unit -> unit (** Called internally by Lwt_unix.fork to make sure we don't get strange behaviour *) +val forwards_signal : int -> bool +(** [forwards_signal signum] is [true] if the engine will call {!Lwt_unix.handle_signal} + when signal [signum] occurs. In this case, Lwt will not install its own signal handler. + + Normally, this just returns [false], but when Lwt is used in combination + with other IO libraries, this allows sharing e.g. the SIGCHLD handler. *) + (** {2 Engines} *) (** An engine represents a set of functions used to register different @@ -82,6 +89,7 @@ class virtual abstract : object method readable_count : int method writable_count : int method timer_count : int + method forwards_signal : int -> bool (** {2 Backend methods} *) diff --git a/src/unix/lwt_unix.cppo.ml b/src/unix/lwt_unix.cppo.ml index 00443cffc..eda06efdf 100644 --- a/src/unix/lwt_unix.cppo.ml +++ b/src/unix/lwt_unix.cppo.ml @@ -2227,12 +2227,19 @@ let event_notifications = ref (Lwt_engine.on_readable (init_notification ()) han | Signals | +-----------------------------------------------------------------+ *) -external set_signal : int -> int -> unit = "lwt_unix_set_signal" -external remove_signal : int -> unit = "lwt_unix_remove_signal" +external set_signal : int -> int -> bool -> unit = "lwt_unix_set_signal" +external remove_signal : int -> bool -> unit = "lwt_unix_remove_signal" external init_signals : unit -> unit = "lwt_unix_init_signals" +external handle_signal : int -> unit = "lwt_unix_handle_signal" let () = init_signals () +let set_signal signum notification = + set_signal signum notification (Lwt_engine.forwards_signal signum) + +let remove_signal signum = + remove_signal signum (Lwt_engine.forwards_signal signum) + module Signal_map = Map.Make(struct type t = int let compare a b = a - b end) type signal_handler = { diff --git a/src/unix/lwt_unix.cppo.mli b/src/unix/lwt_unix.cppo.mli index 1685b405c..df5d08206 100644 --- a/src/unix/lwt_unix.cppo.mli +++ b/src/unix/lwt_unix.cppo.mli @@ -854,6 +854,14 @@ val reinstall_signal_handler : int -> unit signal handler (with [Sys.set_signal]). This is useful in case another part of the program install another signal handler. *) +val handle_signal : int -> unit + (** [handle_signal signum] acts as if Lwt had received the [signum] signal. + This allows another IO library to install the handler, perform its own + handling, but still notify Lwt. It is particularly useful for SIGCHLD, + where several IO libraries may be spawning sub-processes. + + This function is thread-safe. *) + (** {2 Sockets} *) type inet_addr = Unix.inet_addr diff --git a/src/unix/lwt_unix_stubs.c b/src/unix/lwt_unix_stubs.c index ae5a0ba69..a14190964 100644 --- a/src/unix/lwt_unix_stubs.c +++ b/src/unix/lwt_unix_stubs.c @@ -801,6 +801,11 @@ static void handle_signal(int signum) { } } +CAMLprim value lwt_unix_handle_signal(value val_signum) { + handle_signal(caml_convert_signal_number(Int_val(val_signum))); + return Val_unit; +} + #if defined(LWT_ON_WINDOWS) /* Handle Ctrl+C on windows. */ static BOOL WINAPI handle_break(DWORD event) { @@ -813,7 +818,7 @@ static BOOL WINAPI handle_break(DWORD event) { #endif /* Install a signal handler. */ -CAMLprim value lwt_unix_set_signal(value val_signum, value val_notification) { +CAMLprim value lwt_unix_set_signal(value val_signum, value val_notification, value val_forwarded) { #if !defined(LWT_ON_WINDOWS) struct sigaction sa; #endif @@ -825,6 +830,8 @@ CAMLprim value lwt_unix_set_signal(value val_signum, value val_notification) { signal_notifications[signum] = notification; + if (Bool_val(val_forwarded)) return Val_unit; + #if defined(LWT_ON_WINDOWS) if (signum == SIGINT) { if (!SetConsoleCtrlHandler(handle_break, TRUE)) { @@ -855,7 +862,7 @@ CAMLprim value lwt_unix_set_signal(value val_signum, value val_notification) { } /* Remove a signal handler. */ -CAMLprim value lwt_unix_remove_signal(value val_signum) { +CAMLprim value lwt_unix_remove_signal(value val_signum, value val_forwarded) { #if !defined(LWT_ON_WINDOWS) struct sigaction sa; #endif @@ -863,6 +870,9 @@ CAMLprim value lwt_unix_remove_signal(value val_signum) { set_signal. */ int signum = caml_convert_signal_number(Int_val(val_signum)); signal_notifications[signum] = -1; + + if (Bool_val(val_forwarded)) return Val_unit; + #if defined(LWT_ON_WINDOWS) if (signum == SIGINT) SetConsoleCtrlHandler(NULL, FALSE); From 52596733b92b12458fcae701d5051d984c26ed5d Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 12 Jul 2023 09:46:03 +0100 Subject: [PATCH 05/62] Fix signal masking race in worker_loop If an OCaml signal handler runs in a Lwt worker thread then the process will dereference a NULL pointer and crash. The worker tried to avoid this by masking out all signals after starting, but by then it may be too late. Now, we mask out signals before creating a worker thread and then restore the mask in the main thread afterwards. --- src/unix/lwt_unix_stubs.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/unix/lwt_unix_stubs.c b/src/unix/lwt_unix_stubs.c index a14190964..a8eedde87 100644 --- a/src/unix/lwt_unix_stubs.c +++ b/src/unix/lwt_unix_stubs.c @@ -169,6 +169,7 @@ value lwt_unix_system_byte_order() { int lwt_unix_launch_thread(void *(*start)(void *), void *data) { pthread_t thread; pthread_attr_t attr; + sigset_t mask, old_mask; pthread_attr_init(&attr); @@ -176,9 +177,17 @@ int lwt_unix_launch_thread(void *(*start)(void *), void *data) { it when it terminates: */ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + /* Block all signals, otherwise ocaml handlers defined with the + module Sys may be executed in the new thread, oops... */ + sigfillset(&mask); + pthread_sigmask(SIG_SETMASK, &mask, &old_mask); + int zero_if_created_otherwise_errno = pthread_create(&thread, &attr, start, data); + /* Restore the signal mask for the calling thread. */ + pthread_sigmask(SIG_SETMASK, &old_mask, NULL); + pthread_attr_destroy(&attr); return zero_if_created_otherwise_errno; @@ -978,18 +987,11 @@ void initialize_threading() { | Worker loop | +-----------------------------------------------------------------+ */ -/* Function executed by threads of the pool. */ +/* Function executed by threads of the pool. + * Note: all signals are masked for this thread. */ static void *worker_loop(void *data) { lwt_unix_job job = (lwt_unix_job)data; -#if defined(HAVE_PTHREAD) - /* Block all signals, otherwise ocaml handlers defined with the - module Sys may be executed in this thread, oops... */ - sigset_t mask; - sigfillset(&mask); - pthread_sigmask(SIG_SETMASK, &mask, NULL); -#endif - /* Execute the initial job if any. */ if (job != NULL) execute_job(job); From ed81ff90d978537a47aaf232cedd6e066bb2471f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Mon, 17 Jul 2023 11:08:32 +0200 Subject: [PATCH 06/62] Mention addition in CHANGES --- CHANGES | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index cb8d888fd..fe6b468fb 100644 --- a/CHANGES +++ b/CHANGES @@ -23,12 +23,21 @@ * Lwt_preemptive.run_in_main_dont_wait to run a function in the main preemptive thread but without waiting for the result. (Kate Deplaix, #960) + * Lwt_unix.handle_signal and Lwt_engine.forwards_signal to allow other IO libraries (such as Eio) to share signal handlers. (Thomas Leonard, #993, #991) + ====== Build ====== * Remove unused dependency in dune file. (#969, Kate Deplaix) * Fix some compilation warnings for C stubs with OCaml 5. (#976, Antonin Décimo) +====== Fixes ====== + + * Use SA_ONSTACK on OCaml5 to avoid SIGSEGV. (Thomas Leonard, #993, #981) + + * Fix race in worker loop. (Thomas Leonard, #993, #994) + + ===== 5.6.1 ===== ====== Fixes ====== From 1462be31c7ebbef4eb8c92913059edb3b470af5f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 13 Jul 2023 19:22:56 +0300 Subject: [PATCH 07/62] Fix Lwt_io.read_value on OCaml 5.1 The Marshal header size was changed in https://github.com/ocaml/ocaml/pull/12006. --- src/unix/lwt_io.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/unix/lwt_io.ml b/src/unix/lwt_io.ml index 1e9cca05e..34ff686c1 100644 --- a/src/unix/lwt_io.ml +++ b/src/unix/lwt_io.ml @@ -842,12 +842,12 @@ struct Lwt.return (Bytes.unsafe_to_string buf) let read_value ic = - let header = Bytes.create 20 in - unsafe_read_into_exactly ic header 0 20 >>= fun () -> + let header = Bytes.create Marshal.header_size in + unsafe_read_into_exactly ic header 0 Marshal.header_size >>= fun () -> let bsize = Marshal.data_size header 0 in - let buffer = Bytes.create (20 + bsize) in - Bytes.unsafe_blit header 0 buffer 0 20; - unsafe_read_into_exactly ic buffer 20 bsize >>= fun () -> + let buffer = Bytes.create (Marshal.header_size + bsize) in + Bytes.unsafe_blit header 0 buffer 0 Marshal.header_size; + unsafe_read_into_exactly ic buffer Marshal.header_size bsize >>= fun () -> Lwt.return (Marshal.from_bytes buffer 0) (* +---------------------------------------------------------------+ From 318b0f8849f1e404bb4d05e78ee777f3d01b74fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Mon, 17 Jul 2023 11:29:07 +0200 Subject: [PATCH 08/62] Mention fix in CHANGES --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index fe6b468fb..5a80a2d1e 100644 --- a/CHANGES +++ b/CHANGES @@ -37,6 +37,8 @@ * Fix race in worker loop. (Thomas Leonard, #993, #994) + * Fix marshall header size in Lwt_io.read_value. (Simmo Saan, #995) + ===== 5.6.1 ===== From 9983986d47a7e387c0553376c425fadf6717da95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Mon, 17 Jul 2023 11:46:24 +0200 Subject: [PATCH 09/62] Remove lwt_luv code and build-files --- .github/workflows/workflow.yml | 6 ++-- esy.json | 6 ++-- lwt_luv.opam | 29 ----------------- src/unix/luv/dune | 16 ---------- src/unix/luv/lwt_luv.ml | 57 ---------------------------------- src/unix/luv/lwt_luv.mli | 2 -- test/unix/dune | 14 +-------- test/unix/luv_main.ml | 20 ------------ 8 files changed, 6 insertions(+), 144 deletions(-) delete mode 100644 lwt_luv.opam delete mode 100644 src/unix/luv/dune delete mode 100644 src/unix/luv/lwt_luv.ml delete mode 100644 src/unix/luv/lwt_luv.mli delete mode 100644 test/unix/luv_main.ml diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index e24417b2e..cff9aad19 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -93,17 +93,17 @@ jobs: - run: opam depext conf-libev --install if: ${{ matrix.libev == true }} - - run: opam install lwt_luv lwt_react lwt --deps-only --with-test + - run: opam install lwt_react lwt --deps-only --with-test - run: opam install lwt_ppx --deps-only --with-test if: ${{ matrix.ppx == true }} - - run: opam exec -- dune build --only-packages lwt_luv,lwt_react,lwt + - run: opam exec -- dune build --only-packages lwt_react,lwt - run: opam exec -- dune build --only-packages lwt_ppx if: ${{ matrix.ppx == true }} - - run: opam exec -- dune runtest --only-packages lwt_luv,lwt_react,lwt + - run: opam exec -- dune runtest --only-packages lwt_react,lwt - run: opam exec -- dune runtest --only-packages lwt_ppx if: ${{ matrix.ppx == true }} diff --git a/esy.json b/esy.json index 9a2d6b95c..ffa238271 100644 --- a/esy.json +++ b/esy.json @@ -5,12 +5,10 @@ "dependencies": { "ocaml": "~4.9.0", "@opam/conf-libev": "*", - "@opam/bisect_ppx": "*", - "@opam/luv": "*" + "@opam/bisect_ppx": "*" }, "resolutions": { - "@opam/conf-libev": "esy-packages/libev:package.json#0b5eb66", - "@opam/luv": "aantron/luv:luv.opam#df02a9ac6e9e9fc6427e7853d260230fcb96bd2a" + "@opam/conf-libev": "esy-packages/libev:package.json#0b5eb66" } } } diff --git a/lwt_luv.opam b/lwt_luv.opam deleted file mode 100644 index b22e0ee5e..000000000 --- a/lwt_luv.opam +++ /dev/null @@ -1,29 +0,0 @@ -opam-version: "2.0" - -synopsis: "Libuv engine for Lwt" - -version: "1.0.0" -license: "MIT" -homepage: "https://github.com/ocsigen/lwt" -doc: "https://ocsigen.org/lwt" -bug-reports: "https://github.com/ocsigen/lwt/issues" - -maintainer: [ - "Anton Bachin " - "Ulrik Strid " -] -dev-repo: "git+https://github.com/ocsigen/lwt.git" - -depends: [ - "lwt" - "dune" {>= "1.8.0"} - "ocaml" {>= "4.08"} - "luv" - - # Until https://github.com/aantron/bisect_ppx/pull/327. - # "bisect_ppx" {dev & >= "2.0.0"} -] - -build: [ - ["dune" "build" "-p" name "-j" jobs] -] diff --git a/src/unix/luv/dune b/src/unix/luv/dune deleted file mode 100644 index 20be1e689..000000000 --- a/src/unix/luv/dune +++ /dev/null @@ -1,16 +0,0 @@ -(* -*- tuareg -*- *) - -let preprocess = - match Sys.getenv "BISECT_ENABLE" with - | "yes" -> "(preprocess (pps bisect_ppx))" - | _ -> "" - | exception _ -> "" - -let () = Jbuild_plugin.V1.send @@ {| - -(library - (public_name lwt_luv) - (libraries luv lwt lwt.unix) - |} ^ preprocess ^ {| -) -|} diff --git a/src/unix/luv/lwt_luv.ml b/src/unix/luv/lwt_luv.ml deleted file mode 100644 index ae1f7ff65..000000000 --- a/src/unix/luv/lwt_luv.ml +++ /dev/null @@ -1,57 +0,0 @@ -let from_unix : Unix.file_descr -> int = Obj.magic - -class engine = object - inherit Lwt_engine.abstract - - val loop = ref (Luv.Loop.default ()) - - method! fork = - Luv.Loop.fork !loop |> function - | Ok () -> () - | Error e -> failwith (Printf.sprintf "Could not handle the fork, this is probably a error in Lwt, please open a issue on the repo. \nError message: %s" (Luv.Error.err_name e)) - - method private cleanup = Luv.Loop.stop !loop - - method iter block = - match (block) with - | true -> Luv.Loop.run ~loop:!loop ~mode:`ONCE () |> ignore - | false -> Luv.Loop.run ~loop:!loop ~mode:`NOWAIT () |> ignore - - method private register_readable fd f = - let p = Luv.Poll.init ~loop:!loop (from_unix fd) in - match p with - | Ok poll -> - let () = Luv.Poll.start poll [`READABLE;] (fun _ -> f ()) in - lazy( - Luv.Poll.stop poll |> function - | Ok () -> () - | Error e -> failwith (Printf.sprintf "Could not stop read polling, this is probably a error in Lwt, please open a issue on the repo. \nError message: %s" (Luv.Error.err_name e)) - ) - | Result.Error e -> failwith (Printf.sprintf "Could not register fd for read polling, this is probably a error in Lwt, please open a issue on the repo. \nError message: %s" (Luv.Error.err_name e)) - - method private register_writable fd f = - let p = Luv.Poll.init ~loop:!loop (from_unix fd) in - match p with - | Ok poll -> - let () = Luv.Poll.start poll [`WRITABLE;] (fun _ -> f ()) in - lazy( - Luv.Poll.stop poll |> function - | Ok () -> () - | Error e -> failwith (Printf.sprintf "Could not stop write polling, this is probably a error in Lwt, please open a issue on the repo. \nError message: %s" (Luv.Error.err_name e)) - ) - | Result.Error e -> failwith (Printf.sprintf "Could not register fd for write polling, this is probably a error in Lwt, please open a issue on the repo. \nError message: %s" (Luv.Error.err_name e)) - - method private register_timer delay repeat f = - let delay_ms = (int_of_float (delay *. 1000.)) in - let t = Luv.Timer.init ~loop:!loop () in - match t with - | Result.Error e -> failwith (Printf.sprintf "Could not initialize a timer, this is probably a error in Lwt, please open a issue on the repo. \nError message: %s" (Luv.Error.err_name e)) - | Ok timer -> - let timer_fn = match repeat with - | true -> Luv.Timer.start ~repeat:delay_ms timer - | false -> Luv.Timer.start timer - in - match timer_fn delay_ms f with - | Ok () -> lazy(Luv.Timer.stop timer |> ignore) - | Result.Error e -> failwith (Printf.sprintf "Could not start a timer, this is probably a error in Lwt, please open a issue on the repo. \nError message: %s" (Luv.Error.err_name e)) -end diff --git a/src/unix/luv/lwt_luv.mli b/src/unix/luv/lwt_luv.mli deleted file mode 100644 index fccd982b9..000000000 --- a/src/unix/luv/lwt_luv.mli +++ /dev/null @@ -1,2 +0,0 @@ -(** Engine based on libuv. *) -class engine : Lwt_engine.t diff --git a/test/unix/dune b/test/unix/dune index 4e35bc650..a81c56e7a 100644 --- a/test/unix/dune +++ b/test/unix/dune @@ -1,7 +1,7 @@ (library (name tester) (libraries lwt lwttester) - (modules (:standard \ main luv_main dummy) )) + (modules (:standard \ main dummy) )) (executable (name dummy) @@ -13,21 +13,9 @@ (libraries lwttester tester) (modules main)) -(executable - (name luv_main) - (libraries lwt lwt_luv lwttester tester) - (modules luv_main)) - (alias (name runtest) (package lwt) (action (run %{exe:main.exe})) (deps bytes_io_data %{exe:dummy.exe}) ) - -(alias - (name runtest_libuv) - (package lwt) - (action (run %{exe:luv_main.exe})) - (deps bytes_io_data) -) diff --git a/test/unix/luv_main.ml b/test/unix/luv_main.ml deleted file mode 100644 index 38578b183..000000000 --- a/test/unix/luv_main.ml +++ /dev/null @@ -1,20 +0,0 @@ -(* This file is part of Lwt, released under the MIT license. See LICENSE.md for - details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) - -open Tester - -let () = Lwt_engine.set (new Lwt_luv.engine) - -let () = - Test.concurrent "unix with luv" [ - Test_lwt_unix.suite; - Test_lwt_io.suite; - Test_lwt_io_non_block.suite; - Test_lwt_process.suite; - Test_lwt_engine.suite; - Test_mcast.suite; - Test_lwt_fmt.suite; - Test_lwt_timeout.suite; - Test_lwt_bytes.suite; - Test_sleep_and_timeout.suite; - ] From 0e22bae7a2e7e89e45b82e7b76c7376c3ff49147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Tue, 18 Jul 2023 15:07:20 +0200 Subject: [PATCH 10/62] Don't run removed tests of libuv backend --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index cb78adc4c..594b7606f 100644 --- a/Makefile +++ b/Makefile @@ -105,7 +105,6 @@ coverage : rm -rf _build/default/test/ppx_expect find _build -name '*.coverage' | xargs rm -f BISECT_ENABLE=yes dune runtest --force - BISECT_ENABLE=yes dune build @runtest_libuv --force bisect-ppx-report html $(EXPECTED_FILES) bisect-ppx-report summary @echo See _coverage/index.html From b38a6e5f2f0baa94006761ac6cc995a8801fcdca Mon Sep 17 00:00:00 2001 From: Christopher Zimmermann Date: Tue, 4 Jan 2022 22:11:47 +0100 Subject: [PATCH 11/62] Remove obsolete second wakeup_paused It was introduced in d5822af9a80b745515e0988541d7a43ed9a6faba. If I understand it correctly this was done to prevent calling select without timeout while there were pending paused promises. Since now we explicitely check for this condition and call select with zero timeout (`should_block_waiting_for_io`). This extra `wakeup_paused` seems to be obsolete. --- CHANGES | 4 ++++ src/unix/lwt_main.ml | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 5a80a2d1e..53f1de086 100644 --- a/CHANGES +++ b/CHANGES @@ -89,6 +89,10 @@ * Alias Lwt_result.map_err and Lwt_result.bind_lwt_err to Lwt_result.map_error and Lwt_result.bind_lwt_error for consistency with Stdlib. (#927, Antonin Décimo) +====== Misc ====== + + * Resolve paused promises only once in main loop. This lets Lwt.pause behave identical to Lwt_unix.yield.(#917, Christopher Zimmermann) + ===== 5.5.0 ===== ====== Deprecations ====== diff --git a/src/unix/lwt_main.ml b/src/unix/lwt_main.ml index 0cac449b8..17ea3c5a1 100644 --- a/src/unix/lwt_main.ml +++ b/src/unix/lwt_main.ml @@ -26,8 +26,6 @@ let abandon_yielded_and_paused () = let run p = let rec run_loop () = - (* Fulfill paused promises now. *) - Lwt.wakeup_paused (); match Lwt.poll p with | Some x -> x @@ -40,7 +38,7 @@ let run p = Lwt.paused_count () = 0 && Lwt_sequence.is_empty yielded in Lwt_engine.iter should_block_waiting_for_io; - (* Fulfill paused promises again. *) + (* Fulfill paused promises. *) Lwt.wakeup_paused (); (* Fulfill yield promises. *) From 8c693243f352f0f67b6682ddc0f7554cecb0c191 Mon Sep 17 00:00:00 2001 From: Christopher Zimmermann Date: Thu, 6 Jan 2022 18:07:18 +0100 Subject: [PATCH 12/62] document how to wait for one whole iteration of main loop --- src/core/lwt.mli | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/lwt.mli b/src/core/lwt.mli index 4d8ad6dd6..9c9c2d002 100644 --- a/src/core/lwt.mli +++ b/src/core/lwt.mli @@ -1836,6 +1836,9 @@ val pause : unit -> unit t Putting the rest of your computation into a callback of [Lwt.pause ()] creates a “yield” that gives other callbacks a chance to run first. + To wait for one complete iteration of the main loop you need to wait for + [Lwt.pause () >>= Lwt.pause] + For example, to break up a long-running computation, allowing I/O to be handled between chunks: From 322cc45db73aaf1f77041db91e53d9ab78d773d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Fri, 21 Jan 2022 12:07:18 +0100 Subject: [PATCH 13/62] Deprecated unix's yield functions implemented as calls to Lwt.pause --- src/core/lwt.mli | 3 --- src/unix/lwt_main.ml | 14 ++------------ src/unix/lwt_main.mli | 14 ++++++-------- src/unix/lwt_unix.cppo.ml | 2 +- 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/core/lwt.mli b/src/core/lwt.mli index 9c9c2d002..4d8ad6dd6 100644 --- a/src/core/lwt.mli +++ b/src/core/lwt.mli @@ -1836,9 +1836,6 @@ val pause : unit -> unit t Putting the rest of your computation into a callback of [Lwt.pause ()] creates a “yield” that gives other callbacks a chance to run first. - To wait for one complete iteration of the main loop you need to wait for - [Lwt.pause () >>= Lwt.pause] - For example, to break up a long-running computation, allowing I/O to be handled between chunks: diff --git a/src/unix/lwt_main.ml b/src/unix/lwt_main.ml index 17ea3c5a1..ca1f68f19 100644 --- a/src/unix/lwt_main.ml +++ b/src/unix/lwt_main.ml @@ -16,12 +16,10 @@ open Lwt.Infix let enter_iter_hooks = Lwt_sequence.create () let leave_iter_hooks = Lwt_sequence.create () -let yielded = Lwt_sequence.create () -let yield () = (Lwt.add_task_r [@ocaml.warning "-3"]) yielded +let yield () = Lwt.pause () let abandon_yielded_and_paused () = - Lwt_sequence.clear yielded; Lwt.abandon_paused () let run p = @@ -34,20 +32,12 @@ let run p = Lwt_sequence.iter_l (fun f -> f ()) enter_iter_hooks; (* Do the main loop call. *) - let should_block_waiting_for_io = - Lwt.paused_count () = 0 && Lwt_sequence.is_empty yielded in + let should_block_waiting_for_io = Lwt.paused_count () = 0 in Lwt_engine.iter should_block_waiting_for_io; (* Fulfill paused promises. *) Lwt.wakeup_paused (); - (* Fulfill yield promises. *) - if not (Lwt_sequence.is_empty yielded) then begin - let tmp = Lwt_sequence.create () in - Lwt_sequence.transfer_r yielded tmp; - Lwt_sequence.iter_l (fun resolver -> Lwt.wakeup resolver ()) tmp - end; - (* Call leave hooks. *) Lwt_sequence.iter_l (fun f -> f ()) leave_iter_hooks; diff --git a/src/unix/lwt_main.mli b/src/unix/lwt_main.mli index 0d5c6b3dc..9e33dd9a3 100644 --- a/src/unix/lwt_main.mli +++ b/src/unix/lwt_main.mli @@ -49,14 +49,9 @@ val yield : unit -> unit Lwt.t [@@deprecated "Use Lwt.pause instead"] @deprecated Since 5.5.0 [yield] is deprecated in favor of the more general {!Lwt.pause} in order to avoid discrepancies in resolution (see below) and - stay compatible with other execution environments such as js_of_ocaml. + stay compatible with other execution environments such as js_of_ocaml. *) - Currently, paused promises are resolved more frequently than yielded promises. - The difference is unintended but existing applications could depend on it. - Unifying the two pools of promises into one in the future would eliminate - possible discrepancies and simplify the code. *) - -val abandon_yielded_and_paused : unit -> unit +val abandon_yielded_and_paused : unit -> unit [@@deprecated "Use Lwt.abandon_paused instead"] (** Causes promises created with {!Lwt.pause} and {!Lwt_main.yield} to remain forever pending. @@ -64,7 +59,10 @@ val abandon_yielded_and_paused : unit -> unit Once [yield] is phased out, this function will be deprecated as well. This is meant for use with {!Lwt_unix.fork}, as a way to “abandon” more - promise chains that are pending in your process. *) + promise chains that are pending in your process. + + @deprecated Since 5.5.1 [abandon_yielded_and_paused] is deprecated in favour + of [Lwt.abandon_paused]. *) diff --git a/src/unix/lwt_unix.cppo.ml b/src/unix/lwt_unix.cppo.ml index eda06efdf..8d1fa7f31 100644 --- a/src/unix/lwt_unix.cppo.ml +++ b/src/unix/lwt_unix.cppo.ml @@ -123,7 +123,7 @@ let sleep delay = Lwt.on_cancel waiter (fun () -> Lwt_engine.stop_event ev); waiter -let yield = (Lwt_main.yield [@warning "-3"]) +let yield = Lwt.pause let auto_yield timeout = let limit = ref (Unix.gettimeofday () +. timeout) in From d19212007f4f609657bdcc037e110817e6f11ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Mon, 17 Jul 2023 17:13:17 +0200 Subject: [PATCH 14/62] Update CHANGES after rebase --- CHANGES | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 53f1de086..dbecace81 100644 --- a/CHANGES +++ b/CHANGES @@ -39,6 +39,10 @@ * Fix marshall header size in Lwt_io.read_value. (Simmo Saan, #995) +====== Misc ====== + + * Resolve paused promises only once in main loop. This lets Lwt.pause behave identical to Lwt_unix.yield. (#917, Christopher Zimmermann) + ===== 5.6.1 ===== @@ -89,10 +93,6 @@ * Alias Lwt_result.map_err and Lwt_result.bind_lwt_err to Lwt_result.map_error and Lwt_result.bind_lwt_error for consistency with Stdlib. (#927, Antonin Décimo) -====== Misc ====== - - * Resolve paused promises only once in main loop. This lets Lwt.pause behave identical to Lwt_unix.yield.(#917, Christopher Zimmermann) - ===== 5.5.0 ===== ====== Deprecations ====== From e4a74cc5ad50bbb468eface71dc703d893b5699b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Wed, 26 Jul 2023 09:08:21 +0200 Subject: [PATCH 15/62] Update doc of abandon_yielded_and_paused --- src/unix/lwt_main.mli | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/unix/lwt_main.mli b/src/unix/lwt_main.mli index 9e33dd9a3..1cb3363e5 100644 --- a/src/unix/lwt_main.mli +++ b/src/unix/lwt_main.mli @@ -55,8 +55,7 @@ val abandon_yielded_and_paused : unit -> unit [@@deprecated "Use Lwt.abandon_pau (** Causes promises created with {!Lwt.pause} and {!Lwt_main.yield} to remain forever pending. - [yield] is now deprecated in favor of the more general {!Lwt.pause}. - Once [yield] is phased out, this function will be deprecated as well. + (Note that [yield] is deprecated in favor of the more general {!Lwt.pause}.) This is meant for use with {!Lwt_unix.fork}, as a way to “abandon” more promise chains that are pending in your process. From 0baa154ebb0866cbde1977791871b2d5b32b87ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Wed, 26 Jul 2023 09:11:48 +0200 Subject: [PATCH 16/62] Update deprecation comment version number --- src/unix/lwt_main.mli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/lwt_main.mli b/src/unix/lwt_main.mli index 1cb3363e5..3b4f174b7 100644 --- a/src/unix/lwt_main.mli +++ b/src/unix/lwt_main.mli @@ -60,7 +60,7 @@ val abandon_yielded_and_paused : unit -> unit [@@deprecated "Use Lwt.abandon_pau This is meant for use with {!Lwt_unix.fork}, as a way to “abandon” more promise chains that are pending in your process. - @deprecated Since 5.5.1 [abandon_yielded_and_paused] is deprecated in favour + @deprecated Since 5.7 [abandon_yielded_and_paused] is deprecated in favour of [Lwt.abandon_paused]. *) From 0db9599a80794a851d225737ef8728b172a64dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Fri, 28 Jul 2023 11:23:51 +0200 Subject: [PATCH 17/62] Apply suggestion --- src/unix/lwt_main.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/lwt_main.ml b/src/unix/lwt_main.ml index ca1f68f19..25d0ccaee 100644 --- a/src/unix/lwt_main.ml +++ b/src/unix/lwt_main.ml @@ -17,7 +17,7 @@ open Lwt.Infix let enter_iter_hooks = Lwt_sequence.create () let leave_iter_hooks = Lwt_sequence.create () -let yield () = Lwt.pause () +let yield = Lwt.pause let abandon_yielded_and_paused () = Lwt.abandon_paused () From 6419816b2b343dedb163f4e0eeac493f947fd9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Fri, 28 Jul 2023 11:27:52 +0200 Subject: [PATCH 18/62] mention reviewer in changelog --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index dbecace81..2b0328dd6 100644 --- a/CHANGES +++ b/CHANGES @@ -41,7 +41,7 @@ ====== Misc ====== - * Resolve paused promises only once in main loop. This lets Lwt.pause behave identical to Lwt_unix.yield. (#917, Christopher Zimmermann) + * Resolve paused promises only once in main loop. This lets Lwt.pause behave identical to Lwt_unix.yield. (#917, Christopher Zimmermann, Favonia) ===== 5.6.1 ===== From 96a9de3598cf22d4549240f94fd1ae7c9dadfe55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Wed, 12 Oct 2022 10:39:24 +0200 Subject: [PATCH 19/62] Avoid catching runtime-system exceptions Out-of-memory Stack-overflow --- src/core/lwt.ml | 113 ++++++++++++++++++++++++++++--------- src/core/lwt.mli | 2 + src/react/lwt_react.ml | 7 ++- src/unix/lwt_io.ml | 8 ++- src/unix/lwt_preemptive.ml | 2 +- src/unix/lwt_timeout.ml | 3 +- src/unix/lwt_unix.cppo.ml | 14 ++--- test/core/test_lwt.ml | 32 +++++++++++ 8 files changed, 142 insertions(+), 39 deletions(-) diff --git a/src/core/lwt.ml b/src/core/lwt.ml index cfab66246..fc6498bbb 100644 --- a/src/core/lwt.ml +++ b/src/core/lwt.ml @@ -711,6 +711,11 @@ end open Basic_helpers +(* Small helper function to avoid catching ocaml-runtime exceptions *) +let is_not_ocaml_runtime_exception = function + | Out_of_memory -> false + | Stack_overflow -> false + | _ -> true module Sequence_associated_storage : sig @@ -791,7 +796,7 @@ struct let result = f () in current_storage := saved_storage; result - with exn -> + with exn when is_not_ocaml_runtime_exception exn -> current_storage := saved_storage; raise exn end @@ -1129,7 +1134,8 @@ struct be reject later, it is not the responsibility of this function to pass the exception to [!async_exception_hook]. *) try f v - with exn -> !async_exception_hook exn + with exn when is_not_ocaml_runtime_exception exn -> + !async_exception_hook exn @@ -1826,7 +1832,10 @@ struct | Fulfilled v -> current_storage := saved_storage; - let p' = try f v with exn -> fail exn in + let p' = + try f v with exn + when is_not_ocaml_runtime_exception exn -> fail exn + in let Internal p' = to_internal_promise p' in (* Run the user's function [f]. *) @@ -1889,7 +1898,10 @@ struct | Fulfilled v -> current_storage := saved_storage; - let p' = try f v with exn -> fail (add_loc exn) in + let p' = + try f v + with exn when is_not_ocaml_runtime_exception exn -> + fail (add_loc exn) in let Internal p' = to_internal_promise p' in let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in @@ -1943,7 +1955,10 @@ struct | Fulfilled v -> current_storage := saved_storage; - let p''_result = try Fulfilled (f v) with exn -> Rejected exn in + let p''_result = + try Fulfilled (f v) with exn + when is_not_ocaml_runtime_exception exn -> Rejected exn + in let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in let p'' = underlying p'' in @@ -1970,7 +1985,9 @@ struct ~run_immediately_and_ensure_tail_call:true ~callback:(fun () -> to_public_promise - {state = try Fulfilled (f v) with exn -> Rejected exn}) + {state = + try Fulfilled (f v) + with exn when is_not_ocaml_runtime_exception exn -> Rejected exn}) ~if_deferred:(fun () -> let (p'', callback) = create_result_promise_and_callback_if_deferred () in @@ -1987,7 +2004,10 @@ struct external reraise : exn -> 'a = "%reraise" let catch f h = - let p = try f () with exn -> fail exn in + let p = + try f () + with exn when is_not_ocaml_runtime_exception exn -> fail exn + in let Internal p = to_internal_promise p in let p = underlying p in @@ -2009,7 +2029,10 @@ struct | Rejected exn -> current_storage := saved_storage; - let p' = try h exn with exn -> fail exn in + let p' = + try h exn + with exn when is_not_ocaml_runtime_exception exn -> fail exn + in let Internal p' = to_internal_promise p' in let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in @@ -2042,7 +2065,10 @@ struct p'' let backtrace_catch add_loc f h = - let p = try f () with exn -> fail exn in + let p = + try f () + with exn when is_not_ocaml_runtime_exception exn -> fail exn + in let Internal p = to_internal_promise p in let p = underlying p in @@ -2064,7 +2090,11 @@ struct | Rejected exn -> current_storage := saved_storage; - let p' = try h exn with exn -> fail (add_loc exn) in + let p' = + try h exn + with exn when is_not_ocaml_runtime_exception exn -> + fail (add_loc exn) + in let Internal p' = to_internal_promise p' in let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in @@ -2097,7 +2127,10 @@ struct p'' let try_bind f f' h = - let p = try f () with exn -> fail exn in + let p = + try f () + with exn when is_not_ocaml_runtime_exception exn -> fail exn + in let Internal p = to_internal_promise p in let p = underlying p in @@ -2111,7 +2144,10 @@ struct | Fulfilled v -> current_storage := saved_storage; - let p' = try f' v with exn -> fail exn in + let p' = + try f' v + with exn when is_not_ocaml_runtime_exception exn -> fail exn + in let Internal p' = to_internal_promise p' in let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in @@ -2124,7 +2160,10 @@ struct | Rejected exn -> current_storage := saved_storage; - let p' = try h exn with exn -> fail exn in + let p' = + try h exn + with exn when is_not_ocaml_runtime_exception exn -> fail exn + in let Internal p' = to_internal_promise p' in let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in @@ -2163,7 +2202,10 @@ struct p'' let backtrace_try_bind add_loc f f' h = - let p = try f () with exn -> fail exn in + let p = + try f () + with exn when is_not_ocaml_runtime_exception exn -> fail exn + in let Internal p = to_internal_promise p in let p = underlying p in @@ -2177,7 +2219,11 @@ struct | Fulfilled v -> current_storage := saved_storage; - let p' = try f' v with exn -> fail (add_loc exn) in + let p' = + try f' v + with exn when is_not_ocaml_runtime_exception exn -> + fail (add_loc exn) + in let Internal p' = to_internal_promise p' in let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in @@ -2190,7 +2236,11 @@ struct | Rejected exn -> current_storage := saved_storage; - let p' = try h exn with exn -> fail (add_loc exn) in + let p' = + try h exn + with exn when is_not_ocaml_runtime_exception exn -> + fail (add_loc exn) + in let Internal p' = to_internal_promise p' in let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in @@ -2441,7 +2491,10 @@ struct external reraise : exn -> 'a = "%reraise" let dont_wait f h = - let p = try f () with exn -> fail exn in + let p = + try f () + with exn when is_not_ocaml_runtime_exception exn -> fail exn + in let Internal p = to_internal_promise p in match (underlying p).state with @@ -2461,7 +2514,10 @@ struct add_implicitly_removed_callback p_callbacks callback let async f = - let p = try f () with exn -> fail exn in + let p = + try f () + with exn when is_not_ocaml_runtime_exception exn -> fail exn + in let Internal p = to_internal_promise p in match (underlying p).state with @@ -3062,37 +3118,40 @@ struct - let apply f x = try f x with exn -> fail exn + let apply f x = + try f x with exn when is_not_ocaml_runtime_exception exn -> fail exn - let wrap f = try return (f ()) with exn -> fail exn + let wrap f = + try return (f ()) + with exn when is_not_ocaml_runtime_exception exn -> fail exn let wrap1 f x1 = try return (f x1) - with exn -> fail exn + with exn when is_not_ocaml_runtime_exception exn -> fail exn let wrap2 f x1 x2 = try return (f x1 x2) - with exn -> fail exn + with exn when is_not_ocaml_runtime_exception exn -> fail exn let wrap3 f x1 x2 x3 = try return (f x1 x2 x3) - with exn -> fail exn + with exn when is_not_ocaml_runtime_exception exn -> fail exn let wrap4 f x1 x2 x3 x4 = try return (f x1 x2 x3 x4) - with exn -> fail exn + with exn when is_not_ocaml_runtime_exception exn -> fail exn let wrap5 f x1 x2 x3 x4 x5 = try return (f x1 x2 x3 x4 x5) - with exn -> fail exn + with exn when is_not_ocaml_runtime_exception exn -> fail exn let wrap6 f x1 x2 x3 x4 x5 x6 = try return (f x1 x2 x3 x4 x5 x6) - with exn -> fail exn + with exn when is_not_ocaml_runtime_exception exn -> fail exn let wrap7 f x1 x2 x3 x4 x5 x6 x7 = try return (f x1 x2 x3 x4 x5 x6 x7) - with exn -> fail exn + with exn when is_not_ocaml_runtime_exception exn -> fail exn diff --git a/src/core/lwt.mli b/src/core/lwt.mli index 4d8ad6dd6..0b1e7407c 100644 --- a/src/core/lwt.mli +++ b/src/core/lwt.mli @@ -2006,6 +2006,8 @@ val ignore_result : _ t -> unit val poll : 'a t -> 'a option val apply : ('a -> 'b t) -> 'a -> 'b t +val is_not_ocaml_runtime_exception : exn -> bool + val backtrace_bind : (exn -> exn) -> 'a t -> ('a -> 'b t) -> 'b t val backtrace_catch : diff --git a/src/react/lwt_react.ml b/src/react/lwt_react.ml index 84b86c806..16b541c2f 100644 --- a/src/react/lwt_react.ml +++ b/src/react/lwt_react.ml @@ -95,7 +95,12 @@ module E = struct let event, push = create () in let t = Lwt.pause () >>= fun () -> - Lwt_stream.iter (fun v -> try push v with exn -> !Lwt.async_exception_hook exn) stream in + Lwt_stream.iter + (fun v -> + try push v + with exn when Lwt.is_not_ocaml_runtime_exception exn -> + !Lwt.async_exception_hook exn) + stream in with_finaliser (cancel_thread t) event let delay thread = diff --git a/src/unix/lwt_io.ml b/src/unix/lwt_io.ml index 34ff686c1..9510f6fd3 100644 --- a/src/unix/lwt_io.ml +++ b/src/unix/lwt_io.ml @@ -533,8 +533,12 @@ let make : mode = mode; offset = 0L; typ = - Type_normal - (perform_io, fun pos cmd -> try seek pos cmd with e -> Lwt.fail e); + Type_normal ( + perform_io, + fun pos cmd -> + try seek pos cmd + with e when Lwt.is_not_ocaml_runtime_exception e -> Lwt.fail e + ); } and wrapper = { state = Idle; channel = ch; diff --git a/src/unix/lwt_preemptive.ml b/src/unix/lwt_preemptive.ml index 7e34e5d44..cb60ca83e 100644 --- a/src/unix/lwt_preemptive.ml +++ b/src/unix/lwt_preemptive.ml @@ -182,7 +182,7 @@ let detach f args = let task () = try result := Result.Ok (f args) - with exn -> + with exn when Lwt.is_not_ocaml_runtime_exception exn -> result := Result.Error exn in get_worker () >>= fun worker -> diff --git a/src/unix/lwt_timeout.ml b/src/unix/lwt_timeout.ml index 77993038b..dea9cfda9 100644 --- a/src/unix/lwt_timeout.ml +++ b/src/unix/lwt_timeout.ml @@ -72,7 +72,8 @@ let rec loop () = (*XXX Should probably report any exception *) try x.action () - with e -> !handle_exn e + with e when Lwt.is_not_ocaml_runtime_exception e -> + !handle_exn e done; curr := (!curr + 1) mod (Array.length !buckets); if !count > 0 then loop () else begin stopped := true; Lwt.return_unit end) diff --git a/src/unix/lwt_unix.cppo.ml b/src/unix/lwt_unix.cppo.ml index 8d1fa7f31..ea1173e8d 100644 --- a/src/unix/lwt_unix.cppo.ml +++ b/src/unix/lwt_unix.cppo.ml @@ -184,7 +184,7 @@ let wait_for_jobs () = let wrap_result f x = try Result.Ok (f x) - with exn -> + with exn when Lwt.is_not_ocaml_runtime_exception exn -> Result.Error exn let run_job_aux async_method job result = @@ -244,7 +244,7 @@ external run_job_sync : 'a job -> 'a = "lwt_unix_run_job_sync" let self_result job = try Result.Ok (self_result job) - with exn -> + with exn when Lwt.is_not_ocaml_runtime_exception exn -> Result.Error exn let in_retention_test = ref false @@ -267,7 +267,7 @@ let run_job ?async_method job = if async_method = Async_none then try Lwt.return (run_job_sync job) - with exn -> + with exn when Lwt.is_not_ocaml_runtime_exception exn -> Lwt.fail exn else run_job_aux async_method job self_result @@ -519,7 +519,7 @@ let rec retry_syscall node event ch wakener action = Requeued Read | Retry_write -> Requeued Write - | e -> + | e when Lwt.is_not_ocaml_runtime_exception e -> Exn e in match res with @@ -581,7 +581,7 @@ let wrap_syscall event ch action = register_action Read ch action | Retry_write -> register_action Write ch action - | e -> + | e when Lwt.is_not_ocaml_runtime_exception e -> Lwt.fail e (* +-----------------------------------------------------------------+ @@ -2272,7 +2272,7 @@ let on_signal_full signum handler = in (try set_signal signum notification - with exn -> + with exn when Lwt.is_not_ocaml_runtime_exception exn -> stop_notification notification; raise exn); signals := Signal_map.add signum (notification, actions) !signals; @@ -2376,7 +2376,7 @@ let install_sigchld_handler () = Lwt_sequence.remove node; Lwt.wakeup wakener v end - with e -> + with e when Lwt.is_not_ocaml_runtime_exception e -> Lwt_sequence.remove node; Lwt.wakeup_exn wakener e end wait_children) diff --git a/test/core/test_lwt.ml b/test/core/test_lwt.ml index 909713864..195715d6b 100644 --- a/test/core/test_lwt.ml +++ b/test/core/test_lwt.ml @@ -635,6 +635,38 @@ let catch_tests = suite "catch" [ Lwt.wakeup_exn r1 Exit; p4 end; + + test "catch with ocaml-runtime exception" begin fun () -> + try + Lwt.catch + (fun () -> raise Out_of_memory) + (fun _ -> Lwt.return_false) + with + | Out_of_memory -> Lwt.return_true + end; + + test "try_bind with ocaml-runtime exception" begin fun () -> + try + Lwt.try_bind + (fun () -> raise Out_of_memory) + (fun () -> Lwt.return_false) + (fun _ -> Lwt.return_false) + with + | Out_of_memory -> Lwt.return_true + end; + + test "try_bind(2) with ocaml-runtime exception" begin fun () -> + try + let _ = + Lwt.try_bind + (fun () -> Lwt.return_unit) + (fun () -> raise Out_of_memory) + (fun _ -> Lwt.return_false) + in + Lwt.return_false + with + | Out_of_memory -> Lwt.return_true + end; ] let suites = suites @ [catch_tests] From 2837e08b7453a340c5b313b4ccd60c93f42d849e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Wed, 12 Oct 2022 14:19:16 +0200 Subject: [PATCH 20/62] Test that we indeed don't catch OOM/SO --- test/unix/main.ml | 6 + test/unix/test_run_and_runtime_exceptions.ml | 110 +++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 test/unix/test_run_and_runtime_exceptions.ml diff --git a/test/unix/main.ml b/test/unix/main.ml index 34d2d4983..a3e7efb07 100644 --- a/test/unix/main.ml +++ b/test/unix/main.ml @@ -16,3 +16,9 @@ let () = Test_lwt_bytes.suite; Test_sleep_and_timeout.suite; ] + +let () = + (* tests that cannot be run inside of the test framework because they manage + their own Lwt_main.run *) + Test_run_and_runtime_exceptions.test (); + () diff --git a/test/unix/test_run_and_runtime_exceptions.ml b/test/unix/test_run_and_runtime_exceptions.ml new file mode 100644 index 000000000..983b4aabb --- /dev/null +++ b/test/unix/test_run_and_runtime_exceptions.ml @@ -0,0 +1,110 @@ +(* This file is part of Lwt, released under the MIT license. See LICENSE.md for + details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) + +open Lwt.Syntax + +let test () = + + begin + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + if true then raise Out_of_memory else Lwt.return_unit + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + end; + + begin + try + let () = Lwt_main.run ( + let* () = Lwt_unix.sleep 0.001 in + if true then raise Out_of_memory else Lwt.return_unit + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + end; + + begin + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + Lwt.choose [ + (let* () = Lwt.pause () in raise Out_of_memory); + Lwt_unix.sleep 2.; + ] + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + end; + + begin + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + Lwt.catch + (fun () -> raise Out_of_memory) + (fun _ -> Lwt.return_unit) + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + end; + + begin + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + Lwt.try_bind + (fun () -> raise Out_of_memory) + (fun () -> Lwt.return_unit) + (fun _ -> Lwt.return_unit) + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + end; + + begin + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + let _ = + Lwt.async + (fun () -> let* () = Lwt.pause () in raise Out_of_memory) + in + Lwt_unix.sleep 0.5 + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + end; + + begin + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + let _ = + Lwt.dont_wait + (fun () -> let* () = Lwt.pause () in raise Out_of_memory) + (fun _ -> ()) + in + Lwt_unix.sleep 0.5 + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + end; + + Printf.printf "Test run+raise ok\n"; + () From 61d34af7539f0e291b5aa60df13e809b4d03f165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Thu, 27 Oct 2022 09:49:15 +0200 Subject: [PATCH 21/62] Don't catch runtime exceptions, even in Lwt_main.run --- src/unix/lwt_main.ml | 2 +- src/unix/lwt_main.mli | 8 ++ test/unix/dune | 42 ++++++- test/unix/main.ml | 6 - test/unix/ocaml_runtime_exc_1.ml | 26 +++++ test/unix/ocaml_runtime_exc_2.ml | 27 +++++ test/unix/ocaml_runtime_exc_3.ml | 31 ++++++ test/unix/ocaml_runtime_exc_4.ml | 30 +++++ test/unix/ocaml_runtime_exc_5.ml | 32 ++++++ test/unix/ocaml_runtime_exc_6.ml | 33 ++++++ test/unix/test_run_and_runtime_exceptions.ml | 110 ------------------- 11 files changed, 229 insertions(+), 118 deletions(-) create mode 100644 test/unix/ocaml_runtime_exc_1.ml create mode 100644 test/unix/ocaml_runtime_exc_2.ml create mode 100644 test/unix/ocaml_runtime_exc_3.ml create mode 100644 test/unix/ocaml_runtime_exc_4.ml create mode 100644 test/unix/ocaml_runtime_exc_5.ml create mode 100644 test/unix/ocaml_runtime_exc_6.ml delete mode 100644 test/unix/test_run_and_runtime_exceptions.ml diff --git a/src/unix/lwt_main.ml b/src/unix/lwt_main.ml index 25d0ccaee..460181e40 100644 --- a/src/unix/lwt_main.ml +++ b/src/unix/lwt_main.ml @@ -107,7 +107,7 @@ let run p = | result -> finished (); result - | exception exn -> + | exception exn when Lwt.is_not_ocaml_runtime_exception exn -> finished (); raise exn diff --git a/src/unix/lwt_main.mli b/src/unix/lwt_main.mli index 3b4f174b7..130d6562b 100644 --- a/src/unix/lwt_main.mli +++ b/src/unix/lwt_main.mli @@ -39,6 +39,14 @@ let () = Lwt_main.run (main ()) [Lwt_main.run] will raise [Failure]. This should be considered a logic error (i.e., code making such a call is inherently broken). + In addition, note that Lwt does not attempt to catch exceptions thrown by + the OCaml runtime. Specifically, Lwt lets [Out_of_memory] and + [Stack_overflow] exceptions traverse all of its functions and bubble up to + the caller of [Lwt_main.run]. Moreover because these exceptions are left + to traverse the call stack, they leave the internal data-structures in an + inconsistent state. For this reason, calling [Lwt_main.run] again after + such an exception will raise [Failure]. + It is not safe to call [Lwt_main.run] in a function registered with [Stdlib.at_exit], use {!Lwt_main.at_exit} instead. *) diff --git a/test/unix/dune b/test/unix/dune index a81c56e7a..a918f6bb7 100644 --- a/test/unix/dune +++ b/test/unix/dune @@ -1,7 +1,17 @@ (library (name tester) (libraries lwt lwttester) - (modules (:standard \ main dummy) )) + (modules + (:standard + \ + main + dummy + ocaml_runtime_exc_1 + ocaml_runtime_exc_2 + ocaml_runtime_exc_3 + ocaml_runtime_exc_4 + ocaml_runtime_exc_5 + ocaml_runtime_exc_6))) (executable (name dummy) @@ -13,6 +23,36 @@ (libraries lwttester tester) (modules main)) +(executable + (name ocaml_runtime_exc_1) + (libraries lwt lwt.unix) + (modules ocaml_runtime_exc_1)) + +(executable + (name ocaml_runtime_exc_2) + (libraries lwt lwt.unix) + (modules ocaml_runtime_exc_2)) + +(executable + (name ocaml_runtime_exc_3) + (libraries lwt lwt.unix) + (modules ocaml_runtime_exc_3)) + +(executable + (name ocaml_runtime_exc_4) + (libraries lwt lwt.unix) + (modules ocaml_runtime_exc_4)) + +(executable + (name ocaml_runtime_exc_5) + (libraries lwt lwt.unix) + (modules ocaml_runtime_exc_5)) + +(executable + (name ocaml_runtime_exc_6) + (libraries lwt lwt.unix) + (modules ocaml_runtime_exc_6)) + (alias (name runtest) (package lwt) diff --git a/test/unix/main.ml b/test/unix/main.ml index a3e7efb07..34d2d4983 100644 --- a/test/unix/main.ml +++ b/test/unix/main.ml @@ -16,9 +16,3 @@ let () = Test_lwt_bytes.suite; Test_sleep_and_timeout.suite; ] - -let () = - (* tests that cannot be run inside of the test framework because they manage - their own Lwt_main.run *) - Test_run_and_runtime_exceptions.test (); - () diff --git a/test/unix/ocaml_runtime_exc_1.ml b/test/unix/ocaml_runtime_exc_1.ml new file mode 100644 index 000000000..bf902de7b --- /dev/null +++ b/test/unix/ocaml_runtime_exc_1.ml @@ -0,0 +1,26 @@ +(* This file is part of Lwt, released under the MIT license. See LICENSE.md for + details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) + +(* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a + different way than other exceptions and they leave the Lwt main loop in an + inconsistent state where it cannot be restarted. Indeed, attempting to call + [Lwt_main.run] again after it has crashed with a runtime exception causes a + "Nested calls to Lwt_main.run are not allowed" error. + + For this reason, we run this test as its own executable rather than as part + of a larger suite. *) + +open Lwt.Syntax + +let test () = + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + if true then raise Out_of_memory else Lwt.return_unit + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + +let () = test () diff --git a/test/unix/ocaml_runtime_exc_2.ml b/test/unix/ocaml_runtime_exc_2.ml new file mode 100644 index 000000000..474331b91 --- /dev/null +++ b/test/unix/ocaml_runtime_exc_2.ml @@ -0,0 +1,27 @@ +(* This file is part of Lwt, released under the MIT license. See LICENSE.md for + details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) + +(* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a + different way than other exceptions and they leave the Lwt main loop in an + inconsistent state where it cannot be restarted. Indeed, attempting to call + [Lwt_main.run] again after it has crashed with a runtime exception causes a + "Nested calls to Lwt_main.run are not allowed" error. + + For this reason, we run this test as its own executable rather than as part + of a larger suite. *) + +open Lwt.Syntax + +let test () = + try + let () = Lwt_main.run ( + let* () = Lwt_unix.sleep 0.001 in + if true then raise Out_of_memory else Lwt.return_unit + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + +let () = test () + diff --git a/test/unix/ocaml_runtime_exc_3.ml b/test/unix/ocaml_runtime_exc_3.ml new file mode 100644 index 000000000..76df14d19 --- /dev/null +++ b/test/unix/ocaml_runtime_exc_3.ml @@ -0,0 +1,31 @@ +(* This file is part of Lwt, released under the MIT license. See LICENSE.md for + details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) + +(* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a + different way than other exceptions and they leave the Lwt main loop in an + inconsistent state where it cannot be restarted. Indeed, attempting to call + [Lwt_main.run] again after it has crashed with a runtime exception causes a + "Nested calls to Lwt_main.run are not allowed" error. + + For this reason, we run this test as its own executable rather than as part + of a larger suite. *) + +open Lwt.Syntax + +let test () = + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + Lwt.choose [ + (let* () = Lwt.pause () in raise Out_of_memory); + Lwt_unix.sleep 2.; + ] + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + +let () = test () + + diff --git a/test/unix/ocaml_runtime_exc_4.ml b/test/unix/ocaml_runtime_exc_4.ml new file mode 100644 index 000000000..29ffef935 --- /dev/null +++ b/test/unix/ocaml_runtime_exc_4.ml @@ -0,0 +1,30 @@ +(* This file is part of Lwt, released under the MIT license. See LICENSE.md for + details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) + +(* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a + different way than other exceptions and they leave the Lwt main loop in an + inconsistent state where it cannot be restarted. Indeed, attempting to call + [Lwt_main.run] again after it has crashed with a runtime exception causes a + "Nested calls to Lwt_main.run are not allowed" error. + + For this reason, we run this test as its own executable rather than as part + of a larger suite. *) + +open Lwt.Syntax + +let test () = + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + Lwt.catch + (fun () -> raise Out_of_memory) + (fun _ -> Lwt.return_unit) + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + +let () = test () + + diff --git a/test/unix/ocaml_runtime_exc_5.ml b/test/unix/ocaml_runtime_exc_5.ml new file mode 100644 index 000000000..185d99a98 --- /dev/null +++ b/test/unix/ocaml_runtime_exc_5.ml @@ -0,0 +1,32 @@ +(* This file is part of Lwt, released under the MIT license. See LICENSE.md for + details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) + +(* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a + different way than other exceptions and they leave the Lwt main loop in an + inconsistent state where it cannot be restarted. Indeed, attempting to call + [Lwt_main.run] again after it has crashed with a runtime exception causes a + "Nested calls to Lwt_main.run are not allowed" error. + + For this reason, we run this test as its own executable rather than as part + of a larger suite. *) + +open Lwt.Syntax + +let test () = + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + let _ = + Lwt.async + (fun () -> let* () = Lwt.pause () in raise Out_of_memory) + in + Lwt_unix.sleep 0.5 + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + +let () = test () + + diff --git a/test/unix/ocaml_runtime_exc_6.ml b/test/unix/ocaml_runtime_exc_6.ml new file mode 100644 index 000000000..c9069fafe --- /dev/null +++ b/test/unix/ocaml_runtime_exc_6.ml @@ -0,0 +1,33 @@ +(* This file is part of Lwt, released under the MIT license. See LICENSE.md for + details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) + +(* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a + different way than other exceptions and they leave the Lwt main loop in an + inconsistent state where it cannot be restarted. Indeed, attempting to call + [Lwt_main.run] again after it has crashed with a runtime exception causes a + "Nested calls to Lwt_main.run are not allowed" error. + + For this reason, we run this test as its own executable rather than as part + of a larger suite. *) + +open Lwt.Syntax + +let test () = + try + let () = Lwt_main.run ( + let* () = Lwt.pause () in + let _ = + Lwt.dont_wait + (fun () -> let* () = Lwt.pause () in raise Out_of_memory) + (fun _ -> ()) + in + Lwt_unix.sleep 0.5 + ) in + Printf.eprintf "Test run+raise failure\n"; + Stdlib.exit 1 + with + | Out_of_memory -> () + +let () = test () + + diff --git a/test/unix/test_run_and_runtime_exceptions.ml b/test/unix/test_run_and_runtime_exceptions.ml deleted file mode 100644 index 983b4aabb..000000000 --- a/test/unix/test_run_and_runtime_exceptions.ml +++ /dev/null @@ -1,110 +0,0 @@ -(* This file is part of Lwt, released under the MIT license. See LICENSE.md for - details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) - -open Lwt.Syntax - -let test () = - - begin - try - let () = Lwt_main.run ( - let* () = Lwt.pause () in - if true then raise Out_of_memory else Lwt.return_unit - ) in - Printf.eprintf "Test run+raise failure\n"; - Stdlib.exit 1 - with - | Out_of_memory -> () - end; - - begin - try - let () = Lwt_main.run ( - let* () = Lwt_unix.sleep 0.001 in - if true then raise Out_of_memory else Lwt.return_unit - ) in - Printf.eprintf "Test run+raise failure\n"; - Stdlib.exit 1 - with - | Out_of_memory -> () - end; - - begin - try - let () = Lwt_main.run ( - let* () = Lwt.pause () in - Lwt.choose [ - (let* () = Lwt.pause () in raise Out_of_memory); - Lwt_unix.sleep 2.; - ] - ) in - Printf.eprintf "Test run+raise failure\n"; - Stdlib.exit 1 - with - | Out_of_memory -> () - end; - - begin - try - let () = Lwt_main.run ( - let* () = Lwt.pause () in - Lwt.catch - (fun () -> raise Out_of_memory) - (fun _ -> Lwt.return_unit) - ) in - Printf.eprintf "Test run+raise failure\n"; - Stdlib.exit 1 - with - | Out_of_memory -> () - end; - - begin - try - let () = Lwt_main.run ( - let* () = Lwt.pause () in - Lwt.try_bind - (fun () -> raise Out_of_memory) - (fun () -> Lwt.return_unit) - (fun _ -> Lwt.return_unit) - ) in - Printf.eprintf "Test run+raise failure\n"; - Stdlib.exit 1 - with - | Out_of_memory -> () - end; - - begin - try - let () = Lwt_main.run ( - let* () = Lwt.pause () in - let _ = - Lwt.async - (fun () -> let* () = Lwt.pause () in raise Out_of_memory) - in - Lwt_unix.sleep 0.5 - ) in - Printf.eprintf "Test run+raise failure\n"; - Stdlib.exit 1 - with - | Out_of_memory -> () - end; - - begin - try - let () = Lwt_main.run ( - let* () = Lwt.pause () in - let _ = - Lwt.dont_wait - (fun () -> let* () = Lwt.pause () in raise Out_of_memory) - (fun _ -> ()) - in - Lwt_unix.sleep 0.5 - ) in - Printf.eprintf "Test run+raise failure\n"; - Stdlib.exit 1 - with - | Out_of_memory -> () - end; - - Printf.printf "Test run+raise ok\n"; - () From d3c969896038ffb8a92da804e13a56f92848dc17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Thu, 27 Oct 2022 09:49:48 +0200 Subject: [PATCH 22/62] Don't catch runtime exceptions in Lwt_seq --- src/core/lwt_seq.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/lwt_seq.ml b/src/core/lwt_seq.ml index 5c9fe3ade..d336a811f 100644 --- a/src/core/lwt_seq.ml +++ b/src/core/lwt_seq.ml @@ -270,7 +270,7 @@ let rec unfold f u () = match f u with | None -> return_nil | Some (x, u') -> Lwt.return (Cons (x, unfold f u')) - | exception exc -> Lwt.fail exc + | exception exc when Lwt.is_not_ocaml_runtime_exception exc -> Lwt.fail exc let rec unfold_lwt f u () = let* x = f u in @@ -305,7 +305,7 @@ let rec of_seq seq () = | Seq.Nil -> return_nil | Seq.Cons (x, next) -> Lwt.return (Cons (x, (of_seq next))) - | exception exn -> Lwt.fail exn + | exception exn when Lwt.is_not_ocaml_runtime_exception exn -> Lwt.fail exn let rec of_seq_lwt (seq: 'a Lwt.t Seq.t): 'a t = fun () -> match seq () with @@ -321,4 +321,4 @@ let of_seq_lwt (seq: 'a Lwt.t Seq.t): 'a t = fun () -> let+ x = x in let next = of_seq_lwt next in Cons (x, next) - | exception exc -> Lwt.fail exc + | exception exc when Lwt.is_not_ocaml_runtime_exception exc -> Lwt.fail exc From 5928b4b1c4f81cbf97da41aded6b9b20c9a92de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Fri, 3 Mar 2023 11:11:46 +0100 Subject: [PATCH 23/62] Make exception filtering optional --- CHANGES | 1 + src/core/lwt.ml | 66 +++++++++++++++++--------------- src/core/lwt.mli | 34 +++++++++++++++- src/core/lwt_seq.ml | 6 +-- src/react/lwt_react.ml | 2 +- src/unix/lwt_io.ml | 2 +- src/unix/lwt_main.ml | 2 +- src/unix/lwt_preemptive.ml | 2 +- src/unix/lwt_timeout.ml | 2 +- src/unix/lwt_unix.cppo.ml | 14 +++---- test/core/test_lwt.ml | 4 ++ test/unix/ocaml_runtime_exc_1.ml | 3 ++ test/unix/ocaml_runtime_exc_2.ml | 3 ++ test/unix/ocaml_runtime_exc_3.ml | 3 ++ test/unix/ocaml_runtime_exc_4.ml | 3 ++ test/unix/ocaml_runtime_exc_5.ml | 3 ++ test/unix/ocaml_runtime_exc_6.ml | 3 ++ 17 files changed, 106 insertions(+), 47 deletions(-) diff --git a/CHANGES b/CHANGES index 2b0328dd6..829a478cf 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ ====== Additions ====== + * Lwt.set_exception_filter for enabling/disabling system-exception catching (#964) * Lwt.reraise an exception raising function which preserves backtraces, recommended for use in Lwt.catch (#963) * Expose Lwt_io.delete_recursively for deleting a directory and its content recursively. (#984, Antonin Décimo) diff --git a/src/core/lwt.ml b/src/core/lwt.ml index fc6498bbb..a61c815bd 100644 --- a/src/core/lwt.ml +++ b/src/core/lwt.ml @@ -710,12 +710,18 @@ struct end open Basic_helpers - -(* Small helper function to avoid catching ocaml-runtime exceptions *) -let is_not_ocaml_runtime_exception = function +(* Small helpers to avoid catching ocaml-runtime exceptions *) +type exception_filter = exn -> bool +let catch_all_filter = fun _ -> true +let catch_not_runtime_filter = function | Out_of_memory -> false | Stack_overflow -> false | _ -> true +let exception_filter = + (* Default value: the legacy behaviour to avoid breaking programs *) + ref catch_all_filter +let set_exception_filter f = exception_filter := f +let filter_exception e = !exception_filter e module Sequence_associated_storage : sig @@ -796,7 +802,7 @@ struct let result = f () in current_storage := saved_storage; result - with exn when is_not_ocaml_runtime_exception exn -> + with exn when !exception_filter exn -> current_storage := saved_storage; raise exn end @@ -1134,7 +1140,7 @@ struct be reject later, it is not the responsibility of this function to pass the exception to [!async_exception_hook]. *) try f v - with exn when is_not_ocaml_runtime_exception exn -> + with exn when !exception_filter exn -> !async_exception_hook exn @@ -1834,7 +1840,7 @@ struct let p' = try f v with exn - when is_not_ocaml_runtime_exception exn -> fail exn + when !exception_filter exn -> fail exn in let Internal p' = to_internal_promise p' in (* Run the user's function [f]. *) @@ -1900,7 +1906,7 @@ struct let p' = try f v - with exn when is_not_ocaml_runtime_exception exn -> + with exn when !exception_filter exn -> fail (add_loc exn) in let Internal p' = to_internal_promise p' in @@ -1957,7 +1963,7 @@ struct let p''_result = try Fulfilled (f v) with exn - when is_not_ocaml_runtime_exception exn -> Rejected exn + when !exception_filter exn -> Rejected exn in let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in @@ -1987,7 +1993,7 @@ struct to_public_promise {state = try Fulfilled (f v) - with exn when is_not_ocaml_runtime_exception exn -> Rejected exn}) + with exn when !exception_filter exn -> Rejected exn}) ~if_deferred:(fun () -> let (p'', callback) = create_result_promise_and_callback_if_deferred () in @@ -2006,7 +2012,7 @@ struct let catch f h = let p = try f () - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn in let Internal p = to_internal_promise p in let p = underlying p in @@ -2031,7 +2037,7 @@ struct let p' = try h exn - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn in let Internal p' = to_internal_promise p' in @@ -2067,7 +2073,7 @@ struct let backtrace_catch add_loc f h = let p = try f () - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn in let Internal p = to_internal_promise p in let p = underlying p in @@ -2092,7 +2098,7 @@ struct let p' = try h exn - with exn when is_not_ocaml_runtime_exception exn -> + with exn when !exception_filter exn -> fail (add_loc exn) in let Internal p' = to_internal_promise p' in @@ -2129,7 +2135,7 @@ struct let try_bind f f' h = let p = try f () - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn in let Internal p = to_internal_promise p in let p = underlying p in @@ -2146,7 +2152,7 @@ struct let p' = try f' v - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn in let Internal p' = to_internal_promise p' in @@ -2162,7 +2168,7 @@ struct let p' = try h exn - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn in let Internal p' = to_internal_promise p' in @@ -2204,7 +2210,7 @@ struct let backtrace_try_bind add_loc f f' h = let p = try f () - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn in let Internal p = to_internal_promise p in let p = underlying p in @@ -2221,7 +2227,7 @@ struct let p' = try f' v - with exn when is_not_ocaml_runtime_exception exn -> + with exn when !exception_filter exn -> fail (add_loc exn) in let Internal p' = to_internal_promise p' in @@ -2238,7 +2244,7 @@ struct let p' = try h exn - with exn when is_not_ocaml_runtime_exception exn -> + with exn when !exception_filter exn -> fail (add_loc exn) in let Internal p' = to_internal_promise p' in @@ -2493,7 +2499,7 @@ struct let dont_wait f h = let p = try f () - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn in let Internal p = to_internal_promise p in @@ -2516,7 +2522,7 @@ struct let async f = let p = try f () - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn in let Internal p = to_internal_promise p in @@ -3119,39 +3125,39 @@ struct let apply f x = - try f x with exn when is_not_ocaml_runtime_exception exn -> fail exn + try f x with exn when !exception_filter exn -> fail exn let wrap f = try return (f ()) - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn let wrap1 f x1 = try return (f x1) - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn let wrap2 f x1 x2 = try return (f x1 x2) - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn let wrap3 f x1 x2 x3 = try return (f x1 x2 x3) - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn let wrap4 f x1 x2 x3 x4 = try return (f x1 x2 x3 x4) - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn let wrap5 f x1 x2 x3 x4 x5 = try return (f x1 x2 x3 x4 x5) - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn let wrap6 f x1 x2 x3 x4 x5 x6 = try return (f x1 x2 x3 x4 x5 x6) - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn let wrap7 f x1 x2 x3 x4 x5 x6 x7 = try return (f x1 x2 x3 x4 x5 x6 x7) - with exn when is_not_ocaml_runtime_exception exn -> fail exn + with exn when !exception_filter exn -> fail exn diff --git a/src/core/lwt.mli b/src/core/lwt.mli index 0b1e7407c..41edb973f 100644 --- a/src/core/lwt.mli +++ b/src/core/lwt.mli @@ -1999,6 +1999,36 @@ val ignore_result : _ t -> unit resolved, completing any associated side effects along the way. In fact, the function that does {e that} is ordinary {!Lwt.bind}. *) +(** {4 Runtime exception filters} + + Depending on the kind of programs that you write, you may need to treat + exceptions thrown by the OCaml runtime (namely [Out_of_memory] and + [Stack_overflow] differently. This is because (a) these exceptions are not + reproducible (in that they are thrown at different points of your program + depending on the machine that your program runs on) and (b) recovering + from these errors may be impossible. + + The helpers below allow you to change the way that Lwt handles the two OCaml + runtime exceptions [Out_of_memory] and [Stack_overflow]. *) + +(** An [exception_filter] is a value which indicates to Lwt what exceptions to + catch and what exceptions to let bubble up all the way out of the main loop + immediately. *) +type exception_filter + +(** [catch_all_filter] is the default filter. With it the all the exceptions + (including [Out_of_memory] and [Stack_overflow]) are caught and transformed + into rejected promises. *) +val catch_all_filter : exception_filter + +(** [catch_not_runtime_filter] is a filter which lets the OCaml runtime + exceptions ([Out_of_memory] and [Stack_overflow]) go through all the Lwt + abstractions and bubble all the way out of the call to [Lwt_main.run]. *) +val catch_not_runtime_filter : exception_filter + +(** [set_exception_filter] sets the given exception filter globally. *) +val set_exception_filter : exception_filter -> unit + (**/**) @@ -2006,8 +2036,6 @@ val ignore_result : _ t -> unit val poll : 'a t -> 'a option val apply : ('a -> 'b t) -> 'a -> 'b t -val is_not_ocaml_runtime_exception : exn -> bool - val backtrace_bind : (exn -> exn) -> 'a t -> ('a -> 'b t) -> 'b t val backtrace_catch : @@ -2020,3 +2048,5 @@ val backtrace_try_bind : val abandon_wakeups : unit -> unit val debug_state_is : 'a state -> 'a t -> bool t + +val filter_exception : exn -> bool diff --git a/src/core/lwt_seq.ml b/src/core/lwt_seq.ml index d336a811f..c398f3e3a 100644 --- a/src/core/lwt_seq.ml +++ b/src/core/lwt_seq.ml @@ -270,7 +270,7 @@ let rec unfold f u () = match f u with | None -> return_nil | Some (x, u') -> Lwt.return (Cons (x, unfold f u')) - | exception exc when Lwt.is_not_ocaml_runtime_exception exc -> Lwt.fail exc + | exception exc when Lwt.filter_exception exc -> Lwt.fail exc let rec unfold_lwt f u () = let* x = f u in @@ -305,7 +305,7 @@ let rec of_seq seq () = | Seq.Nil -> return_nil | Seq.Cons (x, next) -> Lwt.return (Cons (x, (of_seq next))) - | exception exn when Lwt.is_not_ocaml_runtime_exception exn -> Lwt.fail exn + | exception exn when Lwt.filter_exception exn -> Lwt.fail exn let rec of_seq_lwt (seq: 'a Lwt.t Seq.t): 'a t = fun () -> match seq () with @@ -321,4 +321,4 @@ let of_seq_lwt (seq: 'a Lwt.t Seq.t): 'a t = fun () -> let+ x = x in let next = of_seq_lwt next in Cons (x, next) - | exception exc when Lwt.is_not_ocaml_runtime_exception exc -> Lwt.fail exc + | exception exc when Lwt.filter_exception exc -> Lwt.fail exc diff --git a/src/react/lwt_react.ml b/src/react/lwt_react.ml index 16b541c2f..5c7061097 100644 --- a/src/react/lwt_react.ml +++ b/src/react/lwt_react.ml @@ -98,7 +98,7 @@ module E = struct Lwt_stream.iter (fun v -> try push v - with exn when Lwt.is_not_ocaml_runtime_exception exn -> + with exn when Lwt.filter_exception exn -> !Lwt.async_exception_hook exn) stream in with_finaliser (cancel_thread t) event diff --git a/src/unix/lwt_io.ml b/src/unix/lwt_io.ml index 9510f6fd3..037b40141 100644 --- a/src/unix/lwt_io.ml +++ b/src/unix/lwt_io.ml @@ -537,7 +537,7 @@ let make : perform_io, fun pos cmd -> try seek pos cmd - with e when Lwt.is_not_ocaml_runtime_exception e -> Lwt.fail e + with e when Lwt.filter_exception e -> Lwt.fail e ); } and wrapper = { state = Idle; diff --git a/src/unix/lwt_main.ml b/src/unix/lwt_main.ml index 460181e40..d1e05e91d 100644 --- a/src/unix/lwt_main.ml +++ b/src/unix/lwt_main.ml @@ -107,7 +107,7 @@ let run p = | result -> finished (); result - | exception exn when Lwt.is_not_ocaml_runtime_exception exn -> + | exception exn when Lwt.filter_exception exn -> finished (); raise exn diff --git a/src/unix/lwt_preemptive.ml b/src/unix/lwt_preemptive.ml index cb60ca83e..0ea595217 100644 --- a/src/unix/lwt_preemptive.ml +++ b/src/unix/lwt_preemptive.ml @@ -182,7 +182,7 @@ let detach f args = let task () = try result := Result.Ok (f args) - with exn when Lwt.is_not_ocaml_runtime_exception exn -> + with exn when Lwt.filter_exception exn -> result := Result.Error exn in get_worker () >>= fun worker -> diff --git a/src/unix/lwt_timeout.ml b/src/unix/lwt_timeout.ml index dea9cfda9..ed7211bf4 100644 --- a/src/unix/lwt_timeout.ml +++ b/src/unix/lwt_timeout.ml @@ -72,7 +72,7 @@ let rec loop () = (*XXX Should probably report any exception *) try x.action () - with e when Lwt.is_not_ocaml_runtime_exception e -> + with e when Lwt.filter_exception e -> !handle_exn e done; curr := (!curr + 1) mod (Array.length !buckets); diff --git a/src/unix/lwt_unix.cppo.ml b/src/unix/lwt_unix.cppo.ml index ea1173e8d..f1d15a4a5 100644 --- a/src/unix/lwt_unix.cppo.ml +++ b/src/unix/lwt_unix.cppo.ml @@ -184,7 +184,7 @@ let wait_for_jobs () = let wrap_result f x = try Result.Ok (f x) - with exn when Lwt.is_not_ocaml_runtime_exception exn -> + with exn when Lwt.filter_exception exn -> Result.Error exn let run_job_aux async_method job result = @@ -244,7 +244,7 @@ external run_job_sync : 'a job -> 'a = "lwt_unix_run_job_sync" let self_result job = try Result.Ok (self_result job) - with exn when Lwt.is_not_ocaml_runtime_exception exn -> + with exn when Lwt.filter_exception exn -> Result.Error exn let in_retention_test = ref false @@ -267,7 +267,7 @@ let run_job ?async_method job = if async_method = Async_none then try Lwt.return (run_job_sync job) - with exn when Lwt.is_not_ocaml_runtime_exception exn -> + with exn when Lwt.filter_exception exn -> Lwt.fail exn else run_job_aux async_method job self_result @@ -519,7 +519,7 @@ let rec retry_syscall node event ch wakener action = Requeued Read | Retry_write -> Requeued Write - | e when Lwt.is_not_ocaml_runtime_exception e -> + | e when Lwt.filter_exception e -> Exn e in match res with @@ -581,7 +581,7 @@ let wrap_syscall event ch action = register_action Read ch action | Retry_write -> register_action Write ch action - | e when Lwt.is_not_ocaml_runtime_exception e -> + | e when Lwt.filter_exception e -> Lwt.fail e (* +-----------------------------------------------------------------+ @@ -2272,7 +2272,7 @@ let on_signal_full signum handler = in (try set_signal signum notification - with exn when Lwt.is_not_ocaml_runtime_exception exn -> + with exn when Lwt.filter_exception exn -> stop_notification notification; raise exn); signals := Signal_map.add signum (notification, actions) !signals; @@ -2376,7 +2376,7 @@ let install_sigchld_handler () = Lwt_sequence.remove node; Lwt.wakeup wakener v end - with e when Lwt.is_not_ocaml_runtime_exception e -> + with e when Lwt.filter_exception e -> Lwt_sequence.remove node; Lwt.wakeup_exn wakener e end wait_children) diff --git a/test/core/test_lwt.ml b/test/core/test_lwt.ml index 195715d6b..6ed07aae1 100644 --- a/test/core/test_lwt.ml +++ b/test/core/test_lwt.ml @@ -636,7 +636,9 @@ let catch_tests = suite "catch" [ p4 end; + test "catch with ocaml-runtime exception" begin fun () -> + Lwt.set_exception_filter Lwt.catch_not_runtime_filter; try Lwt.catch (fun () -> raise Out_of_memory) @@ -646,6 +648,7 @@ let catch_tests = suite "catch" [ end; test "try_bind with ocaml-runtime exception" begin fun () -> + Lwt.set_exception_filter Lwt.catch_not_runtime_filter; try Lwt.try_bind (fun () -> raise Out_of_memory) @@ -656,6 +659,7 @@ let catch_tests = suite "catch" [ end; test "try_bind(2) with ocaml-runtime exception" begin fun () -> + Lwt.set_exception_filter Lwt.catch_not_runtime_filter; try let _ = Lwt.try_bind diff --git a/test/unix/ocaml_runtime_exc_1.ml b/test/unix/ocaml_runtime_exc_1.ml index bf902de7b..76ff65596 100644 --- a/test/unix/ocaml_runtime_exc_1.ml +++ b/test/unix/ocaml_runtime_exc_1.ml @@ -1,6 +1,9 @@ (* This file is part of Lwt, released under the MIT license. See LICENSE.md for details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) +(* set the exception filter being tested *) +let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter + (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an inconsistent state where it cannot be restarted. Indeed, attempting to call diff --git a/test/unix/ocaml_runtime_exc_2.ml b/test/unix/ocaml_runtime_exc_2.ml index 474331b91..83a2f158e 100644 --- a/test/unix/ocaml_runtime_exc_2.ml +++ b/test/unix/ocaml_runtime_exc_2.ml @@ -1,6 +1,9 @@ (* This file is part of Lwt, released under the MIT license. See LICENSE.md for details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) +(* set the exception filter being tested *) +let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter + (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an inconsistent state where it cannot be restarted. Indeed, attempting to call diff --git a/test/unix/ocaml_runtime_exc_3.ml b/test/unix/ocaml_runtime_exc_3.ml index 76df14d19..beed6611d 100644 --- a/test/unix/ocaml_runtime_exc_3.ml +++ b/test/unix/ocaml_runtime_exc_3.ml @@ -1,6 +1,9 @@ (* This file is part of Lwt, released under the MIT license. See LICENSE.md for details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) +(* set the exception filter being tested *) +let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter + (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an inconsistent state where it cannot be restarted. Indeed, attempting to call diff --git a/test/unix/ocaml_runtime_exc_4.ml b/test/unix/ocaml_runtime_exc_4.ml index 29ffef935..6ba480ba1 100644 --- a/test/unix/ocaml_runtime_exc_4.ml +++ b/test/unix/ocaml_runtime_exc_4.ml @@ -1,6 +1,9 @@ (* This file is part of Lwt, released under the MIT license. See LICENSE.md for details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) +(* set the exception filter being tested *) +let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter + (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an inconsistent state where it cannot be restarted. Indeed, attempting to call diff --git a/test/unix/ocaml_runtime_exc_5.ml b/test/unix/ocaml_runtime_exc_5.ml index 185d99a98..99f2814e3 100644 --- a/test/unix/ocaml_runtime_exc_5.ml +++ b/test/unix/ocaml_runtime_exc_5.ml @@ -1,6 +1,9 @@ (* This file is part of Lwt, released under the MIT license. See LICENSE.md for details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) +(* set the exception filter being tested *) +let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter + (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an inconsistent state where it cannot be restarted. Indeed, attempting to call diff --git a/test/unix/ocaml_runtime_exc_6.ml b/test/unix/ocaml_runtime_exc_6.ml index c9069fafe..6123d76fa 100644 --- a/test/unix/ocaml_runtime_exc_6.ml +++ b/test/unix/ocaml_runtime_exc_6.ml @@ -1,6 +1,9 @@ (* This file is part of Lwt, released under the MIT license. See LICENSE.md for details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) +(* set the exception filter being tested *) +let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter + (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an inconsistent state where it cannot be restarted. Indeed, attempting to call From f953a5bb549eaf538a30ae75d27be2bbc63a1ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Thu, 6 Jul 2023 10:50:08 +0200 Subject: [PATCH 24/62] improve names and documentation --- src/core/lwt.ml | 6 +++--- src/core/lwt.mli | 16 ++++++++-------- src/unix/lwt_main.mli | 17 ++++++++++------- test/core/test_lwt.ml | 6 +++--- test/unix/ocaml_runtime_exc_1.ml | 2 +- test/unix/ocaml_runtime_exc_2.ml | 2 +- test/unix/ocaml_runtime_exc_3.ml | 2 +- test/unix/ocaml_runtime_exc_4.ml | 2 +- test/unix/ocaml_runtime_exc_5.ml | 2 +- test/unix/ocaml_runtime_exc_6.ml | 2 +- 10 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/core/lwt.ml b/src/core/lwt.ml index a61c815bd..2a6889e8d 100644 --- a/src/core/lwt.ml +++ b/src/core/lwt.ml @@ -712,14 +712,14 @@ open Basic_helpers (* Small helpers to avoid catching ocaml-runtime exceptions *) type exception_filter = exn -> bool -let catch_all_filter = fun _ -> true -let catch_not_runtime_filter = function +let catch_filter__all = fun _ -> true +let catch_filter__all_except_runtime = function | Out_of_memory -> false | Stack_overflow -> false | _ -> true let exception_filter = (* Default value: the legacy behaviour to avoid breaking programs *) - ref catch_all_filter + ref catch_filter__all let set_exception_filter f = exception_filter := f let filter_exception e = !exception_filter e diff --git a/src/core/lwt.mli b/src/core/lwt.mli index 41edb973f..f33ce3175 100644 --- a/src/core/lwt.mli +++ b/src/core/lwt.mli @@ -2003,10 +2003,10 @@ val ignore_result : _ t -> unit Depending on the kind of programs that you write, you may need to treat exceptions thrown by the OCaml runtime (namely [Out_of_memory] and - [Stack_overflow] differently. This is because (a) these exceptions are not - reproducible (in that they are thrown at different points of your program - depending on the machine that your program runs on) and (b) recovering - from these errors may be impossible. + [Stack_overflow]) differently than all the other exceptions. This is because + (a) these exceptions are not reproducible (in that they are thrown at + different points of your program depending on the machine that your program + runs on) and (b) recovering from these errors may be impossible. The helpers below allow you to change the way that Lwt handles the two OCaml runtime exceptions [Out_of_memory] and [Stack_overflow]. *) @@ -2016,15 +2016,15 @@ val ignore_result : _ t -> unit immediately. *) type exception_filter -(** [catch_all_filter] is the default filter. With it the all the exceptions +(** [catch_filter__all] is the default filter. With it the all the exceptions (including [Out_of_memory] and [Stack_overflow]) are caught and transformed into rejected promises. *) -val catch_all_filter : exception_filter +val catch_filter__all : exception_filter -(** [catch_not_runtime_filter] is a filter which lets the OCaml runtime +(** [catch_filter__all_except_runtime] is a filter which lets the OCaml runtime exceptions ([Out_of_memory] and [Stack_overflow]) go through all the Lwt abstractions and bubble all the way out of the call to [Lwt_main.run]. *) -val catch_not_runtime_filter : exception_filter +val catch_filter__all_except_runtime : exception_filter (** [set_exception_filter] sets the given exception filter globally. *) val set_exception_filter : exception_filter -> unit diff --git a/src/unix/lwt_main.mli b/src/unix/lwt_main.mli index 130d6562b..c6e7625f4 100644 --- a/src/unix/lwt_main.mli +++ b/src/unix/lwt_main.mli @@ -39,13 +39,16 @@ let () = Lwt_main.run (main ()) [Lwt_main.run] will raise [Failure]. This should be considered a logic error (i.e., code making such a call is inherently broken). - In addition, note that Lwt does not attempt to catch exceptions thrown by - the OCaml runtime. Specifically, Lwt lets [Out_of_memory] and - [Stack_overflow] exceptions traverse all of its functions and bubble up to - the caller of [Lwt_main.run]. Moreover because these exceptions are left - to traverse the call stack, they leave the internal data-structures in an - inconsistent state. For this reason, calling [Lwt_main.run] again after - such an exception will raise [Failure]. + In addition, note that if you have set the exception filter to let runtime + exceptions bubble up (via + [Lwt.set_exception_filter catch_filter__all_except_runtime]) then Lwt does + not attempt to catch exceptions thrown by the OCaml runtime. Specifically, + in this case, Lwt lets [Out_of_memory] and [Stack_overflow] exceptions + traverse all of its functions and bubble up to the caller of + [Lwt_main.run]. Moreover because these exceptions are left to traverse the + call stack, they leave the internal data-structures in an inconsistent + state. For this reason, calling [Lwt_main.run] again after such an + exception will raise [Failure]. It is not safe to call [Lwt_main.run] in a function registered with [Stdlib.at_exit], use {!Lwt_main.at_exit} instead. *) diff --git a/test/core/test_lwt.ml b/test/core/test_lwt.ml index 6ed07aae1..7bfdca8fb 100644 --- a/test/core/test_lwt.ml +++ b/test/core/test_lwt.ml @@ -638,7 +638,7 @@ let catch_tests = suite "catch" [ test "catch with ocaml-runtime exception" begin fun () -> - Lwt.set_exception_filter Lwt.catch_not_runtime_filter; + Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime; try Lwt.catch (fun () -> raise Out_of_memory) @@ -648,7 +648,7 @@ let catch_tests = suite "catch" [ end; test "try_bind with ocaml-runtime exception" begin fun () -> - Lwt.set_exception_filter Lwt.catch_not_runtime_filter; + Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime; try Lwt.try_bind (fun () -> raise Out_of_memory) @@ -659,7 +659,7 @@ let catch_tests = suite "catch" [ end; test "try_bind(2) with ocaml-runtime exception" begin fun () -> - Lwt.set_exception_filter Lwt.catch_not_runtime_filter; + Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime; try let _ = Lwt.try_bind diff --git a/test/unix/ocaml_runtime_exc_1.ml b/test/unix/ocaml_runtime_exc_1.ml index 76ff65596..1fd848369 100644 --- a/test/unix/ocaml_runtime_exc_1.ml +++ b/test/unix/ocaml_runtime_exc_1.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter +let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an diff --git a/test/unix/ocaml_runtime_exc_2.ml b/test/unix/ocaml_runtime_exc_2.ml index 83a2f158e..07a9ecd1b 100644 --- a/test/unix/ocaml_runtime_exc_2.ml +++ b/test/unix/ocaml_runtime_exc_2.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter +let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an diff --git a/test/unix/ocaml_runtime_exc_3.ml b/test/unix/ocaml_runtime_exc_3.ml index beed6611d..5d70d52cc 100644 --- a/test/unix/ocaml_runtime_exc_3.ml +++ b/test/unix/ocaml_runtime_exc_3.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter +let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an diff --git a/test/unix/ocaml_runtime_exc_4.ml b/test/unix/ocaml_runtime_exc_4.ml index 6ba480ba1..492ebdeff 100644 --- a/test/unix/ocaml_runtime_exc_4.ml +++ b/test/unix/ocaml_runtime_exc_4.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter +let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an diff --git a/test/unix/ocaml_runtime_exc_5.ml b/test/unix/ocaml_runtime_exc_5.ml index 99f2814e3..dc6d88835 100644 --- a/test/unix/ocaml_runtime_exc_5.ml +++ b/test/unix/ocaml_runtime_exc_5.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter +let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an diff --git a/test/unix/ocaml_runtime_exc_6.ml b/test/unix/ocaml_runtime_exc_6.ml index 6123d76fa..5244e453f 100644 --- a/test/unix/ocaml_runtime_exc_6.ml +++ b/test/unix/ocaml_runtime_exc_6.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_not_runtime_filter +let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an From f4da93056512d330e6778bb200b0baacf9e4ecbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Tue, 25 Jul 2023 09:50:04 +0200 Subject: [PATCH 25/62] Better naming for exception filters --- src/core/lwt.ml | 78 ++++++++++++++++---------------- src/core/lwt.mli | 36 ++++++++------- src/core/lwt_seq.ml | 6 +-- src/react/lwt_react.ml | 2 +- src/unix/lwt_io.ml | 2 +- src/unix/lwt_main.ml | 2 +- src/unix/lwt_main.mli | 8 ++-- src/unix/lwt_preemptive.ml | 2 +- src/unix/lwt_timeout.ml | 2 +- src/unix/lwt_unix.cppo.ml | 14 +++--- test/core/test_lwt.ml | 6 +-- test/unix/ocaml_runtime_exc_1.ml | 2 +- test/unix/ocaml_runtime_exc_2.ml | 2 +- test/unix/ocaml_runtime_exc_3.ml | 2 +- test/unix/ocaml_runtime_exc_4.ml | 2 +- test/unix/ocaml_runtime_exc_5.ml | 2 +- test/unix/ocaml_runtime_exc_6.ml | 2 +- 17 files changed, 88 insertions(+), 82 deletions(-) diff --git a/src/core/lwt.ml b/src/core/lwt.ml index 2a6889e8d..be6f5128f 100644 --- a/src/core/lwt.ml +++ b/src/core/lwt.ml @@ -711,17 +711,19 @@ end open Basic_helpers (* Small helpers to avoid catching ocaml-runtime exceptions *) -type exception_filter = exn -> bool -let catch_filter__all = fun _ -> true -let catch_filter__all_except_runtime = function - | Out_of_memory -> false - | Stack_overflow -> false - | _ -> true -let exception_filter = - (* Default value: the legacy behaviour to avoid breaking programs *) - ref catch_filter__all -let set_exception_filter f = exception_filter := f -let filter_exception e = !exception_filter e +module Exception_filter = struct + type t = exn -> bool + let handle_all = fun _ -> true + let handle_all_except_runtime = function + | Out_of_memory -> false + | Stack_overflow -> false + | _ -> true + let v = + (* Default value: the legacy behaviour to avoid breaking programs *) + ref handle_all + let set f = v := f + let run e = !v e +end module Sequence_associated_storage : sig @@ -802,7 +804,7 @@ struct let result = f () in current_storage := saved_storage; result - with exn when !exception_filter exn -> + with exn when Exception_filter.run exn -> current_storage := saved_storage; raise exn end @@ -1140,7 +1142,7 @@ struct be reject later, it is not the responsibility of this function to pass the exception to [!async_exception_hook]. *) try f v - with exn when !exception_filter exn -> + with exn when Exception_filter.run exn -> !async_exception_hook exn @@ -1840,7 +1842,7 @@ struct let p' = try f v with exn - when !exception_filter exn -> fail exn + when Exception_filter.run exn -> fail exn in let Internal p' = to_internal_promise p' in (* Run the user's function [f]. *) @@ -1906,7 +1908,7 @@ struct let p' = try f v - with exn when !exception_filter exn -> + with exn when Exception_filter.run exn -> fail (add_loc exn) in let Internal p' = to_internal_promise p' in @@ -1963,7 +1965,7 @@ struct let p''_result = try Fulfilled (f v) with exn - when !exception_filter exn -> Rejected exn + when Exception_filter.run exn -> Rejected exn in let State_may_now_be_pending_proxy p'' = may_now_be_proxy p'' in @@ -1993,7 +1995,7 @@ struct to_public_promise {state = try Fulfilled (f v) - with exn when !exception_filter exn -> Rejected exn}) + with exn when Exception_filter.run exn -> Rejected exn}) ~if_deferred:(fun () -> let (p'', callback) = create_result_promise_and_callback_if_deferred () in @@ -2012,7 +2014,7 @@ struct let catch f h = let p = try f () - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn in let Internal p = to_internal_promise p in let p = underlying p in @@ -2037,7 +2039,7 @@ struct let p' = try h exn - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn in let Internal p' = to_internal_promise p' in @@ -2073,7 +2075,7 @@ struct let backtrace_catch add_loc f h = let p = try f () - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn in let Internal p = to_internal_promise p in let p = underlying p in @@ -2098,7 +2100,7 @@ struct let p' = try h exn - with exn when !exception_filter exn -> + with exn when Exception_filter.run exn -> fail (add_loc exn) in let Internal p' = to_internal_promise p' in @@ -2135,7 +2137,7 @@ struct let try_bind f f' h = let p = try f () - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn in let Internal p = to_internal_promise p in let p = underlying p in @@ -2152,7 +2154,7 @@ struct let p' = try f' v - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn in let Internal p' = to_internal_promise p' in @@ -2168,7 +2170,7 @@ struct let p' = try h exn - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn in let Internal p' = to_internal_promise p' in @@ -2210,7 +2212,7 @@ struct let backtrace_try_bind add_loc f f' h = let p = try f () - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn in let Internal p = to_internal_promise p in let p = underlying p in @@ -2227,7 +2229,7 @@ struct let p' = try f' v - with exn when !exception_filter exn -> + with exn when Exception_filter.run exn -> fail (add_loc exn) in let Internal p' = to_internal_promise p' in @@ -2244,7 +2246,7 @@ struct let p' = try h exn - with exn when !exception_filter exn -> + with exn when Exception_filter.run exn -> fail (add_loc exn) in let Internal p' = to_internal_promise p' in @@ -2499,7 +2501,7 @@ struct let dont_wait f h = let p = try f () - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn in let Internal p = to_internal_promise p in @@ -2522,7 +2524,7 @@ struct let async f = let p = try f () - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn in let Internal p = to_internal_promise p in @@ -3125,39 +3127,39 @@ struct let apply f x = - try f x with exn when !exception_filter exn -> fail exn + try f x with exn when Exception_filter.run exn -> fail exn let wrap f = try return (f ()) - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn let wrap1 f x1 = try return (f x1) - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn let wrap2 f x1 x2 = try return (f x1 x2) - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn let wrap3 f x1 x2 x3 = try return (f x1 x2 x3) - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn let wrap4 f x1 x2 x3 x4 = try return (f x1 x2 x3 x4) - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn let wrap5 f x1 x2 x3 x4 x5 = try return (f x1 x2 x3 x4 x5) - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn let wrap6 f x1 x2 x3 x4 x5 x6 = try return (f x1 x2 x3 x4 x5 x6) - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn let wrap7 f x1 x2 x3 x4 x5 x6 x7 = try return (f x1 x2 x3 x4 x5 x6 x7) - with exn when !exception_filter exn -> fail exn + with exn when Exception_filter.run exn -> fail exn diff --git a/src/core/lwt.mli b/src/core/lwt.mli index f33ce3175..e05a9adb6 100644 --- a/src/core/lwt.mli +++ b/src/core/lwt.mli @@ -2011,24 +2011,30 @@ val ignore_result : _ t -> unit The helpers below allow you to change the way that Lwt handles the two OCaml runtime exceptions [Out_of_memory] and [Stack_overflow]. *) -(** An [exception_filter] is a value which indicates to Lwt what exceptions to - catch and what exceptions to let bubble up all the way out of the main loop - immediately. *) -type exception_filter +module Exception_filter: sig -(** [catch_filter__all] is the default filter. With it the all the exceptions - (including [Out_of_memory] and [Stack_overflow]) are caught and transformed - into rejected promises. *) -val catch_filter__all : exception_filter + (** An [Exception_filter.t] is a value which indicates to Lwt what exceptions to + catch and what exceptions to let bubble up all the way out of the main loop + immediately. *) + type t -(** [catch_filter__all_except_runtime] is a filter which lets the OCaml runtime - exceptions ([Out_of_memory] and [Stack_overflow]) go through all the Lwt - abstractions and bubble all the way out of the call to [Lwt_main.run]. *) -val catch_filter__all_except_runtime : exception_filter + (** [handle_all] is the default filter. With it the all the exceptions + (including [Out_of_memory] and [Stack_overflow]) can be handled: caught + and transformed into rejected promises. *) + val handle_all : t -(** [set_exception_filter] sets the given exception filter globally. *) -val set_exception_filter : exception_filter -> unit + (** [handle_all_except_runtime] is a filter which lets the OCaml runtime + exceptions ([Out_of_memory] and [Stack_overflow]) go through all the Lwt + abstractions and bubble all the way out of the call to [Lwt_main.run]. *) + val handle_all_except_runtime : t + (** [set] sets the given exception filter globally. *) + val set : t -> unit + + (**/**) + val run : exn -> bool + +end (**/**) @@ -2048,5 +2054,3 @@ val backtrace_try_bind : val abandon_wakeups : unit -> unit val debug_state_is : 'a state -> 'a t -> bool t - -val filter_exception : exn -> bool diff --git a/src/core/lwt_seq.ml b/src/core/lwt_seq.ml index c398f3e3a..a263ad0f0 100644 --- a/src/core/lwt_seq.ml +++ b/src/core/lwt_seq.ml @@ -270,7 +270,7 @@ let rec unfold f u () = match f u with | None -> return_nil | Some (x, u') -> Lwt.return (Cons (x, unfold f u')) - | exception exc when Lwt.filter_exception exc -> Lwt.fail exc + | exception exc when Lwt.Exception_filter.run exc -> Lwt.fail exc let rec unfold_lwt f u () = let* x = f u in @@ -305,7 +305,7 @@ let rec of_seq seq () = | Seq.Nil -> return_nil | Seq.Cons (x, next) -> Lwt.return (Cons (x, (of_seq next))) - | exception exn when Lwt.filter_exception exn -> Lwt.fail exn + | exception exn when Lwt.Exception_filter.run exn -> Lwt.fail exn let rec of_seq_lwt (seq: 'a Lwt.t Seq.t): 'a t = fun () -> match seq () with @@ -321,4 +321,4 @@ let of_seq_lwt (seq: 'a Lwt.t Seq.t): 'a t = fun () -> let+ x = x in let next = of_seq_lwt next in Cons (x, next) - | exception exc when Lwt.filter_exception exc -> Lwt.fail exc + | exception exc when Lwt.Exception_filter.run exc -> Lwt.fail exc diff --git a/src/react/lwt_react.ml b/src/react/lwt_react.ml index 5c7061097..8d141065c 100644 --- a/src/react/lwt_react.ml +++ b/src/react/lwt_react.ml @@ -98,7 +98,7 @@ module E = struct Lwt_stream.iter (fun v -> try push v - with exn when Lwt.filter_exception exn -> + with exn when Lwt.Exception_filter.run exn -> !Lwt.async_exception_hook exn) stream in with_finaliser (cancel_thread t) event diff --git a/src/unix/lwt_io.ml b/src/unix/lwt_io.ml index 037b40141..cf0b18bf0 100644 --- a/src/unix/lwt_io.ml +++ b/src/unix/lwt_io.ml @@ -537,7 +537,7 @@ let make : perform_io, fun pos cmd -> try seek pos cmd - with e when Lwt.filter_exception e -> Lwt.fail e + with e when Lwt.Exception_filter.run e -> Lwt.fail e ); } and wrapper = { state = Idle; diff --git a/src/unix/lwt_main.ml b/src/unix/lwt_main.ml index d1e05e91d..356dc318f 100644 --- a/src/unix/lwt_main.ml +++ b/src/unix/lwt_main.ml @@ -107,7 +107,7 @@ let run p = | result -> finished (); result - | exception exn when Lwt.filter_exception exn -> + | exception exn when Lwt.Exception_filter.run exn -> finished (); raise exn diff --git a/src/unix/lwt_main.mli b/src/unix/lwt_main.mli index c6e7625f4..f2ebde219 100644 --- a/src/unix/lwt_main.mli +++ b/src/unix/lwt_main.mli @@ -41,10 +41,10 @@ let () = Lwt_main.run (main ()) In addition, note that if you have set the exception filter to let runtime exceptions bubble up (via - [Lwt.set_exception_filter catch_filter__all_except_runtime]) then Lwt does - not attempt to catch exceptions thrown by the OCaml runtime. Specifically, - in this case, Lwt lets [Out_of_memory] and [Stack_overflow] exceptions - traverse all of its functions and bubble up to the caller of + [Lwt.Exception_filter.(set handle_all_except_runtime)]) + then Lwt does not attempt to catch exceptions thrown by the OCaml runtime. + Specifically, in this case, Lwt lets [Out_of_memory] and [Stack_overflow] + exceptions traverse all of its functions and bubble up to the caller of [Lwt_main.run]. Moreover because these exceptions are left to traverse the call stack, they leave the internal data-structures in an inconsistent state. For this reason, calling [Lwt_main.run] again after such an diff --git a/src/unix/lwt_preemptive.ml b/src/unix/lwt_preemptive.ml index 0ea595217..357bd29fd 100644 --- a/src/unix/lwt_preemptive.ml +++ b/src/unix/lwt_preemptive.ml @@ -182,7 +182,7 @@ let detach f args = let task () = try result := Result.Ok (f args) - with exn when Lwt.filter_exception exn -> + with exn when Lwt.Exception_filter.run exn -> result := Result.Error exn in get_worker () >>= fun worker -> diff --git a/src/unix/lwt_timeout.ml b/src/unix/lwt_timeout.ml index ed7211bf4..088f32ebd 100644 --- a/src/unix/lwt_timeout.ml +++ b/src/unix/lwt_timeout.ml @@ -72,7 +72,7 @@ let rec loop () = (*XXX Should probably report any exception *) try x.action () - with e when Lwt.filter_exception e -> + with e when Lwt.Exception_filter.run e -> !handle_exn e done; curr := (!curr + 1) mod (Array.length !buckets); diff --git a/src/unix/lwt_unix.cppo.ml b/src/unix/lwt_unix.cppo.ml index f1d15a4a5..20f73378e 100644 --- a/src/unix/lwt_unix.cppo.ml +++ b/src/unix/lwt_unix.cppo.ml @@ -184,7 +184,7 @@ let wait_for_jobs () = let wrap_result f x = try Result.Ok (f x) - with exn when Lwt.filter_exception exn -> + with exn when Lwt.Exception_filter.run exn -> Result.Error exn let run_job_aux async_method job result = @@ -244,7 +244,7 @@ external run_job_sync : 'a job -> 'a = "lwt_unix_run_job_sync" let self_result job = try Result.Ok (self_result job) - with exn when Lwt.filter_exception exn -> + with exn when Lwt.Exception_filter.run exn -> Result.Error exn let in_retention_test = ref false @@ -267,7 +267,7 @@ let run_job ?async_method job = if async_method = Async_none then try Lwt.return (run_job_sync job) - with exn when Lwt.filter_exception exn -> + with exn when Lwt.Exception_filter.run exn -> Lwt.fail exn else run_job_aux async_method job self_result @@ -519,7 +519,7 @@ let rec retry_syscall node event ch wakener action = Requeued Read | Retry_write -> Requeued Write - | e when Lwt.filter_exception e -> + | e when Lwt.Exception_filter.run e -> Exn e in match res with @@ -581,7 +581,7 @@ let wrap_syscall event ch action = register_action Read ch action | Retry_write -> register_action Write ch action - | e when Lwt.filter_exception e -> + | e when Lwt.Exception_filter.run e -> Lwt.fail e (* +-----------------------------------------------------------------+ @@ -2272,7 +2272,7 @@ let on_signal_full signum handler = in (try set_signal signum notification - with exn when Lwt.filter_exception exn -> + with exn when Lwt.Exception_filter.run exn -> stop_notification notification; raise exn); signals := Signal_map.add signum (notification, actions) !signals; @@ -2376,7 +2376,7 @@ let install_sigchld_handler () = Lwt_sequence.remove node; Lwt.wakeup wakener v end - with e when Lwt.filter_exception e -> + with e when Lwt.Exception_filter.run e -> Lwt_sequence.remove node; Lwt.wakeup_exn wakener e end wait_children) diff --git a/test/core/test_lwt.ml b/test/core/test_lwt.ml index 7bfdca8fb..3a08fdc32 100644 --- a/test/core/test_lwt.ml +++ b/test/core/test_lwt.ml @@ -638,7 +638,7 @@ let catch_tests = suite "catch" [ test "catch with ocaml-runtime exception" begin fun () -> - Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime; + Lwt.Exception_filter.(set handle_all_except_runtime); try Lwt.catch (fun () -> raise Out_of_memory) @@ -648,7 +648,7 @@ let catch_tests = suite "catch" [ end; test "try_bind with ocaml-runtime exception" begin fun () -> - Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime; + Lwt.Exception_filter.(set handle_all_except_runtime); try Lwt.try_bind (fun () -> raise Out_of_memory) @@ -659,7 +659,7 @@ let catch_tests = suite "catch" [ end; test "try_bind(2) with ocaml-runtime exception" begin fun () -> - Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime; + Lwt.Exception_filter.(set handle_all_except_runtime); try let _ = Lwt.try_bind diff --git a/test/unix/ocaml_runtime_exc_1.ml b/test/unix/ocaml_runtime_exc_1.ml index 1fd848369..0b5144d40 100644 --- a/test/unix/ocaml_runtime_exc_1.ml +++ b/test/unix/ocaml_runtime_exc_1.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime +let () = Lwt.Exception_filter.(set handle_all_except_runtime) (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an diff --git a/test/unix/ocaml_runtime_exc_2.ml b/test/unix/ocaml_runtime_exc_2.ml index 07a9ecd1b..9d8b1c271 100644 --- a/test/unix/ocaml_runtime_exc_2.ml +++ b/test/unix/ocaml_runtime_exc_2.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime +let () = Lwt.Exception_filter.(set handle_all_except_runtime) (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an diff --git a/test/unix/ocaml_runtime_exc_3.ml b/test/unix/ocaml_runtime_exc_3.ml index 5d70d52cc..b92fa4664 100644 --- a/test/unix/ocaml_runtime_exc_3.ml +++ b/test/unix/ocaml_runtime_exc_3.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime +let () = Lwt.Exception_filter.(set handle_all_except_runtime) (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an diff --git a/test/unix/ocaml_runtime_exc_4.ml b/test/unix/ocaml_runtime_exc_4.ml index 492ebdeff..7f2071a47 100644 --- a/test/unix/ocaml_runtime_exc_4.ml +++ b/test/unix/ocaml_runtime_exc_4.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime +let () = Lwt.Exception_filter.(set handle_all_except_runtime) (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an diff --git a/test/unix/ocaml_runtime_exc_5.ml b/test/unix/ocaml_runtime_exc_5.ml index dc6d88835..460e9c5d2 100644 --- a/test/unix/ocaml_runtime_exc_5.ml +++ b/test/unix/ocaml_runtime_exc_5.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime +let () = Lwt.Exception_filter.(set handle_all_except_runtime) (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an diff --git a/test/unix/ocaml_runtime_exc_6.ml b/test/unix/ocaml_runtime_exc_6.ml index 5244e453f..efdbe8209 100644 --- a/test/unix/ocaml_runtime_exc_6.ml +++ b/test/unix/ocaml_runtime_exc_6.ml @@ -2,7 +2,7 @@ details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) (* set the exception filter being tested *) -let () = Lwt.set_exception_filter Lwt.catch_filter__all_except_runtime +let () = Lwt.Exception_filter.(set handle_all_except_runtime) (* OCaml runtime exceptions (out-of-memory, stack-overflow) are fatal in a different way than other exceptions and they leave the Lwt main loop in an From 032b1202d313719c0937e0f69aa6d0648705a648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Fri, 4 Aug 2023 09:22:27 +0200 Subject: [PATCH 26/62] Improve documentation --- CHANGES | 2 +- src/core/lwt.mli | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 829a478cf..86e5c47f7 100644 --- a/CHANGES +++ b/CHANGES @@ -18,7 +18,7 @@ ====== Additions ====== - * Lwt.set_exception_filter for enabling/disabling system-exception catching (#964) + * Lwt.Exception_filter for enabling/disabling system-exception catching (#964) * Lwt.reraise an exception raising function which preserves backtraces, recommended for use in Lwt.catch (#963) * Expose Lwt_io.delete_recursively for deleting a directory and its content recursively. (#984, Antonin Décimo) diff --git a/src/core/lwt.mli b/src/core/lwt.mli index e05a9adb6..552c536b5 100644 --- a/src/core/lwt.mli +++ b/src/core/lwt.mli @@ -2025,10 +2025,17 @@ module Exception_filter: sig (** [handle_all_except_runtime] is a filter which lets the OCaml runtime exceptions ([Out_of_memory] and [Stack_overflow]) go through all the Lwt - abstractions and bubble all the way out of the call to [Lwt_main.run]. *) + abstractions and bubble all the way out of the call to [Lwt_main.run]. + + Note that if you set this handler, then the runtime exceptions leave the + Lwt internal state inconsistent. For this reason, you will not be able to + call [Lwt_main.run] again after such an exception has escaped + [Lwt_main.run]. *) val handle_all_except_runtime : t - (** [set] sets the given exception filter globally. *) + (** [set] sets the given exception filter globally. You should call this + function at most once during the start of your program, before the + first call to [Lwt_main.run]. *) val set : t -> unit (**/**) From f0312e6efdeb58eea2d4b7e7af7e29c5bb4c611d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Fri, 4 Aug 2023 15:53:51 +0200 Subject: [PATCH 27/62] Prepare for release --- CHANGES | 22 ++++++++++++---------- lwt.opam | 2 +- lwt_ppx_let.opam | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CHANGES b/CHANGES index 86e5c47f7..02f2e4737 100644 --- a/CHANGES +++ b/CHANGES @@ -1,25 +1,27 @@ -===== dev ===== +===== 5.7.0 ===== ====== Breaking API changes ====== - * Lwt_result.catch now takes a function (unit -> 'a t) rather than a promise ('a t) (#965) + * Lwt_result.catch now takes a function (unit -> 'a t) rather than a promise ('a t). (#965) - * Remove the deprecated Lwt.result type (use Stdlib.result instead) (#968) + * Remove the deprecated Lwt.result type (use Stdlib.result instead). (#968) - * Remove the deprecated Lwt.make_value and Lwt.make_result functions (use Ok and Error instead) (#968) + * Remove the deprecated Lwt.make_value and Lwt.make_result functions (use Ok and Error instead). (#968) - * Remove the deprecated and unsafe waiter_of_wakener (keep the waiter around when you create the wakener instead) (#968) + * Remove the deprecated and unsafe waiter_of_wakener (keep the waiter around when you create the wakener instead). (#968) - * Remove the deprecated Lwt_stream.on_termination and Lwt_stream.on_terminate (bind to Lwt_stream.closed instead) (#968) + * Remove the deprecated Lwt_stream.on_termination and Lwt_stream.on_terminate (bind to Lwt_stream.closed instead). (#968) - * Remove the deprecated Lwt_stream.result type (use Stdlib.result instead) (#968) + * Remove the deprecated Lwt_stream.result type (use Stdlib.result instead). (#968) - * Remove the deprecated Lwt_stream.map_exn function (use wrap_exn instead) (#968) + * Remove the deprecated Lwt_stream.map_exn function (use wrap_exn instead). (#968) ====== Additions ====== - * Lwt.Exception_filter for enabling/disabling system-exception catching (#964) - * Lwt.reraise an exception raising function which preserves backtraces, recommended for use in Lwt.catch (#963) + * Lwt.Exception_filter for enabling/disabling system-exception catching. (#964) + + * Lwt.reraise an exception raising function which preserves backtraces, recommended for use in Lwt.catch. (#963) + * Expose Lwt_io.delete_recursively for deleting a directory and its content recursively. (#984, Antonin Décimo) * Lwt_preemptive.run_in_main_dont_wait to run a function in the main preemptive thread but without waiting for the result. (Kate Deplaix, #960) diff --git a/lwt.opam b/lwt.opam index 869f1776c..2cecf8b66 100644 --- a/lwt.opam +++ b/lwt.opam @@ -2,7 +2,7 @@ opam-version: "2.0" synopsis: "Promises and event-driven I/O" -version: "5.6.1" +version: "5.7.0" license: "MIT" homepage: "https://github.com/ocsigen/lwt" doc: "https://ocsigen.org/lwt" diff --git a/lwt_ppx_let.opam b/lwt_ppx_let.opam index d2114baa8..255acc4d4 100644 --- a/lwt_ppx_let.opam +++ b/lwt_ppx_let.opam @@ -2,7 +2,7 @@ opam-version: "2.0" synopsis: "Dummy package context for ppx_let tests" -version: "5.6.0" +version: "5.7.0" license: "MIT" homepage: "https://github.com/ocsigen/lwt" doc: "https://ocsigen.org/lwt" From 73f25656acc5214e30c340abb798cfa62ee92800 Mon Sep 17 00:00:00 2001 From: Nils Date: Sat, 23 Sep 2023 20:20:02 +0200 Subject: [PATCH 28/62] [typo] Fix missing "is" in manual.wiki --- docs/manual.wiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual.wiki b/docs/manual.wiki index c78e59f3c..fff3a43e7 100644 --- a/docs/manual.wiki +++ b/docs/manual.wiki @@ -615,7 +615,7 @@ val s' : int Lwt_stream.t = {{{libev}}} is used by default on Linux, because it supports any number of file descriptors, while {{{select}}} supports only 1024. {{{libev}}} - also much more efficient. On Windows, {{{Unix.select}}} is used because + is also much more efficient. On Windows, {{{Unix.select}}} is used because {{{libev}}} does not work properly. The user may change the backend in use at any time. From 0d8725d3d113a9f0738b599957d2d97aecdc42d8 Mon Sep 17 00:00:00 2001 From: Jerry James Date: Fri, 1 Dec 2023 19:42:55 -0700 Subject: [PATCH 29/62] Prepare for stricter checking in GCC 14 --- src/unix/config/discover.ml | 21 +++++++++---------- .../unix_c/unix_getprotoby_getservby_job.c | 1 + 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/unix/config/discover.ml b/src/unix/config/discover.ml index bd528cad7..931efd593 100644 --- a/src/unix/config/discover.ml +++ b/src/unix/config/discover.ml @@ -652,9 +652,8 @@ struct int main() { - struct hostent *he; - struct servent *se; - he = + int x; + x = gethostbyname_r( (const char*)NULL, (struct hostent*)NULL, @@ -662,9 +661,9 @@ struct (int)0, (struct hostent**)NULL, (int*)NULL); - he = + x = gethostbyaddr_r( - (const char*)NULL, + (const void*)NULL, (int)0, (int)0, (struct hostent*)NULL, @@ -672,7 +671,7 @@ struct (int)0, (struct hostent**)NULL, (int*)NULL); - se = + x = getservbyname_r( (const char*)NULL, (const char*)NULL, @@ -680,7 +679,7 @@ struct (char*)NULL, (int)0, (struct servent**)NULL); - se = + x = getservbyport_r( (int)0, (const char*)NULL, @@ -688,20 +687,20 @@ struct (char*)NULL, (int)0, (struct servent**)NULL); - pr = + x = getprotoent_r( (struct protoent*)NULL, (char*)NULL, (int)0, (struct protoent**)NULL); - pr = + x = getprotobyname_r( (const char*)NULL, (struct protoent*)NULL, (char*)NULL, (int)0, (struct protoent**)NULL); - pr = + x = getprotobynumber_r( (int)0, (struct protoent*)NULL, @@ -793,7 +792,7 @@ struct int main() { - int (*mincore_ptr)(const void*, size_t, char*) = mincore; + int (*mincore_ptr)(void*, size_t, unsigned char*) = mincore; return (int)(mincore_ptr == NULL); } |} diff --git a/src/unix/unix_c/unix_getprotoby_getservby_job.c b/src/unix/unix_c/unix_getprotoby_getservby_job.c index 5169fc9b6..88804d1ea 100644 --- a/src/unix/unix_c/unix_getprotoby_getservby_job.c +++ b/src/unix/unix_c/unix_getprotoby_getservby_job.c @@ -9,6 +9,7 @@ #define ARGS(args...) args +#include #include #include #include From abee970b7601115cdd1c8764773122f5a79829a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Thu, 14 Dec 2023 10:52:29 +0100 Subject: [PATCH 30/62] Improve documentation of Lwt_stream Fixes #1005 --- src/core/lwt_stream.mli | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/lwt_stream.mli b/src/core/lwt_stream.mli index 11e9a8472..4f74483cf 100644 --- a/src/core/lwt_stream.mli +++ b/src/core/lwt_stream.mli @@ -84,17 +84,17 @@ class type ['a] bounded_push = object is already blocked on [push], it raises {!Lwt_stream.Full}. *) method close : unit - (** Closes the stream. Any thread currently blocked on - {!Lwt_stream.bounded_push.push} fails with {!Lwt_stream.Closed}. *) + (** Closes the stream. Any thread currently blocked on a call to + the [push] method fails with {!Lwt_stream.Closed}. *) method count : int (** Number of elements in the stream queue. *) method blocked : bool - (** Is a thread is blocked on {!Lwt_stream.bounded_push.push} ? *) + (** Is a thread is blocked on a call to the [push] method? *) method closed : bool - (** Is the stream closed ? *) + (** Is the stream closed? *) method set_reference : 'a. 'a -> unit (** Set the reference to an external source. *) From 1482a71c61ae4b4857c55f36d953460b270a7e7d Mon Sep 17 00:00:00 2001 From: Idir Lankri Date: Mon, 17 Apr 2023 17:07:39 +0200 Subject: [PATCH 31/62] [ci] Use the latest version of `actions/checkout` To avoid the warning about deprecated Node.js 16 actions. See https://github.com/ocsigen/lwt/actions/runs/8134580776. --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index cff9aad19..1d0d8a6db 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -76,7 +76,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Hack Git CRLF for ocaml/setup-ocaml issue #529 if: ${{ startsWith(matrix.os, 'windows-') }} From 0886b777f82b5daeeb97fcf0e06a555386016cc1 Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Mon, 15 Apr 2024 16:13:58 +0900 Subject: [PATCH 32/62] Add OCaml 5.1 to CI Signed-off-by: Sora Morimoto --- .github/workflows/workflow.yml | 50 ++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 1d0d8a6db..80dd94649 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -1,4 +1,4 @@ -name: Main workflow +name: Builds, tests & co on: pull_request: @@ -8,21 +8,22 @@ on: - cron: 0 1 * * MON jobs: - build: + build-and-test: strategy: fail-fast: false matrix: os: - ubuntu-latest ocaml-compiler: - - 4.08.x - - 4.09.x - - 4.10.x - - 4.11.x - - 4.12.x - - 4.13.x - - 4.14.x - - 5.0.x + - "4.08" + - "4.09" + - "4.10" + - "4.11" + - "4.12" + - "4.13" + - "4.14" + - "5.0" + - "5.1" libev: - true - false @@ -33,7 +34,7 @@ jobs: *.opam include: - os: ubuntu-latest - ocaml-compiler: ocaml-variants.4.14.0+options,ocaml-option-flambda,ocaml-option-musl,ocaml-option-static + ocaml-compiler: ocaml-variants.4.14.2+options,ocaml-option-flambda,ocaml-option-musl,ocaml-option-static libev: false ppx: true local-packages: | @@ -75,20 +76,19 @@ jobs: runs-on: ${{ matrix.os }} steps: + - name: Hack Git CRLF for ocaml/setup-ocaml#529 issue + run: git config --global core.autocrlf input + - name: Checkout code uses: actions/checkout@v4 - - name: Hack Git CRLF for ocaml/setup-ocaml issue #529 - if: ${{ startsWith(matrix.os, 'windows-') }} - run: | - & "C:\Program Files\Git\bin\git.exe" config --system core.autocrlf input - - - name: Use OCaml ${{ matrix.ocaml-compiler }} + - name: Set-up OCaml uses: ocaml/setup-ocaml@v2 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-depext-flags: --with-test opam-local-packages: ${{ matrix.local-packages }} + allow-prerelease-opam: true - run: opam depext conf-libev --install if: ${{ matrix.libev == true }} @@ -110,3 +110,19 @@ jobs: - run: opam exec -- make ppx_let-test-deps ppx_let-test if: ${{ matrix.ppx == true }} + + lint-opam: + runs-on: ubuntu-latest + + steps: + - name: Checkout tree + uses: actions/checkout@v4 + + - name: Set-up OCaml + uses: ocaml/setup-ocaml@v2 + with: + ocaml-compiler: "5.1" + allow-prerelease-opam: true + dune-cache: true + + - uses: ocaml/setup-ocaml/lint-opam@v2 From 1e4421d0def4886cd420deaa2857a71b6e9580b2 Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Mon, 15 Apr 2024 16:14:14 +0900 Subject: [PATCH 33/62] Generate opam files with dune Signed-off-by: Sora Morimoto --- .github/workflows/workflow.yml | 24 +++++------ dune-project | 60 +++++++++++++++++++++++++- lwt.opam | 79 ++++++++++++++++++---------------- lwt.opam.template | 24 +++++++++++ lwt_ppx.opam | 41 ++++++++++-------- lwt_ppx_let.opam | 43 ++++++++++-------- lwt_react.opam | 40 +++++++++-------- 7 files changed, 205 insertions(+), 106 deletions(-) create mode 100644 lwt.opam.template diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 80dd94649..423bd0f5b 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -111,18 +111,18 @@ jobs: - run: opam exec -- make ppx_let-test-deps ppx_let-test if: ${{ matrix.ppx == true }} - lint-opam: - runs-on: ubuntu-latest + # lint-opam: + # runs-on: ubuntu-latest - steps: - - name: Checkout tree - uses: actions/checkout@v4 + # steps: + # - name: Checkout tree + # uses: actions/checkout@v4 - - name: Set-up OCaml - uses: ocaml/setup-ocaml@v2 - with: - ocaml-compiler: "5.1" - allow-prerelease-opam: true - dune-cache: true + # - name: Set-up OCaml + # uses: ocaml/setup-ocaml@v2 + # with: + # ocaml-compiler: "5.1" + # allow-prerelease-opam: true + # dune-cache: true - - uses: ocaml/setup-ocaml/lint-opam@v2 + # - uses: ocaml/setup-ocaml/lint-opam@v2 diff --git a/dune-project b/dune-project index bfe19a202..fcbe532a7 100644 --- a/dune-project +++ b/dune-project @@ -1 +1,59 @@ -(lang dune 1.8) +(lang dune 1.12) + +(name lwt) + +(generate_opam_files true) + +(maintainers + "Raphaël Proust " + "Anton Bachin ") +(authors "Jérôme Vouillon" "Jérémie Dimino") +(license MIT) +(source (github ocsigen/lwt)) +(documentation "https://ocsigen.org/lwt") + +(package + (name lwt_ppx_let) + (synopsis "Dummy package context for ppx_let tests") + (description "Internal package used to partition ppx_let tests.") + (depends + (ocaml (>= 4.08)) + (ppx_let :with-test) + lwt)) + +(package + (name lwt_ppx) + (synopsis "PPX syntax for Lwt, providing something similar to async/await from JavaScript") + (depends + (ocaml (>= 4.08)) + (ppxlib (>= 0.16.0)) + lwt)) + +(package + (name lwt_react) + (synopsis "Helpers for using React with Lwt") + (depends + (ocaml (>= 4.08)) + (cppo (and :build (>= 1.1.0))) + (lwt (>= 3.0.0)) + (react (>= 1.0.0)))) + +(package + (name lwt) + (synopsis "Promises and event-driven I/O") + (description "A promise is a value that may become determined in the future. + +Lwt provides typed, composable promises. Promises that are resolved by I/O are +resolved by Lwt in parallel. + +Meanwhile, OCaml code, including code creating and waiting on promises, runs in +a single thread by default. This reduces the need for locks or other +synchronization primitives. Code can be run in parallel on an opt-in basis. +") + (depends + (ocaml (>= 4.08)) + (cppo (and :build (>= 1.1.0))) + (ocamlfind (and :dev (>= 1.7.3-1))) + dune-configurator + ocplib-endian) + (depopts base-threads base-unix conf-libev)) diff --git a/lwt.opam b/lwt.opam index 2cecf8b66..75bcc6274 100644 --- a/lwt.opam +++ b/lwt.opam @@ -1,52 +1,55 @@ +# This file is generated by dune, edit dune-project instead opam-version: "2.0" - synopsis: "Promises and event-driven I/O" +description: """ +A promise is a value that may become determined in the future. + +Lwt provides typed, composable promises. Promises that are resolved by I/O are +resolved by Lwt in parallel. -version: "5.7.0" +Meanwhile, OCaml code, including code creating and waiting on promises, runs in +a single thread by default. This reduces the need for locks or other +synchronization primitives. Code can be run in parallel on an opt-in basis. +""" +maintainer: [ + "Raphaël Proust " "Anton Bachin " +] +authors: ["Jérôme Vouillon" "Jérémie Dimino"] license: "MIT" homepage: "https://github.com/ocsigen/lwt" doc: "https://ocsigen.org/lwt" bug-reports: "https://github.com/ocsigen/lwt/issues" - -authors: [ - "Jérôme Vouillon" - "Jérémie Dimino" -] -maintainer: [ - "Raphaël Proust " - "Anton Bachin " -] -dev-repo: "git+https://github.com/ocsigen/lwt.git" - depends: [ + "dune" {>= "1.12"} + "ocaml" {>= "4.08"} "cppo" {build & >= "1.1.0"} - "dune" {>= "1.8.0"} + "ocamlfind" {dev & >= "1.7.3-1"} "dune-configurator" - "ocaml" {>= "4.08"} "ocplib-endian" - - # Until https://github.com/aantron/bisect_ppx/pull/327. - # "bisect_ppx" {dev & >= "2.0.0"} - "ocamlfind" {dev & >= "1.7.3-1"} ] - -depopts: [ - "base-threads" - "base-unix" - "conf-libev" -] - +depopts: ["base-threads" "base-unix" "conf-libev"] +dev-repo: "git+https://github.com/ocsigen/lwt.git" build: [ - ["dune" "exec" "-p" name "src/unix/config/discover.exe" "--" "--save" - "--use-libev" "%{conf-libev:installed}%"] - ["dune" "build" "-p" name "-j" jobs] + ["dune" "subst"] {pinned} + [ + "dune" + "exec" + "-p" + name + "src/unix/config/discover.exe" + "--" + "--save" + "--use-libev" "%{conf-libev:installed}%" + ] + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] ] - -description: "A promise is a value that may become determined in the future. - -Lwt provides typed, composable promises. Promises that are resolved by I/O are -resolved by Lwt in parallel. - -Meanwhile, OCaml code, including code creating and waiting on promises, runs in -a single thread by default. This reduces the need for locks or other -synchronization primitives. Code can be run in parallel on an opt-in basis." diff --git a/lwt.opam.template b/lwt.opam.template new file mode 100644 index 000000000..8fc8771fc --- /dev/null +++ b/lwt.opam.template @@ -0,0 +1,24 @@ +build: [ + ["dune" "subst"] {pinned} + [ + "dune" + "exec" + "-p" + name + "src/unix/config/discover.exe" + "--" + "--save" + "--use-libev" "%{conf-libev:installed}%" + ] + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] diff --git a/lwt_ppx.opam b/lwt_ppx.opam index 63c37abe8..958244c1f 100644 --- a/lwt_ppx.opam +++ b/lwt_ppx.opam @@ -1,28 +1,33 @@ +# This file is generated by dune, edit dune-project instead opam-version: "2.0" - -synopsis: "PPX syntax for Lwt, providing something similar to async/await from JavaScript" - -version: "2.1.0" +synopsis: + "PPX syntax for Lwt, providing something similar to async/await from JavaScript" +maintainer: [ + "Raphaël Proust " "Anton Bachin " +] +authors: ["Jérôme Vouillon" "Jérémie Dimino"] license: "MIT" homepage: "https://github.com/ocsigen/lwt" -doc: "https://ocsigen.org/lwt/dev/api/Ppx_lwt" +doc: "https://ocsigen.org/lwt" bug-reports: "https://github.com/ocsigen/lwt/issues" - -authors: [ - "Gabriel Radanne" -] -maintainer: [ - "Anton Bachin " -] -dev-repo: "git+https://github.com/ocsigen/lwt.git" - depends: [ - "dune" {>= "1.8.0"} - "lwt" + "dune" {>= "1.12"} "ocaml" {>= "4.08"} "ppxlib" {>= "0.16.0"} + "lwt" ] - build: [ - ["dune" "build" "-p" name "-j" jobs] + ["dune" "subst"] {pinned} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] ] +dev-repo: "git+https://github.com/ocsigen/lwt.git" diff --git a/lwt_ppx_let.opam b/lwt_ppx_let.opam index 255acc4d4..8fbb382a3 100644 --- a/lwt_ppx_let.opam +++ b/lwt_ppx_let.opam @@ -1,28 +1,33 @@ +# This file is generated by dune, edit dune-project instead opam-version: "2.0" - synopsis: "Dummy package context for ppx_let tests" - -version: "5.7.0" +description: "Internal package used to partition ppx_let tests." +maintainer: [ + "Raphaël Proust " "Anton Bachin " +] +authors: ["Jérôme Vouillon" "Jérémie Dimino"] license: "MIT" homepage: "https://github.com/ocsigen/lwt" doc: "https://ocsigen.org/lwt" bug-reports: "https://github.com/ocsigen/lwt/issues" - -authors: [ - "Jérôme Vouillon" - "Jérémie Dimino" -] -maintainer: [ - "Raphaël Proust " - "Anton Bachin " -] -dev-repo: "git+https://github.com/ocsigen/lwt.git" - depends: [ - "dune" {>= "1.8.0"} - "lwt" - "ppx_let" {with-test} + "dune" {>= "1.12"} "ocaml" {>= "4.08"} + "ppx_let" {with-test} + "lwt" ] - -description: "Internal package used to partition ppx_let tests." +build: [ + ["dune" "subst"] {pinned} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/ocsigen/lwt.git" diff --git a/lwt_react.opam b/lwt_react.opam index c1623389d..7f5d7e195 100644 --- a/lwt_react.opam +++ b/lwt_react.opam @@ -1,29 +1,33 @@ +# This file is generated by dune, edit dune-project instead opam-version: "2.0" - synopsis: "Helpers for using React with Lwt" - -version: "1.2.0" +maintainer: [ + "Raphaël Proust " "Anton Bachin " +] +authors: ["Jérôme Vouillon" "Jérémie Dimino"] license: "MIT" homepage: "https://github.com/ocsigen/lwt" -doc: "https://ocsigen.org/lwt/dev/api/Lwt_react" +doc: "https://ocsigen.org/lwt" bug-reports: "https://github.com/ocsigen/lwt/issues" - -authors: [ - "Jérémie Dimino" -] -maintainer: [ - "Anton Bachin " -] -dev-repo: "git+https://github.com/ocsigen/lwt.git" - depends: [ - "dune" {>= "1.8.0"} - "lwt" {>= "3.0.0"} + "dune" {>= "1.12"} "ocaml" {>= "4.08"} - "react" {>= "1.0.0"} "cppo" {build & >= "1.1.0"} + "lwt" {>= "3.0.0"} + "react" {>= "1.0.0"} ] - build: [ - ["dune" "build" "-p" name "-j" jobs] + ["dune" "subst"] {pinned} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] ] +dev-repo: "git+https://github.com/ocsigen/lwt.git" From 042ee9f2f7ce96ddb4d081caf9385f044b835475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Thu, 9 Jun 2022 11:11:39 +0200 Subject: [PATCH 34/62] Convert manual from Wiki to mld MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to view the manual offline. Converted with: sed -E \ -e 's/^==== *(.*) *====/{3 \1}/g' \ -e 's/^=== *(.*) *===/{2 \1}/g' \ -e 's/^== *(.*) *==/{1 \1}/g' \ -e 's/^= *(.*) *=/{0 \1}/g' \ -e 's/\[\[(.*)\|(.*)]]/{{: \1 }\2}/g' \ -e 's|//(.*)//|{e \1}|g' \ -e 's/<>/in {!\1}/g' \ -e 's/<>/]}/g' \ -e "s/\`\`(.*)''/“\1”/g" \ -e 's/\.\.\./…/g' \ "$@" | \ perl -pe 's|{{{(.*?)}}}|[\1]|g' and then some manual fixes for lists and tables. --- docs/dune | 3 + docs/manual.mld | 986 +++++++++++++++++++++++++++++++++++++++++++++ docs/manual.wiki | 881 ---------------------------------------- src/core/index.mld | 2 +- 4 files changed, 990 insertions(+), 882 deletions(-) create mode 100644 docs/dune create mode 100644 docs/manual.mld delete mode 100644 docs/manual.wiki diff --git a/docs/dune b/docs/dune new file mode 100644 index 000000000..cb49e843e --- /dev/null +++ b/docs/dune @@ -0,0 +1,3 @@ +(documentation + (package lwt) + (mld_files :standard)) diff --git a/docs/manual.mld b/docs/manual.mld new file mode 100644 index 000000000..b6f1f319a --- /dev/null +++ b/docs/manual.mld @@ -0,0 +1,986 @@ +{0 Lwt manual } + +{1 Introduction } + + When writing a program, a common developer's task is to handle I/O + operations. Indeed, most software interacts with several different + resources, such as: + +{ul + {- the kernel, by doing system calls,} + {- the user, by reading the keyboard, the mouse, or any input device,} + {- a graphical server, to build graphical user interface,} + {- other computers, by using the network,} + {- …and so on.}} + + When this list contains only one item, it is pretty easy to + handle. However as this list grows it becomes harder and harder to + make everything work together. Several choices have been proposed + to solve this problem: + +{ul + {- using a main loop, and integrating all components we are + interacting with into this main loop,} + {- using preemptive system threads.}} + + Both solutions have their advantages and their drawbacks. For the + first one, it may work, but it becomes very complicated to write + a piece of asynchronous sequential code. The typical example is + graphical user interfaces freezing and not redrawing themselves + because they are waiting for some blocking part of the code to + complete. + + If you already wrote code using preemptive threads, you should know + that doing it right with threads is a difficult job. Moreover, system + threads consume non-negligible resources, and so you can only launch + a limited number of threads at the same time. Thus, this is not a + general solution. + + [Lwt] offers a third alternative. It provides promises, which are + very fast: a promise is just a reference that will be filled asynchronously, + and calling a function that returns a promise does not require a new stack, + new process, or anything else. It is just a normal, fast, function call. + Promises compose nicely, allowing us to write highly asynchronous programs. + + In the first part, we will explain the concepts of [Lwt], then we will + describe the main modules [Lwt] consists of. + +{2 Finding examples } + + Additional sources of examples: + +{ul + {- {{: https://github.com/dkim/rwo-lwt#readme }Concurrent Programming with Lwt}} + {- {{: https://mirage.io/wiki/tutorial-lwt }Mirage Lwt Tutorial}} + {- {{: http://www.baturin.org/code/lwt-counter-server/ }Simple Server with Lwt}}} + +{1 The Lwt core library } + + In this section we describe the basics of [Lwt]. It is advised to + start [utop] and try the given code examples. + +{2 Lwt concepts } + + Let's take a classic function of the [Pervasives] module: + +{[ +# Pervasives.input_char;; +- : in_channel -> char = +]} + + This function will wait for a character to come on the given input + channel, and then return it. The problem with this function is that it is + blocking: while it is being executed, the whole program will be + blocked, and other events will not be handled until it returns. + + Now, let's look at the lwt equivalent: + +{[ +# Lwt_io.read_char;; +- : Lwt_io.input_channel -> char Lwt.t = +]} + + As you can see, it does not return just a character, but something of + type [char Lwt.t]. The type ['a Lwt.t] is the type + of promises that can be fulfilled later with a value of type ['a]. + [Lwt_io.read_char] will try to read a character from the + given input channel and {e immediately} return a promise, without + blocking, whether a character is available or not. If a character is + not available, the promise will just not be fulfilled {e yet}. + + Now, let's see what we can do with a [Lwt] promise. The following + code creates a pipe, creates a promise that is fulfilled with the result of + reading the input side: + +{[ +# let ic, oc = Lwt_io.pipe ();; +val ic : Lwt_io.input_channel = +val oc : Lwt_io.output_channel = +# let p = Lwt_io.read_char ic;; +val p : char Lwt.t = +]} + + We can now look at the state of our newly created promise: + +{[ +# Lwt.state p;; +- : char Lwt.state = Lwt.Sleep +]} + + A promise may be in one of the following states: + +{ul + {- [Return x], which means that the promise has been fulfilled + with the value [x]. This usually implies that the asynchronous + operation, that you started by calling the function that returned the + promise, has completed successfully.} + {- [Fail exn], which means that the promise has been rejected + with the exception [exn]. This usually means that the asynchronous + operation associated with the promise has failed.} + {- [Sleep], which means that the promise is has not yet been + fulfilled or rejected, so it is {e pending}.}} + + The above promise [p] is pending because there is nothing yet + to read from the pipe. Let's write something: + +{[ +# Lwt_io.write_char oc 'a';; +- : unit Lwt.t = +# Lwt.state p;; +- : char Lwt.state = Lwt.Return 'a' +]} + + So, after we write something, the reading promise has been fulfilled + with the value ['a']. + +{2 Primitives for promise creation } + + There are several primitives for creating [Lwt] promises. These + functions are located in the module [Lwt]. + + Here are the main primitives: + +{ul + {- [Lwt.return : 'a -> 'a Lwt.t] + creates a promise which is already fulfilled with the given value} + {- [Lwt.fail : exn -> 'a Lwt.t] + creates a promise which is already rejected with the given exception} + {- [Lwt.wait : unit -> 'a Lwt.t * 'a Lwt.u] + creates a pending promise, and returns it, paired with a resolver (of + type ['a Lwt.u]), which must be used to resolve (fulfill or reject) + the promise.}} + + To resolve a pending promise, use one of the following + functions: + +{ul + {- [Lwt.wakeup : 'a Lwt.u -> 'a -> unit] + fulfills the promise with a value.} + {- [Lwt.wakeup_exn : 'a Lwt.u -> exn -> unit] + rejects the promise with an exception.}} + + Note that it is an error to try to resolve the same promise twice. [Lwt] + will raise [Invalid_argument] if you try to do so. + + With this information, try to guess the result of each of the + following expressions: + +{[ +# Lwt.state (Lwt.return 42);; +# Lwt.state (Lwt.fail Exit);; +# let p, r = Lwt.wait ();; +# Lwt.state p;; +# Lwt.wakeup r 42;; +# Lwt.state p;; +# let p, r = Lwt.wait ();; +# Lwt.state p;; +# Lwt.wakeup_exn r Exit;; +# Lwt.state p;; +]} + +{3 Primitives for promise composition } + + The most important operation you need to know is [bind]: + +{[ +val bind : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.t +]} + + [bind p f] creates a promise which waits for [p] to become + become fulfilled, then passes the resulting value to [f]. If [p] is a + pending promise, then [bind p f] will be a pending promise too, + until [p] is resolved. If [p] is rejected, then the resulting + promise will be rejected with the same exception. For example, consider the + following expression: + +{[ +Lwt.bind + (Lwt_io.read_line Lwt_io.stdin) + (fun str -> Lwt_io.printlf "You typed %S" str) +]} + + This code will first wait for the user to enter a line of text, then + print a message on the standard output. + + Similarly to [bind], there is a function to handle the case + when [p] is rejected: + +{[ +val catch : (unit -> 'a Lwt.t) -> (exn -> 'a Lwt.t) -> 'a Lwt.t +]} + + [catch f g] will call [f ()], then wait for it to become + resolved, and if it was rejected with an exception [exn], call + [g exn] to handle it. Note that both exceptions raised with + [Pervasives.raise] and [Lwt.fail] are caught by + [catch]. + +{3 Cancelable promises } + + In some case, we may want to cancel a promise. For example, because it + has not resolved after a timeout. This can be done with cancelable + promises. To create a cancelable promise, you must use the + [Lwt.task] function: + +{[ +val task : unit -> 'a Lwt.t * 'a Lwt.u +]} + + It has the same semantics as [Lwt.wait], except that the + pending promise can be canceled with [Lwt.cancel]: + +{[ +val cancel : 'a Lwt.t -> unit +]} + + The promise will then be rejected with the exception + [Lwt.Canceled]. To execute a function when the promise is + canceled, you must use [Lwt.on_cancel]: + +{[ +val on_cancel : 'a Lwt.t -> (unit -> unit) -> unit +]} + + Note that canceling a promise does not automatically cancel the + asynchronous operation that is going to resolve it. It does, however, + prevent any further chained operations from running. The asynchronous + operation associated with a promise can only be canceled if its implementation + has taken care to set an [on_cancel] callback on the promise that + it returned to you. In practice, most operations (such as system calls) + can't be canceled once they are started anyway, so promise cancellation is + useful mainly for interrupting future operations once you know that a chain of + asynchronous operations will not be needed. + + It is also possible to cancel a promise which has not been + created directly by you with [Lwt.task]. In this case, the deepest + cancelable promise that the given promise depends on will be canceled. + + For example, consider the following code: + +{[ +# let p, r = Lwt.task ();; +val p : '_a Lwt.t = +val r : '_a Lwt.u = +# let p' = Lwt.bind p (fun x -> Lwt.return (x + 1));; +val p' : int Lwt.t = +]} + + Here, cancelling [p'] will in fact cancel [p], rejecting + it with [Lwt.Canceled]. [Lwt.bind] will then propagate the + exception forward to [p']: + +{[ +# Lwt.cancel p';; +- : unit = () +# Lwt.state p;; +- : int Lwt.state = Lwt.Fail Lwt.Canceled +# Lwt.state p';; +- : int Lwt.state = Lwt.Fail Lwt.Canceled +]} + + It is possible to prevent a promise from being canceled + by using the function [Lwt.protected]: + +{[ +val protected : 'a Lwt.t -> 'a Lwt.t +]} + + Canceling [(protected p)] will have no effect on [p]. + +{3 Primitives for concurrent composition } + + We now show how to compose several promises concurrently. The + main functions for this are in the [Lwt] module: [join], + [choose] and [pick]. + + The first one, [join] takes a list of promises and returns a promise + that is waiting for all of them to resolve: + +{[ +val join : unit Lwt.t list -> unit Lwt.t +]} + + Moreover, if at least one promise is rejected, [join l] will be rejected + with the same exception as the first one, after all the promises are resolved. + + Conversely, [choose] waits for at least {e one} promise to become + resolved, then resolves with the same value or exception: + +{[ +val choose : 'a Lwt.t list -> 'a Lwt.t +]} + + For example: + +{[ +# let p1, r1 = Lwt.wait ();; +val p1 : '_a Lwt.t = +val r1 : '_a Lwt.u = +# let p2, r2 = Lwt.wait ();; +val p2 : '_a Lwt.t = +val r2 : '_a Lwt.u = +# let p3 = Lwt.choose [p1; p2];; +val p3 : '_a Lwt.t = +# Lwt.state p3;; +- : '_a Lwt.state = Lwt.Sleep +# Lwt.wakeup r2 42;; +- : unit = () +# Lwt.state p3;; +- : int Lwt.state = Lwt.Return 42 +]} + + The last one, [pick], is the same as [choose], except that it tries to cancel + all other promises when one resolves. Promises created via [Lwt.wait()] are not cancellable + and are thus not cancelled. + +{3 Rules } + + A callback, like the [f] that you might pass to [Lwt.bind], is + an ordinary OCaml function. [Lwt] just handles ordering calls to these + functions. + + [Lwt] uses some preemptive threading internally, but all of your code + runs in the main thread, except when you explicitly opt into additional + threads with [Lwt_preemptive]. + + This simplifies reasoning about critical sections: all the code in one + callback cannot be interrupted by any of the code in another callback. + However, it also carries the danger that if a single callback takes a very + long time, it will not give [Lwt] a chance to run your other callbacks. + In particular: + +{ul + {- do not write functions that may take time to complete, without splitting + them up using [Lwt.pause] or performing some [Lwt] I/O,} + {- do not do I/O that may block, otherwise the whole program will + hang inside that callback. You must instead use the asynchronous I/O + operations provided by [Lwt].}} + +{2 The syntax extension } + + [Lwt] offers a PPX syntax extension which increases code readability and + makes coding using [Lwt] easier. The syntax extension is documented + in {!Ppx_lwt}. + + To use the PPX syntax extension, add the [lwt_ppx] package when + compiling: + +{[ +$ ocamlfind ocamlc -package lwt_ppx -linkpkg -o foo foo.ml +]} + + Or, in [utop]: + +{[ +# #require "lwt_ppx";; +]} + + [lwt_ppx] is distributed in a separate opam package of that same name. + + For a brief overview of the syntax, see the Correspondence table below. + +{3 Correspondence table } + +{%html: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Without LwtWith Lwt
let pattern_1 = expr_1
+and pattern_2 = expr2
+…
+and pattern_n = expr_n in
+expr
let%lwt pattern_1 = expr_1
+and pattern_2 = expr2
+…
+and pattern_n = expr_n in
+expr
try expr with
+| pattern_1 = expr_1
+| pattern_2 = expr2
+…
+| pattern_n = expr_n
try%lwt expr with
+| pattern_1 = expr_1
+| pattern_2 = expr2
+…
+| pattern_n = expr_n
match expr with
+| pattern_1 = expr_1
+| pattern_2 = expr2
+…
+| pattern_n = expr_n
match%lwt expr with
+| pattern_1 = expr_1
+| pattern_2 = expr2
+…
+| pattern_n = expr_n
for ident = expr_init to expr_final do
+  expr
+done
for%lwt ident = expr_init to expr_final do
+  expr
+done
while expr do expr done
while%lwt expr do expr done
if expr then expr else expr
if%lwt expr then expr else expr
assert expr
assert%lwt expr
raise exn
[%lwt raise exn]
+%} + +{2 Backtrace support } + + If an exception is raised inside a callback called by Lwt, the backtrace + provided by OCaml will not be very useful. It will end inside the Lwt + scheduler instead of continuing into the code that started the operations that + led to the callback call. To avoid this, and get good backtraces from Lwt, use + the syntax extension. The [let%lwt] construct will properly propagate + backtraces. + + As always, to get backtraces from an OCaml program, you need to either declare + the environment variable [OCAMLRUNPARAM=b] or call + [Printexc.record_backtrace true] at the start of your program, and be + sure to compile it with [-g]. Most modern build systems add [-g] by + default. + +{2 [let*] syntax } + + To use Lwt with the [let*] syntax introduced in OCaml 4.08, you can open + the [Syntax] module: + +{[ +open Syntax +]} + + Then, you can write + +{[ +let* () = Lwt_io.printl "Hello," in +let* () = Lwt_io.printl "world!" in +Lwt.return () +]} + +{2 Other modules of the core library } + + The core library contains several modules that only depend on + [Lwt]. The following naming convention is used in [Lwt]: when a + function takes as argument a function, returning a promise, that is going + to be executed sequentially, it is suffixed with “[_s]”. And + when it is going to be executed concurrently, it is suffixed with + “[_p]”. For example, in the [Lwt_list] module we have: + +{[ +val map_s : ('a -> 'b Lwt.t) -> 'a list -> 'b list Lwt.t +val map_p : ('a -> 'b Lwt.t) -> 'a list -> 'b list Lwt.t +]} + +{3 Mutexes } + + [Lwt_mutex] provides mutexes for [Lwt]. Its use is almost the + same as the [Mutex] module of the thread library shipped with + OCaml. In general, programs using [Lwt] do not need a lot of + mutexes, because callbacks run without preempting each other. They are + only useful for synchronising or sequencing complex operations spread over + multiple callback calls. + +{3 Lists } + + The [Lwt_list] module defines iteration and scanning functions + over lists, similar to the ones of the [List] module, but using + functions that return a promise. For example: + +{[ +val iter_s : ('a -> unit Lwt.t) -> 'a list -> unit Lwt.t +val iter_p : ('a -> unit Lwt.t) -> 'a list -> unit Lwt.t +]} + + In [iter_s f l], [iter_s] will call f on each elements + of [l], waiting for resolution between each element. On the + contrary, in [iter_p f l], [iter_p] will call f on all + elements of [l], only then wait for all the promises to resolve. + +{3 Data streams } + + [Lwt] streams are used in a lot of places in [Lwt] and its + submodules. They offer a high-level interface to manipulate data flows. + + A stream is an object which returns elements sequentially and + lazily. Lazily means that the source of the stream is touched only for new + elements when needed. This module contains a lot of stream + transformation, iteration, and scanning functions. + + The common way of creating a stream is by using + [Lwt_stream.from] or by using [Lwt_stream.create]: + +{[ +val from : (unit -> 'a option Lwt.t) -> 'a Lwt_stream.t +val create : unit -> 'a Lwt_stream.t * ('a option -> unit) +]} + + As for streams of the standard library, [from] takes as + argument a function which is used to create new elements. + + [create] returns a function used to push new elements + into the stream and the stream which will receive them. + + For example: + +{[ +# let stream, push = Lwt_stream.create ();; +val stream : '_a Lwt_stream.t = +val push : '_a option -> unit = +# push (Some 1);; +- : unit = () +# push (Some 2);; +- : unit = () +# push (Some 3);; +- : unit = () +# Lwt.state (Lwt_stream.next stream);; +- : int Lwt.state = Lwt.Return 1 +# Lwt.state (Lwt_stream.next stream);; +- : int Lwt.state = Lwt.Return 2 +# Lwt.state (Lwt_stream.next stream);; +- : int Lwt.state = Lwt.Return 3 +# Lwt.state (Lwt_stream.next stream);; +- : int Lwt.state = Lwt.Sleep +]} + + Note that streams are consumable. Once you take an element from a + stream, it is removed from the stream. So, if you want to iterate two times + over a stream, you may consider “cloning” it, with + [Lwt_stream.clone]. Cloned stream will return the same + elements in the same order. Consuming one will not consume the other. + For example: + +{[ +# let s = Lwt_stream.of_list [1; 2];; +val s : int Lwt_stream.t = +# let s' = Lwt_stream.clone s;; +val s' : int Lwt_stream.t = +# Lwt.state (Lwt_stream.next s);; +- : int Lwt.state = Lwt.Return 1 +# Lwt.state (Lwt_stream.next s);; +- : int Lwt.state = Lwt.Return 2 +# Lwt.state (Lwt_stream.next s');; +- : int Lwt.state = Lwt.Return 1 +# Lwt.state (Lwt_stream.next s');; +- : int Lwt.state = Lwt.Return 2 +]} + +{3 Mailbox variables } + + The [Lwt_mvar] module provides mailbox variables. A mailbox + variable, also called a “mvar”, is a cell which may contain 0 or 1 + element. If it contains no elements, we say that the mvar is empty, + if it contains one, we say that it is full. Adding an element to a + full mvar will block until one is taken. Taking an element from an + empty mvar will block until one is added. + + Mailbox variables are commonly used to pass messages between chains of + callbacks being executed concurrently. + + Note that a mailbox variable can be seen as a pushable stream with a + limited memory. + +{1 Running an Lwt program } + + An [Lwt] computation you have created will give you something of type + [Lwt.t], a promise. However, even though you have the promise, the + computation may not have run yet, and the promise might still be pending. + + For example if your program is just: + +{[ +let _ = Lwt_io.printl "Hello, world!" +]} + + you have no guarantee that the promise for writing ["Hello, world!"] + on the terminal will be resolved before the program exits. In order + to wait for the promise to resolve, you have to call the function + [Lwt_main.run]: + +{[ +val Lwt_main.run : 'a Lwt.t -> 'a +]} + + This function waits for the given promise to resolve and returns + its result. In fact it does more than that; it also runs the + scheduler which is responsible for making asynchronous computations progress + when events are received from the outside world. + + So basically, when you write a [Lwt] program, you must call + [Lwt_main.run] on your top-level, outer-most promise. For instance: + +{[ +let () = Lwt_main.run (Lwt_io.printl "Hello, world!") +]} + + Note that you must not make nested calls to [Lwt_main.run]. It + cannot be used anywhere else to get the result of a promise. + +{1 The [lwt.unix] library } + + The package [lwt.unix] contains all [Unix]-dependent + modules of [Lwt]. Among all its features, it implements Lwt-friendly, + non-blocking versions of functions of the OCaml standard and Unix libraries. + +{2 Unix primitives } + + Module [Lwt_unix] provides non-blocking system calls. For example, + the [Lwt] counterpart of [Unix.read] is: + +{[ +val read : file_descr -> string -> int -> int -> int Lwt.t +]} + + [Lwt_io] provides features similar to buffered channels of + the standard library (of type [in_channel] or + [out_channel]), but with non-blocking semantics. + + [Lwt_gc] allows you to register a finalizer that returns a + promise. At the end of the program, [Lwt] will wait for all these + finalizers to resolve. + +{2 The Lwt scheduler } + + Operations doing I/O have to be resumed when some events are received by + the process, so they can resolve their associated pending promises. + For example, when you read from a file descriptor, you + may have to wait for the file descriptor to become readable if no + data are immediately available on it. + + [Lwt] contains a scheduler which is responsible for managing + multiple operations waiting for events, and restarting them when needed. + This scheduler is implemented by the two modules [Lwt_engine] + and [Lwt_main]. [Lwt_engine] is a low-level module, it + provides a signature for custom I/O multiplexers as well as two built-in + implementations, [libev] and [select]. The signature is given by the + class [Lwt_engine.t]. + + [libev] is used by default on Linux, because it supports any + number of file descriptors, while [select] supports only 1024. [libev] + is also much more efficient. On Windows, [Unix.select] is used because + [libev] does not work properly. The user may change the backend in use at + any time. + + If you see an [Invalid_argument] error on [Unix.select], it + may be because the 1024 file descriptor limit was exceeded. Try + switching to [libev], if possible. + + The engine can also be used directly in order to integrate other + libraries with [Lwt]. For example, [GTK] needs to be notified + when some events are received. If you use [Lwt] with [GTK] + you need to use the [Lwt] scheduler to monitor [GTK] + sources. This is what is done by the [Lwt_glib] library. + + The [Lwt_main] module contains the {e main loop} of + [Lwt]. It is run by calling the function [Lwt_main.run]: + +{[ +val Lwt_main.run : 'a Lwt.t -> 'a +]} + + This function continuously runs the scheduler until the promise passed + as argument is resolved. + + To make sure [Lwt] is compiled with [libev] support, + tell opam that the library is available on the system by installing the + {{: http://opam.ocaml.org/packages/conf-libev/conf-libev.4-11/ }conf-libev} + package. You may get the actual library with your system package manager: + +{ul + {- [brew install libev] on MacOSX,} + {- [apt-get install libev-dev] on Debian/Ubuntu, or} + {- [yum install libev-devel] on CentOS, which requires to set + [export C_INCLUDE_PATH=/usr/include/libev/] and + [export LIBRARY_PATH=/usr/lib64/] before calling + [opam install conf-libev].}} + + +{2 Logging } + + For logging, we recommend the [logs] package from opam, which includes an + Lwt-aware module [Logs_lwt]. + +{1 The Lwt.react library } + + The [Lwt_react] module provides helpers for using the [react] + library with [Lwt]. It extends the [React] module by adding + [Lwt]-specific functions. It can be used as a replacement of + [React]. For example you can add at the beginning of your + program: + +{[ +open Lwt_react +]} + + instead of: + +{[ +open React +]} + + or: + +{[ +module React = Lwt_react +]} + + Among the added functionalities we have [Lwt_react.E.next], which + takes an event and returns a promise which will be pending until the next + occurrence of this event. For example: + +{[ +# open Lwt_react;; +# let event, push = E.create ();; +val event : '_a React.event = +val push : '_a -> unit = +# let p = E.next event;; +val p : '_a Lwt.t = +# Lwt.state p;; +- : '_a Lwt.state = Lwt.Sleep +# push 42;; +- : unit = () +# Lwt.state p;; +- : int Lwt.state = Lwt.Return 42 +]} + + Another interesting feature is the ability to limit events + (resp. signals) from occurring (resp. changing) too often. For example, + suppose you are doing a program which displays something on the screen + each time a signal changes. If at some point the signal changes 1000 + times per second, you probably don't want to render it 1000 times per + second. For that you use [Lwt_react.S.limit]: + +{[ +val limit : (unit -> unit Lwt.t) -> 'a React.signal -> 'a React.signal +]} + + [Lwt_react.S.limit f signal] returns a signal which varies as + [signal] except that two consecutive updates are separated by a + call to [f]. For example if [f] returns a promise which is pending + for 0.1 seconds, then there will be no more than 10 changes per + second: + +{[ +open Lwt_react + +let draw x = + (* Draw the screen *) + … + +let () = + (* The signal we are interested in: *) + let signal = … in + + (* The limited signal: *) + let signal' = S.limit (fun () -> Lwt_unix.sleep 0.1) signal in + + (* Redraw the screen each time the limited signal change: *) + S.notify_p draw signal' +]} + +{1 Other libraries } + +{2 Parallelise computations to other cores } + + If you have some compute-intensive steps within your program, you can execute + them on a separate core. You can get performance benefits from the + parallelisation. In addition, whilst your compute-intensive function is running + on a different core, your normal I/O-bound tasks continue running on the + original core. + + The module {!Lwt_domain} from the [lwt_domain] package provides all the + necessary helpers to achieve this. It is based on the [Domainslib] library + and uses similar concepts (such as tasks and pools). + + First, you need to create a task pool: + +{[ +val setup_pool : ?name:string -> int -> pool +]} + + Then you simple detach the function calls to the created pool: + +{[ +val detach : pool -> ('a -> 'b) -> 'a -> 'b Lwt.t +]} + + The returned promise resolves as soon as the function returns. + +{2 Detaching computation to preemptive threads } + + It may happen that you want to run a function which will take time to + compute or that you want to use a blocking function that cannot be + used in a non-blocking way. For these situations, [Lwt] allows you to + {e detach} the computation to a preemptive thread. + + This is done by the module [Lwt_preemptive] of the + [lwt.unix] package which maintains a pool of system + threads. The main function is: + +{[ +val detach : ('a -> 'b) -> 'a -> 'b Lwt.t +]} + + [detach f x] will execute [f x] in another thread and + return a pending promise, usable from the main thread, which will be fulfilled + with the result of the preemptive thread. + + If you want to trigger some [Lwt] operations from your detached thread, + you have to call back into the main thread using + [Lwt_preemptive.run_in_main]: + +{[ +val run_in_main : (unit -> 'a Lwt.t) -> 'a +]} + + This is roughly the equivalent of [Lwt.main_run], but for detached + threads, rather than for the whole process. Note that you must not call + [Lwt_main.run] in a detached thread. + +{2 SSL support } + + The library [Lwt_ssl] allows use of SSL asynchronously. + +{1 Writing stubs using [Lwt] } + +{2 Thread-safe notifications } + + If you want to notify the main thread from another thread, you can use the [Lwt] + thread safe notification system. First you need to create a notification identifier + (which is just an integer) from the OCaml side using the + [Lwt_unix.make_notification] function, then you can send it from either the + OCaml code with [Lwt_unix.send_notification] function, or from the C code using + the function [lwt_unix_send_notification] (defined in [lwt_unix_.h]). + + Notifications are received and processed asynchronously by the main thread. + +{2 Jobs } + + For operations that cannot be executed asynchronously, [Lwt] + uses a system of jobs that can be executed in a different threads. A + job is composed of three functions: + +{ul + {- A stub function to create the job. It must allocate a new job + structure and fill its [worker] and [result] fields. This + function is executed in the main thread. + The return type for the OCaml external must be of the form ['a job].} + {- A function which executes the job. This one may be executed asynchronously + in another thread. This function must not: + {ul + {- access or allocate OCaml block values (tuples, strings, …),} + {- call OCaml code.}}} + {- A function which reads the result of the job, frees resources and + returns the result as an OCaml value. This function is executed in + the main thread.}} + + With [Lwt < 2.3.3], 4 functions (including 3 stubs) were + required. It is still possible to use this mode but it is + deprecated. + + We show as example the implementation of [Lwt_unix.mkdir]. On the C + side we have: + +{@c[/**/ +/* Structure holding informations for calling [mkdir]. */ +struct job_mkdir { + /* Informations used by lwt. + It must be the first field of the structure. */ + struct lwt_unix_job job; + /* This field store the result of the call. */ + int result; + /* This field store the value of [errno] after the call. */ + int errno_copy; + /* Pointer to a copy of the path parameter. */ + char* path; + /* Copy of the mode parameter. */ + int mode; + /* Buffer for storing the path. */ + char data[]; +}; + +/* The function calling [mkdir]. */ +static void worker_mkdir(struct job_mkdir* job) +{ + /* Perform the blocking call. */ + job->result = mkdir(job->path, job->mode); + /* Save the value of errno. */ + job->errno_copy = errno; +} + +/* The function building the caml result. */ +static value result_mkdir(struct job_mkdir* job) +{ + /* Check for errors. */ + if (job->result < 0) { + /* Save the value of errno so we can use it + once the job has been freed. */ + int error = job->errno_copy; + /* Copy the contents of job->path into a caml string. */ + value string_argument = caml_copy_string(job->path); + /* Free the job structure. */ + lwt_unix_free_job(&job->job); + /* Raise the error. */ + unix_error(error, "mkdir", string_argument); + } + /* Free the job structure. */ + lwt_unix_free_job(&job->job); + /* Return the result. */ + return Val_unit; +} + +/* The stub creating the job structure. */ +CAMLprim value lwt_unix_mkdir_job(value path, value mode) +{ + /* Get the length of the path parameter. */ + mlsize_t len_path = caml_string_length(path) + 1; + /* Allocate a new job. */ + struct job_mkdir* job = + (struct job_mkdir*)lwt_unix_new_plus(struct job_mkdir, len_path); + /* Set the offset of the path parameter inside the job structure. */ + job->path = job->data; + /* Copy the path parameter inside the job structure. */ + memcpy(job->path, String_val(path), len_path); + /* Initialize function fields. */ + job->job.worker = (lwt_unix_job_worker)worker_mkdir; + job->job.result = (lwt_unix_job_result)result_mkdir; + /* Copy the mode parameter. */ + job->mode = Int_val(mode); + /* Wrap the structure into a caml value. */ + return lwt_unix_alloc_job(&job->job); +} +]} + + and on the ocaml side: + +{[ +(* The stub for creating the job. *) +external mkdir_job : string -> int -> unit job = "lwt_unix_mkdir_job" + +(* The ocaml function. *) +let mkdir name perms = Lwt_unix.run_job (mkdir_job name perms) +]} diff --git a/docs/manual.wiki b/docs/manual.wiki deleted file mode 100644 index fff3a43e7..000000000 --- a/docs/manual.wiki +++ /dev/null @@ -1,881 +0,0 @@ -= Lwt manual = - -== Introduction == - - When writing a program, a common developer's task is to handle I/O - operations. Indeed, most software interacts with several different - resources, such as: - - * the kernel, by doing system calls, - * the user, by reading the keyboard, the mouse, or any input device, - * a graphical server, to build graphical user interface, - * other computers, by using the network, - * ...and so on. - - When this list contains only one item, it is pretty easy to - handle. However as this list grows it becomes harder and harder to - make everything work together. Several choices have been proposed - to solve this problem: - - * using a main loop, and integrating all components we are - interacting with into this main loop. - * using preemptive system threads - - Both solutions have their advantages and their drawbacks. For the - first one, it may work, but it becomes very complicated to write - a piece of asynchronous sequential code. The typical example is - graphical user interfaces freezing and not redrawing themselves - because they are waiting for some blocking part of the code to - complete. - - If you already wrote code using preemptive threads, you should know - that doing it right with threads is a difficult job. Moreover, system - threads consume non-negligible resources, and so you can only launch - a limited number of threads at the same time. Thus, this is not a - general solution. - - {{{Lwt}}} offers a third alternative. It provides promises, which are - very fast: a promise is just a reference that will be filled asynchronously, - and calling a function that returns a promise does not require a new stack, - new process, or anything else. It is just a normal, fast, function call. - Promises compose nicely, allowing us to write highly asynchronous programs. - - In the first part, we will explain the concepts of {{{Lwt}}}, then we will - describe the main modules {{{Lwt}}} consists of. - -=== Finding examples === - - Additional sources of examples: - - * [[https://github.com/dkim/rwo-lwt#readme|Concurrent Programming with Lwt]] - * [[https://mirage.io/wiki/tutorial-lwt|Mirage Lwt Tutorial]] - * [[http://www.baturin.org/code/lwt-counter-server/|Simple Server with Lwt]] - -== The Lwt core library == - - In this section we describe the basics of {{{Lwt}}}. It is advised to - start {{{utop}}} and try the given code examples. - -=== Lwt concepts === - - Let's take a classic function of the {{{Stdlib}}} module: - -< char = ->> - - This function will wait for a character to come on the given input - channel, and then return it. The problem with this function is that it is - blocking: while it is being executed, the whole program will be - blocked, and other events will not be handled until it returns. - - Now, let's look at the lwt equivalent: - -< char Lwt.t = ->> - - As you can see, it does not return just a character, but something of - type {{{char Lwt.t}}}. The type {{{'a Lwt.t}}} is the type - of promises that can be fulfilled later with a value of type {{{'a}}}. - {{{Lwt_io.read_char}}} will try to read a character from the - given input channel and //immediately// return a promise, without - blocking, whether a character is available or not. If a character is - not available, the promise will just not be fulfilled //yet//. - - Now, let's see what we can do with a {{{Lwt}}} promise. The following - code creates a pipe, creates a promise that is fulfilled with the result of - reading the input side: - -< -val oc : Lwt_io.output_channel = -# let p = Lwt_io.read_char ic;; -val p : char Lwt.t = ->> - - We can now look at the state of our newly created promise: - -<> - - A promise may be in one of the following states: - - * {{{Return x}}}, which means that the promise has been fulfilled - with the value {{{x}}}. This usually implies that the asynchronous - operation, that you started by calling the function that returned the - promise, has completed successfully. - * {{{Fail exn}}}, which means that the promise has been rejected - with the exception {{{exn}}}. This usually means that the asynchronous - operation associated with the promise has failed. - * {{{Sleep}}}, which means that the promise is has not yet been - fulfilled or rejected, so it is //pending//. - - The above promise {{{p}}} is pending because there is nothing yet - to read from the pipe. Let's write something: - -< -# Lwt.state p;; -- : char Lwt.state = Lwt.Return 'a' ->> - - So, after we write something, the reading promise has been fulfilled - with the value {{{'a'}}}. - -=== Primitives for promise creation === - - There are several primitives for creating {{{Lwt}}} promises. These - functions are located in the module {{{Lwt}}}. - - Here are the main primitives: - - * {{{Lwt.return : 'a -> 'a Lwt.t}}} - \\ - creates a promise which is already fulfilled with the given value - * {{{Lwt.fail : exn -> 'a Lwt.t}}} - \\ - creates a promise which is already rejected with the given exception - * {{{Lwt.wait : unit -> 'a Lwt.t * 'a Lwt.u}}} - \\ - creates a pending promise, and returns it, paired with a resolver (of - type {{{'a Lwt.u}}}), which must be used to resolve (fulfill or reject) - the promise. - - To resolve a pending promise, use one of the following - functions: - - * {{{Lwt.wakeup : 'a Lwt.u -> 'a -> unit}}} - \\ - fulfills the promise with a value. - * {{{Lwt.wakeup_exn : 'a Lwt.u -> exn -> unit}}} - \\ - rejects the promise with an exception. - - Note that it is an error to try to resolve the same promise twice. {{{Lwt}}} - will raise {{{Invalid_argument}}} if you try to do so. - - With this information, try to guess the result of each of the - following expressions: - -<> - -==== Primitives for promise composition ==== - - The most important operation you need to know is {{{bind}}}: - -< ('a -> 'b Lwt.t) -> 'b Lwt.t ->> - - {{{bind p f}}} creates a promise which waits for {{{p}}} to become - become fulfilled, then passes the resulting value to {{{f}}}. If {{{p}}} is a - pending promise, then {{{bind p f}}} will be a pending promise too, - until {{{p}}} is resolved. If {{{p}}} is rejected, then the resulting - promise will be rejected with the same exception. For example, consider the - following expression: - -< Lwt_io.printlf "You typed %S" str) ->> - - This code will first wait for the user to enter a line of text, then - print a message on the standard output. - - Similarly to {{{bind}}}, there is a function to handle the case - when {{{p}}} is rejected: - -< 'a Lwt.t) -> (exn -> 'a Lwt.t) -> 'a Lwt.t ->> - - {{{catch f g}}} will call {{{f ()}}}, then wait for it to become - resolved, and if it was rejected with an exception {{{exn}}}, call - {{{g exn}}} to handle it. Note that both exceptions raised with - {{{Stdlib.raise}}} and {{{Lwt.fail}}} are caught by - {{{catch}}}. - -==== Cancelable promises ==== - - In some case, we may want to cancel a promise. For example, because it - has not resolved after a timeout. This can be done with cancelable - promises. To create a cancelable promise, you must use the - {{{Lwt.task}}} function: - -< 'a Lwt.t * 'a Lwt.u ->> - - It has the same semantics as {{{Lwt.wait}}}, except that the - pending promise can be canceled with {{{Lwt.cancel}}}: - -< unit ->> - - The promise will then be rejected with the exception - {{{Lwt.Canceled}}}. To execute a function when the promise is - canceled, you must use {{{Lwt.on_cancel}}}: - -< (unit -> unit) -> unit ->> - - Note that canceling a promise does not automatically cancel the - asynchronous operation that is going to resolve it. It does, however, - prevent any further chained operations from running. The asynchronous - operation associated with a promise can only be canceled if its implementation - has taken care to set an {{{on_cancel}}} callback on the promise that - it returned to you. In practice, most operations (such as system calls) - can't be canceled once they are started anyway, so promise cancellation is - useful mainly for interrupting future operations once you know that a chain of - asynchronous operations will not be needed. - - It is also possible to cancel a promise which has not been - created directly by you with {{{Lwt.task}}}. In this case, the deepest - cancelable promise that the given promise depends on will be canceled. - - For example, consider the following code: - -< -val r : '_a Lwt.u = -# let p' = Lwt.bind p (fun x -> Lwt.return (x + 1));; -val p' : int Lwt.t = ->> - - Here, cancelling {{{p'}}} will in fact cancel {{{p}}}, rejecting - it with {{{Lwt.Canceled}}}. {{{Lwt.bind}}} will then propagate the - exception forward to {{{p'}}}: - -<> - - It is possible to prevent a promise from being canceled - by using the function {{{Lwt.protected}}}: - -< 'a Lwt.t ->> - - Canceling {{{(protected p)}}} will have no effect on {{{p}}}. - -==== Primitives for concurrent composition ==== - - We now show how to compose several promises concurrently. The - main functions for this are in the {{{Lwt}}} module: {{{join}}}, - {{{choose}}} and {{{pick}}}. - - The first one, {{{join}}} takes a list of promises and returns a promise - that is waiting for all of them to resolve: - -< unit Lwt.t ->> - - Moreover, if at least one promise is rejected, {{{join l}}} will be rejected - with the same exception as the first one, after all the promises are resolved. - - Conversely, {{{choose}}} waits for at least //one// promise to become - resolved, then resolves with the same value or exception: - -< 'a Lwt.t ->> - - For example: - -< -val r1 : '_a Lwt.u = -# let p2, r2 = Lwt.wait ();; -val p2 : '_a Lwt.t = -val r2 : '_a Lwt.u = -# let p3 = Lwt.choose [p1; p2];; -val p3 : '_a Lwt.t = -# Lwt.state p3;; -- : '_a Lwt.state = Lwt.Sleep -# Lwt.wakeup r2 42;; -- : unit = () -# Lwt.state p3;; -- : int Lwt.state = Lwt.Return 42 ->> - - The last one, {{{pick}}}, is the same as {{{choose}}}, except that it tries to cancel - all other promises when one resolves. Promises created via {{{Lwt.wait()}}} are not cancellable - and are thus not cancelled. - -==== Rules ==== - - A callback, like the {{{f}}} that you might pass to {{{Lwt.bind}}}, is - an ordinary OCaml function. {{{Lwt}}} just handles ordering calls to these - functions. - - {{{Lwt}}} uses some preemptive threading internally, but all of your code - runs in the main thread, except when you explicitly opt into additional - threads with {{{Lwt_preemptive}}}. - - This simplifies reasoning about critical sections: all the code in one - callback cannot be interrupted by any of the code in another callback. - However, it also carries the danger that if a single callback takes a very - long time, it will not give {{{Lwt}}} a chance to run your other callbacks. - In particular: - - * do not write functions that may take time to complete, without splitting - them up using {{{Lwt.pause}}} or performing some {{{Lwt}}} I/O, - * do not do I/O that may block, otherwise the whole program will - hang inside that callback. You must instead use the asynchronous I/O - operations provided by {{{Lwt}}}. - -=== The syntax extension === - - {{{Lwt}}} offers a PPX syntax extension which increases code readability and - makes coding using {{{Lwt}}} easier. The syntax extension is documented - <>. - - To use the PPX syntax extension, add the {{{lwt_ppx}}} package when - compiling: - -<> - - Or, in {{{utop}}}: - -<> - - {{{lwt_ppx}}} is distributed in a separate opam package of that same name. - - For a brief overview of the syntax, see the Correspondence table below. - -==== Correspondence table ==== - - |= without {{{Lwt}}} |= with {{{Lwt}}} | - | | | - | {{{let}}} //pattern,,1,,// {{{=}}} //expr,,1,,// | {{{let%lwt}}} //pattern,,1,,// {{{=}}} //expr,,1,,// | - | {{{and}}} //pattern,,2,,// {{{=}}} //expr,,2,,// | {{{and}}} //pattern,,2,,// {{{=}}} //expr,,2,,// | - | ... | ... | - | {{{and}}} //pattern,,n,,// {{{=}}} //expr,,n,,// {{{in}}} | {{{and}}} //pattern,,n,,// {{{=}}} //expr,,n,,// {{{in}}} | - | //expr// | //expr// | - | | | - | {{{try}}} | {{{try%lwt}}} | - | // expr// | // expr// | - | {{{with}}} | {{{with}}} | - | // // {{{|}}} //pattern,,1,,// {{{->}}} //expr,,1,,// | // // {{{|}}} //pattern,,1,,// {{{->}}} //expr,,1,,// | - | // // {{{|}}} //pattern,,2,,// {{{->}}} //expr,,2,,// | // // {{{|}}} //pattern,,2,,// {{{->}}} //expr,,2,,// | - | // // ... | // // ... | - | // // {{{|}}} //pattern,,n,,// {{{->}}} //expr,,n,,// | // // {{{|}}} //pattern,,n,,// {{{->}}} //expr,,n,,// | - | | | - | {{{for}}} //ident// {{{=}}} //expr,,init,,// {{{to}}} //expr,,final,,// {{{do}}} | {{{for%lwt}}} //ident// {{{=}}} //expr,,init,,// {{{to}}} //expr,,final,,// {{{do}}} | - | // expr// | // expr// | - | {{{done}}} | {{{done}}} | - | | | - | {{{while}}} //expr// {{{do}}} | {{{while%lwt}}} //expr// {{{do}}} | - | // expr// | // expr// | - | {{{done}}} | {{{done}}} | - | | | - | {{{assert}}} //expr// | {{{assert%lwt}}} //expr// | - | | | - | {{{match}}} //expr// {{{with}}} | {{{match%lwt}}} //expr// {{{with}}} | - | // // {{{|}}} //pattern,,1,,// {{{->}}} //expr,,1,,// | // // {{{|}}} //pattern,,1,,// {{{->}}} //expr,,1,,// | - | // // {{{|}}} //pattern,,2,,// {{{->}}} //expr,,2,,// | // // {{{|}}} //pattern,,2,,// {{{->}}} //expr,,2,,// | - | // // ... | // // ... | - | // // {{{|}}} //pattern,,n,,// {{{->}}} //expr,,n,,// | // // {{{|}}} //pattern,,n,,// {{{->}}} //expr,,n,,// | - | | | - | {{{if}}} //expr// {{{then}}} //expr// {{{else}}} //expr// | {{{if%lwt}}} //expr// {{{then}}} //expr// {{{else}}} //expr// | - | | | - | {{{raise}}} //exn// | {{{[%lwt raise}}} //exn//{{{]}}} | - -=== Backtrace support === - - If an exception is raised inside a callback called by Lwt, the backtrace - provided by OCaml will not be very useful. It will end inside the Lwt - scheduler instead of continuing into the code that started the operations that - led to the callback call. To avoid this, and get good backtraces from Lwt, use - the syntax extension. The {{{let%lwt}}} construct will properly propagate - backtraces. - - As always, to get backtraces from an OCaml program, you need to either declare - the environment variable {{{OCAMLRUNPARAM=b}}} or call - {{{Printexc.record_backtrace true}}} at the start of your program, and be - sure to compile it with {{{-g}}}. Most modern build systems add {{{-g}}} by - default. - -=== {{{let*}}} syntax === - - To use Lwt with the {{{let*}}} syntax introduced in OCaml 4.08, you can open - the {{{Syntax}}} module: - -<> - - Then, you can write - -<> - -=== Other modules of the core library === - - The core library contains several modules that only depend on - {{{Lwt}}}. The following naming convention is used in {{{Lwt}}}: when a - function takes as argument a function, returning a promise, that is going - to be executed sequentially, it is suffixed with ``{{{_s}}}''. And - when it is going to be executed concurrently, it is suffixed with - ``{{{_p}}}''. For example, in the {{{Lwt_list}}} module we have: - -< 'b Lwt.t) -> 'a list -> 'b list Lwt.t -val map_p : ('a -> 'b Lwt.t) -> 'a list -> 'b list Lwt.t ->> - -==== Mutexes ==== - - {{{Lwt_mutex}}} provides mutexes for {{{Lwt}}}. Its use is almost the - same as the {{{Mutex}}} module of the thread library shipped with - OCaml. In general, programs using {{{Lwt}}} do not need a lot of - mutexes, because callbacks run without preempting each other. They are - only useful for synchronising or sequencing complex operations spread over - multiple callback calls. - -==== Lists ==== - - The {{{Lwt_list}}} module defines iteration and scanning functions - over lists, similar to the ones of the {{{List}}} module, but using - functions that return a promise. For example: - -< unit Lwt.t) -> 'a list -> unit Lwt.t -val iter_p : ('a -> unit Lwt.t) -> 'a list -> unit Lwt.t ->> - - In {{{iter_s f l}}}, {{{iter_s}}} will call f on each elements - of {{{l}}}, waiting for resolution between each element. On the - contrary, in {{{iter_p f l}}}, {{{iter_p}}} will call f on all - elements of {{{l}}}, only then wait for all the promises to resolve. - -==== Data streams ==== - - {{{Lwt}}} streams are used in a lot of places in {{{Lwt}}} and its - submodules. They offer a high-level interface to manipulate data flows. - - A stream is an object which returns elements sequentially and - lazily. Lazily means that the source of the stream is touched only for new - elements when needed. This module contains a lot of stream - transformation, iteration, and scanning functions. - - The common way of creating a stream is by using - {{{Lwt_stream.from}}} or by using {{{Lwt_stream.create}}}: - -< 'a option Lwt.t) -> 'a Lwt_stream.t -val create : unit -> 'a Lwt_stream.t * ('a option -> unit) ->> - - As for streams of the standard library, {{{from}}} takes as - argument a function which is used to create new elements. - - {{{create}}} returns a function used to push new elements - into the stream and the stream which will receive them. - - For example: - -< -val push : '_a option -> unit = -# push (Some 1);; -- : unit = () -# push (Some 2);; -- : unit = () -# push (Some 3);; -- : unit = () -# Lwt.state (Lwt_stream.next stream);; -- : int Lwt.state = Lwt.Return 1 -# Lwt.state (Lwt_stream.next stream);; -- : int Lwt.state = Lwt.Return 2 -# Lwt.state (Lwt_stream.next stream);; -- : int Lwt.state = Lwt.Return 3 -# Lwt.state (Lwt_stream.next stream);; -- : int Lwt.state = Lwt.Sleep ->> - - Note that streams are consumable. Once you take an element from a - stream, it is removed from the stream. So, if you want to iterate two times - over a stream, you may consider ``cloning'' it, with - {{{Lwt_stream.clone}}}. Cloned stream will return the same - elements in the same order. Consuming one will not consume the other. - For example: - -< -# let s' = Lwt_stream.clone s;; -val s' : int Lwt_stream.t = -# Lwt.state (Lwt_stream.next s);; -- : int Lwt.state = Lwt.Return 1 -# Lwt.state (Lwt_stream.next s);; -- : int Lwt.state = Lwt.Return 2 -# Lwt.state (Lwt_stream.next s');; -- : int Lwt.state = Lwt.Return 1 -# Lwt.state (Lwt_stream.next s');; -- : int Lwt.state = Lwt.Return 2 ->> - -==== Mailbox variables ==== - - The {{{Lwt_mvar}}} module provides mailbox variables. A mailbox - variable, also called a ``mvar'', is a cell which may contain 0 or 1 - element. If it contains no elements, we say that the mvar is empty, - if it contains one, we say that it is full. Adding an element to a - full mvar will block until one is taken. Taking an element from an - empty mvar will block until one is added. - - Mailbox variables are commonly used to pass messages between chains of - callbacks being executed concurrently. - - Note that a mailbox variable can be seen as a pushable stream with a - limited memory. - -== Running an Lwt program == - - An {{{Lwt}}} computation you have created will give you something of type - {{{Lwt.t}}}, a promise. However, even though you have the promise, the - computation may not have run yet, and the promise might still be pending. - - For example if your program is just: - -<> - - you have no guarantee that the promise for writing {{{"Hello, world!"}}} - on the terminal will be resolved before the program exits. In order - to wait for the promise to resolve, you have to call the function - {{{Lwt_main.run}}}: - -< 'a ->> - - This function waits for the given promise to resolve and returns - its result. In fact it does more than that; it also runs the - scheduler which is responsible for making asynchronous computations progress - when events are received from the outside world. - - So basically, when you write a {{{Lwt}}} program, you must call - {{{Lwt_main.run}}} on your top-level, outer-most promise. For instance: - -<> - - Note that you must not make nested calls to {{{Lwt_main.run}}}. It - cannot be used anywhere else to get the result of a promise. - -== The {{{lwt.unix}}} library == - - The package {{{lwt.unix}}} contains all {{{Unix}}}-dependent - modules of {{{Lwt}}}. Among all its features, it implements Lwt-friendly, - non-blocking versions of functions of the OCaml standard and Unix libraries. - -=== Unix primitives === - - Module {{{Lwt_unix}}} provides non-blocking system calls. For example, - the {{{Lwt}}} counterpart of {{{Unix.read}}} is: - -< string -> int -> int -> int Lwt.t ->> - - {{{Lwt_io}}} provides features similar to buffered channels of - the standard library (of type {{{in_channel}}} or - {{{out_channel}}}), but with non-blocking semantics. - - {{{Lwt_gc}}} allows you to register a finalizer that returns a - promise. At the end of the program, {{{Lwt}}} will wait for all these - finalizers to resolve. - -=== The Lwt scheduler === - - Operations doing I/O have to be resumed when some events are received by - the process, so they can resolve their associated pending promises. - For example, when you read from a file descriptor, you - may have to wait for the file descriptor to become readable if no - data are immediately available on it. - - {{{Lwt}}} contains a scheduler which is responsible for managing - multiple operations waiting for events, and restarting them when needed. - This scheduler is implemented by the two modules {{{Lwt_engine}}} - and {{{Lwt_main}}}. {{{Lwt_engine}}} is a low-level module, it - provides a signature for custom I/O multiplexers as well as two built-in - implementations, {{{libev}}} and {{{select}}}. The signature is given by the - class {{{Lwt_engine.t}}}. - - {{{libev}}} is used by default on Linux, because it supports any - number of file descriptors, while {{{select}}} supports only 1024. {{{libev}}} - is also much more efficient. On Windows, {{{Unix.select}}} is used because - {{{libev}}} does not work properly. The user may change the backend in use at - any time. - - If you see an {{{Invalid_argument}}} error on {{{Unix.select}}}, it - may be because the 1024 file descriptor limit was exceeded. Try - switching to {{{libev}}}, if possible. - - The engine can also be used directly in order to integrate other - libraries with {{{Lwt}}}. For example, {{{GTK}}} needs to be notified - when some events are received. If you use {{{Lwt}}} with {{{GTK}}} - you need to use the {{{Lwt}}} scheduler to monitor {{{GTK}}} - sources. This is what is done by the {{{Lwt_glib}}} library. - - The {{{Lwt_main}}} module contains the //main loop// of - {{{Lwt}}}. It is run by calling the function {{{Lwt_main.run}}}: - -< 'a ->> - - This function continuously runs the scheduler until the promise passed - as argument is resolved. - - To make sure {{{Lwt}}} is compiled with {{{libev}}} support, - tell opam that the library is available on the system by installing the - [[http://opam.ocaml.org/packages/conf-libev/conf-libev.4-11/|conf-libev]] - package. You may get the actual library with your system package manager: - - * {{{brew install libev}}} on MacOSX, - * {{{apt-get install libev-dev}}} on Debian/Ubuntu, or - * {{{yum install libev-devel}}} on CentOS, which requires to set - {{{export C_INCLUDE_PATH=/usr/include/libev/}}} and - {{{export LIBRARY_PATH=/usr/lib64/}}} before calling - {{{opam install conf-libev}}}. - - -=== Logging === - - For logging, we recommend the {{{logs}}} package from opam, which includes an - Lwt-aware module {{{Logs_lwt}}}. - -== The Lwt.react library == - - The {{{Lwt_react}}} module provides helpers for using the {{{react}}} - library with {{{Lwt}}}. It extends the {{{React}}} module by adding - {{{Lwt}}}-specific functions. It can be used as a replacement of - {{{React}}}. For example you can add at the beginning of your - program: - -<> - - instead of: - -<> - - or: - -<> - - Among the added functionalities we have {{{Lwt_react.E.next}}}, which - takes an event and returns a promise which will be pending until the next - occurrence of this event. For example: - -< -val push : '_a -> unit = -# let p = E.next event;; -val p : '_a Lwt.t = -# Lwt.state p;; -- : '_a Lwt.state = Lwt.Sleep -# push 42;; -- : unit = () -# Lwt.state p;; -- : int Lwt.state = Lwt.Return 42 ->> - - Another interesting feature is the ability to limit events - (resp. signals) from occurring (resp. changing) too often. For example, - suppose you are doing a program which displays something on the screen - each time a signal changes. If at some point the signal changes 1000 - times per second, you probably don't want to render it 1000 times per - second. For that you use {{{Lwt_react.S.limit}}}: - -< unit Lwt.t) -> 'a React.signal -> 'a React.signal ->> - - {{{Lwt_react.S.limit f signal}}} returns a signal which varies as - {{{signal}}} except that two consecutive updates are separated by a - call to {{{f}}}. For example if {{{f}}} returns a promise which is pending - for 0.1 seconds, then there will be no more than 10 changes per - second: - -< Lwt_unix.sleep 0.1) signal in - - (* Redraw the screen each time the limited signal change: *) - S.notify_p draw signal' ->> - -== Other libraries == - -=== Detaching computation to preemptive threads === - - It may happen that you want to run a function which will take time to - compute or that you want to use a blocking function that cannot be - used in a non-blocking way. For these situations, {{{Lwt}}} allows you to - //detach// the computation to a preemptive thread. - - This is done by the module {{{Lwt_preemptive}}} of the - {{{lwt.unix}}} package which maintains a pool of system - threads. The main function is: - -< 'b) -> 'a -> 'b Lwt.t ->> - - {{{detach f x}}} will execute {{{f x}}} in another thread and - return a pending promise, usable from the main thread, which will be fulfilled - with the result of the preemptive thread. - - If you want to trigger some {{{Lwt}}} operations from your detached thread, - you have to call back into the main thread using - {{{Lwt_preemptive.run_in_main}}}: - -< 'a Lwt.t) -> 'a ->> - - This is roughly the equivalent of {{{Lwt.main_run}}}, but for detached - threads, rather than for the whole process. Note that you must not call - {{{Lwt_main.run}}} in a detached thread. - -=== SSL support === - - The library {{{Lwt_ssl}}} allows use of SSL asynchronously. - -== Writing stubs using {{{Lwt}}} == - -=== Thread-safe notifications === - - If you want to notify the main thread from another thread, you can use the {{{Lwt}}} - thread safe notification system. First you need to create a notification identifier - (which is just an integer) from the OCaml side using the - {{{Lwt_unix.make_notification}}} function, then you can send it from either the - OCaml code with {{{Lwt_unix.send_notification}}} function, or from the C code using - the function {{{lwt_unix_send_notification}}} (defined in {{{lwt_unix_.h}}}). - - Notifications are received and processed asynchronously by the main thread. - -=== Jobs === - - For operations that cannot be executed asynchronously, {{{Lwt}}} - uses a system of jobs that can be executed in a different threads. A - job is composed of three functions: - - * A stub function to create the job. It must allocate a new job - structure and fill its [worker] and [result] fields. This - function is executed in the main thread. - The return type for the OCaml external must be of the form {{{'a job}}}. - * A function which executes the job. This one may be executed asynchronously - in another thread. This function must not: - ** access or allocate OCaml block values (tuples, strings, ...), - ** call OCaml code. - * A function which reads the result of the job, frees resources and - returns the result as an OCaml value. This function is executed in - the main thread. - - With {{{Lwt < 2.3.3}}}, 4 functions (including 3 stubs) were - required. It is still possible to use this mode but it is - deprecated. - - We show as example the implementation of {{{Lwt_unix.mkdir}}}. On the C - side we have: - -<result = mkdir(job->path, job->mode); - /* Save the value of errno. */ - job->errno_copy = errno; -} - -/* The function building the caml result. */ -static value result_mkdir(struct job_mkdir* job) -{ - /* Check for errors. */ - if (job->result < 0) { - /* Save the value of errno so we can use it - once the job has been freed. */ - int error = job->errno_copy; - /* Copy the contents of job->path into a caml string. */ - value string_argument = caml_copy_string(job->path); - /* Free the job structure. */ - lwt_unix_free_job(&job->job); - /* Raise the error. */ - unix_error(error, "mkdir", string_argument); - } - /* Free the job structure. */ - lwt_unix_free_job(&job->job); - /* Return the result. */ - return Val_unit; -} - -/* The stub creating the job structure. */ -CAMLprim value lwt_unix_mkdir_job(value path, value mode) -{ - /* Get the length of the path parameter. */ - mlsize_t len_path = caml_string_length(path) + 1; - /* Allocate a new job. */ - struct job_mkdir* job = - (struct job_mkdir*)lwt_unix_new_plus(struct job_mkdir, len_path); - /* Set the offset of the path parameter inside the job structure. */ - job->path = job->data; - /* Copy the path parameter inside the job structure. */ - memcpy(job->path, String_val(path), len_path); - /* Initialize function fields. */ - job->job.worker = (lwt_unix_job_worker)worker_mkdir; - job->job.result = (lwt_unix_job_result)result_mkdir; - /* Copy the mode parameter. */ - job->mode = Int_val(mode); - /* Wrap the structure into a caml value. */ - return lwt_unix_alloc_job(&job->job); -} ->> - - and on the ocaml side: - -< int -> unit job = "lwt_unix_mkdir_job" - -(* The ocaml function. *) -let mkdir name perms = Lwt_unix.run_job (mkdir_job name perms) ->> diff --git a/src/core/index.mld b/src/core/index.mld index 238f380f1..603f25925 100644 --- a/src/core/index.mld +++ b/src/core/index.mld @@ -79,7 +79,7 @@ In Lwt, {1 Additional Docs} -- {{:http://ocsigen.org/lwt/} Online manual}. +- {{!page-manual} Manual} ({{:http://ocsigen.org/lwt/} Online manual}). - {{:https://github.com/dkim/rwo-lwt#readme} Concurrent Programming with Lwt} is a nice source of Lwt examples. They are translations of code from Real World OCaml, but are just as useful if you are not reading the book. From c26b007f271af89457f8889606d7b3f05ab49dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Tue, 21 Jun 2022 10:14:48 +0200 Subject: [PATCH 35/62] Update the manual --- docs/manual.mld | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/manual.mld b/docs/manual.mld index b6f1f319a..99a40aab6 100644 --- a/docs/manual.mld +++ b/docs/manual.mld @@ -61,10 +61,10 @@ {2 Lwt concepts } - Let's take a classic function of the [Pervasives] module: + Let's take a classic function of the [Stdlib] module: {[ -# Pervasives.input_char;; +# Stdlib.input_char;; - : in_channel -> char = ]} From a0c3020d660ed9bb2115ae823587805541d16afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Wed, 15 Feb 2023 16:17:40 +0100 Subject: [PATCH 36/62] Use odoc tables --- docs/manual.mld | 84 +++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 48 deletions(-) diff --git a/docs/manual.mld b/docs/manual.mld index 99a40aab6..946867ce7 100644 --- a/docs/manual.mld +++ b/docs/manual.mld @@ -381,74 +381,62 @@ $ ocamlfind ocamlc -package lwt_ppx -linkpkg -o foo foo.ml {3 Correspondence table } -{%html: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Without LwtWith Lwt
let pattern_1 = expr_1
+{table
+  {tr
+    {th Without Lwt}
+    {th With Lwt}}
+  {tr
+    {td {[let pattern_1 = expr_1
 and pattern_2 = expr2
 …
 and pattern_n = expr_n in
-expr
let%lwt pattern_1 = expr_1
+expr]}}
+    {td {[let%lwt pattern_1 = expr_1
 and pattern_2 = expr2
 …
 and pattern_n = expr_n in
-expr
try expr with
+expr]}}}
+  {tr
+    {td {[try expr with
 | pattern_1 = expr_1
 | pattern_2 = expr2
 …
-| pattern_n = expr_n
try%lwt expr with
+| pattern_n = expr_n]}}
+    {td {[try%lwt expr with
 | pattern_1 = expr_1
 | pattern_2 = expr2
 …
-| pattern_n = expr_n
match expr with
+| pattern_n = expr_n]}}}
+  {tr
+    {td {[match expr with
 | pattern_1 = expr_1
 | pattern_2 = expr2
 …
-| pattern_n = expr_n
match%lwt expr with
+| pattern_n = expr_n]}}
+    {td {[match%lwt expr with
 | pattern_1 = expr_1
 | pattern_2 = expr2
 …
-| pattern_n = expr_n
for ident = expr_init to expr_final do
+| pattern_n = expr_n]}}}
+  {tr
+    {td {[for ident = expr_init to expr_final do
   expr
-done
for%lwt ident = expr_init to expr_final do
+done]}}
+    {td {[for%lwt ident = expr_init to expr_final do
   expr
-done
while expr do expr done
while%lwt expr do expr done
if expr then expr else expr
if%lwt expr then expr else expr
assert expr
assert%lwt expr
raise exn
[%lwt raise exn]
-%} +done]}}} + {tr + {td {[while expr do expr done]}} + {td {[while%lwt expr do expr done]}}} + {tr + {td {[if expr then expr else expr]}} + {td {[if%lwt expr then expr else expr]}}} + {tr + {td {[assert expr]}} + {td {[assert%lwt expr]}}} + {tr + {td {[raise exn]}} + {td {[[%lwt raise exn]]}}}} {2 Backtrace support } From b890e7dca03b5c7eecddabb5c04b1e7058394296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Mon, 17 Jul 2023 14:24:54 +0200 Subject: [PATCH 37/62] Add dependency to odoc --- dune-project | 1 + lwt.opam | 1 + 2 files changed, 2 insertions(+) diff --git a/dune-project b/dune-project index fcbe532a7..782590fc2 100644 --- a/dune-project +++ b/dune-project @@ -54,6 +54,7 @@ synchronization primitives. Code can be run in parallel on an opt-in basis. (ocaml (>= 4.08)) (cppo (and :build (>= 1.1.0))) (ocamlfind (and :dev (>= 1.7.3-1))) + (odoc (and :with-doc (>= 2.3.0))) dune-configurator ocplib-endian) (depopts base-threads base-unix conf-libev)) diff --git a/lwt.opam b/lwt.opam index 75bcc6274..2f0979c47 100644 --- a/lwt.opam +++ b/lwt.opam @@ -24,6 +24,7 @@ depends: [ "ocaml" {>= "4.08"} "cppo" {build & >= "1.1.0"} "ocamlfind" {dev & >= "1.7.3-1"} + "odoc" {with-doc & >= "2.3.0"} "dune-configurator" "ocplib-endian" ] From bb05501f0aa72705451141071119b2dc887eaead Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Mon, 15 Apr 2024 17:36:05 +0900 Subject: [PATCH 38/62] Replace the deprecated opam depext subcommand with the install command Signed-off-by: Sora Morimoto --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 423bd0f5b..67982c28d 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -90,7 +90,7 @@ jobs: opam-local-packages: ${{ matrix.local-packages }} allow-prerelease-opam: true - - run: opam depext conf-libev --install + - run: opam install conf-libev if: ${{ matrix.libev == true }} - run: opam install lwt_react lwt --deps-only --with-test From 9cb71a7ded6fe0de35ccdb9a4f4300655ea16f52 Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Mon, 15 Apr 2024 17:50:58 +0900 Subject: [PATCH 39/62] Bump OCaml to 5.1 for a matrix with flambda activated Signed-off-by: Sora Morimoto --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 423bd0f5b..a4983872e 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -34,7 +34,7 @@ jobs: *.opam include: - os: ubuntu-latest - ocaml-compiler: ocaml-variants.4.14.2+options,ocaml-option-flambda,ocaml-option-musl,ocaml-option-static + ocaml-compiler: ocaml-variants.5.1.1+options,ocaml-option-flambda,ocaml-option-musl,ocaml-option-static,ocaml-option-no-compression libev: false ppx: true local-packages: | From 41af0826c4787fe172aaa3254f3c0ac39333cc60 Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Wed, 24 Apr 2024 17:38:08 +0900 Subject: [PATCH 40/62] Enable lint-opam job in CI Signed-off-by: Sora Morimoto --- .github/workflows/workflow.yml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index c02b5662e..568b771c6 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -40,33 +40,33 @@ jobs: local-packages: | *.opam - os: macos-latest - ocaml-compiler: 4.14.x + ocaml-compiler: "4.14" libev: true ppx: true local-packages: | *.opam - os: windows-latest - ocaml-compiler: 4.14.x + ocaml-compiler: "4.14" libev: false ppx: true local-packages: | *.opam - os: ubuntu-latest - ocaml-compiler: 4.08.x + ocaml-compiler: "4.08" libev: true ppx: false local-packages: | *.opam !lwt_ppx.opam - os: macos-latest - ocaml-compiler: 4.08.x + ocaml-compiler: "4.08" libev: true ppx: false local-packages: | *.opam !lwt_ppx.opam - os: windows-latest - ocaml-compiler: 4.08.x + ocaml-compiler: "4.08" libev: false ppx: false local-packages: | @@ -111,18 +111,18 @@ jobs: - run: opam exec -- make ppx_let-test-deps ppx_let-test if: ${{ matrix.ppx == true }} - # lint-opam: - # runs-on: ubuntu-latest + lint-opam: + runs-on: ubuntu-latest - # steps: - # - name: Checkout tree - # uses: actions/checkout@v4 + steps: + - name: Checkout tree + uses: actions/checkout@v4 - # - name: Set-up OCaml - # uses: ocaml/setup-ocaml@v2 - # with: - # ocaml-compiler: "5.1" - # allow-prerelease-opam: true - # dune-cache: true + - name: Set-up OCaml + uses: ocaml/setup-ocaml@v2 + with: + ocaml-compiler: "5.1" + allow-prerelease-opam: true + dune-cache: true - # - uses: ocaml/setup-ocaml/lint-opam@v2 + - uses: ocaml/setup-ocaml/lint-opam@v2 From 9fa55a296a75b051fd5e6eb430bb73be30ccaf6d Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Mon, 29 Apr 2024 22:01:47 +0900 Subject: [PATCH 41/62] ci: bump OCaml version on macOS and Windows Signed-off-by: Sora Morimoto --- .github/workflows/workflow.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 568b771c6..a380be715 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -52,21 +52,21 @@ jobs: local-packages: | *.opam - os: ubuntu-latest - ocaml-compiler: "4.08" + ocaml-compiler: "4.14" libev: true ppx: false local-packages: | *.opam !lwt_ppx.opam - os: macos-latest - ocaml-compiler: "4.08" + ocaml-compiler: "4.14" libev: true ppx: false local-packages: | *.opam !lwt_ppx.opam - os: windows-latest - ocaml-compiler: "4.08" + ocaml-compiler: "4.14" libev: false ppx: false local-packages: | From fd1397b58023a19e2cb3d5267054613292dd6664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Fri, 2 Feb 2024 14:09:32 +0100 Subject: [PATCH 42/62] Constify C constructors and flags tables Now these tables will go in the readonly segment, where they belong. --- src/unix/lwt_unix_stubs.c | 7 ++++--- src/unix/unix_c/unix_access_job.c | 2 +- src/unix/unix_c/unix_getnameinfo_job.c | 4 ++-- src/unix/unix_c/unix_lockf_job.c | 4 ++-- src/unix/unix_c/unix_lseek_job.c | 2 +- src/unix/unix_c/unix_madvise.c | 2 +- src/unix/unix_c/unix_open_job.c | 2 +- src/unix/unix_c/unix_recv_send_utils.c | 2 +- src/unix/unix_c/unix_recv_send_utils.h | 2 +- src/unix/unix_c/unix_tcflow_job.c | 2 +- src/unix/unix_c/unix_tcflush_job.c | 2 +- src/unix/unix_c/unix_tcsetattr_job.c | 2 +- src/unix/unix_c/unix_termios_conversion.c | 10 ++++------ src/unix/unix_c/unix_wait4.c | 2 +- 14 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/unix/lwt_unix_stubs.c b/src/unix/lwt_unix_stubs.c index a8eedde87..443773bac 100644 --- a/src/unix/lwt_unix_stubs.c +++ b/src/unix/lwt_unix_stubs.c @@ -465,10 +465,11 @@ static void lwt_unix_socketpair(int domain, int type, int protocol, uerror("socketpair", Nothing); } -static int socket_domain_table[] = {PF_UNIX, PF_INET, PF_INET6}; +static const int socket_domain_table[] = + {PF_UNIX, PF_INET, PF_INET6}; -static int socket_type_table[] = {SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, - SOCK_SEQPACKET}; +static const int socket_type_table[] = + {SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET}; CAMLprim value lwt_unix_socketpair_stub(value cloexec, value domain, value type, value protocol) { diff --git a/src/unix/unix_c/unix_access_job.c b/src/unix/unix_c/unix_access_job.c index a054fc34c..0a607686e 100644 --- a/src/unix/unix_c/unix_access_job.c +++ b/src/unix/unix_c/unix_access_job.c @@ -34,7 +34,7 @@ +-----------------------------------------------------------------+ */ /* Table mapping constructors of ocaml type Unix.access_permission to C values. */ -static int access_permission_table[] = { +static const int access_permission_table[] = { /* Constructor R_OK. */ R_OK, /* Constructor W_OK. */ diff --git a/src/unix/unix_c/unix_getnameinfo_job.c b/src/unix/unix_c/unix_getnameinfo_job.c index 3ae74091d..8f5e3bbdb 100644 --- a/src/unix/unix_c/unix_getnameinfo_job.c +++ b/src/unix/unix_c/unix_getnameinfo_job.c @@ -28,8 +28,8 @@ struct job_getnameinfo { int result; }; -static int getnameinfo_flag_table[] = {NI_NOFQDN, NI_NUMERICHOST, NI_NAMEREQD, - NI_NUMERICSERV, NI_DGRAM}; +static const int getnameinfo_flag_table[] = + {NI_NOFQDN, NI_NUMERICHOST, NI_NAMEREQD, NI_NUMERICSERV, NI_DGRAM}; static void worker_getnameinfo(struct job_getnameinfo *job) { diff --git a/src/unix/unix_c/unix_lockf_job.c b/src/unix/unix_c/unix_lockf_job.c index a0449d80f..67732f8b0 100644 --- a/src/unix/unix_c/unix_lockf_job.c +++ b/src/unix/unix_c/unix_lockf_job.c @@ -84,8 +84,8 @@ static void worker_lockf(struct job_lockf *job) #else -static int lock_command_table[] = {F_ULOCK, F_LOCK, F_TLOCK, - F_TEST, F_LOCK, F_TLOCK}; +static const int lock_command_table[] = + {F_ULOCK, F_LOCK, F_TLOCK, F_TEST, F_LOCK, F_TLOCK}; static void worker_lockf(struct job_lockf *job) { diff --git a/src/unix/unix_c/unix_lseek_job.c b/src/unix/unix_c/unix_lseek_job.c index fe2740493..291611a84 100644 --- a/src/unix/unix_c/unix_lseek_job.c +++ b/src/unix/unix_c/unix_lseek_job.c @@ -35,7 +35,7 @@ +-----------------------------------------------------------------+ */ /* Table mapping constructors of ocaml type Unix.seek_command to C values. */ -static int seek_command_table[] = { +static const int seek_command_table[] = { /* Constructor SEEK_SET. */ SEEK_SET, /* Constructor SEEK_CUR. */ diff --git a/src/unix/unix_c/unix_madvise.c b/src/unix/unix_c/unix_madvise.c index f619a4441..51b1d3ce9 100644 --- a/src/unix/unix_c/unix_madvise.c +++ b/src/unix/unix_c/unix_madvise.c @@ -12,7 +12,7 @@ #include #include -static int advise_table[] = { +static const int advise_table[] = { MADV_NORMAL, MADV_RANDOM, MADV_SEQUENTIAL, MADV_WILLNEED, MADV_DONTNEED, #if defined(MADV_MERGEABLE) MADV_MERGEABLE, diff --git a/src/unix/unix_c/unix_open_job.c b/src/unix/unix_c/unix_open_job.c index a77ad1cab..0c1be8459 100644 --- a/src/unix/unix_c/unix_open_job.c +++ b/src/unix/unix_c/unix_open_job.c @@ -35,7 +35,7 @@ #define caml_unix_cloexec_default unix_cloexec_default #endif -static int open_flag_table[] = { +static const int open_flag_table[] = { O_RDONLY, O_WRONLY, O_RDWR, O_NONBLOCK, O_APPEND, O_CREAT, O_TRUNC, O_EXCL, O_NOCTTY, O_DSYNC, O_SYNC, O_RSYNC, 0, /* O_SHARE_DELETE, Windows-only */ diff --git a/src/unix/unix_c/unix_recv_send_utils.c b/src/unix/unix_c/unix_recv_send_utils.c index f9486afe9..bbb3d9c1f 100644 --- a/src/unix/unix_c/unix_recv_send_utils.c +++ b/src/unix/unix_c/unix_recv_send_utils.c @@ -19,7 +19,7 @@ #include "unix_recv_send_utils.h" -int msg_flag_table[3] = {MSG_OOB, MSG_DONTROUTE, MSG_PEEK}; +const int msg_flag_table[3] = {MSG_OOB, MSG_DONTROUTE, MSG_PEEK}; value wrapper_recv_msg(int fd, int n_iovs, struct iovec *iovs) { diff --git a/src/unix/unix_c/unix_recv_send_utils.h b/src/unix/unix_c/unix_recv_send_utils.h index f4b9bd76d..97cea4fcc 100644 --- a/src/unix/unix_c/unix_recv_send_utils.h +++ b/src/unix/unix_c/unix_recv_send_utils.h @@ -36,7 +36,7 @@ #define caml_unix_socket_type_table socket_type_table #endif -extern int msg_flag_table[]; +extern const int msg_flag_table[]; extern int caml_unix_socket_domain_table[]; extern int caml_unix_socket_type_table[]; extern void get_sockaddr(value mladdr, union sock_addr_union *addr /*out*/, diff --git a/src/unix/unix_c/unix_tcflow_job.c b/src/unix/unix_c/unix_tcflow_job.c index 764fe3c57..24042b80c 100644 --- a/src/unix/unix_c/unix_tcflow_job.c +++ b/src/unix/unix_c/unix_tcflow_job.c @@ -35,7 +35,7 @@ +-----------------------------------------------------------------+ */ /* Table mapping constructors of ocaml type Unix.flow_action to C values. */ -static int flow_action_table[] = { +static const int flow_action_table[] = { /* Constructor TCOOFF. */ TCOOFF, /* Constructor TCOON. */ diff --git a/src/unix/unix_c/unix_tcflush_job.c b/src/unix/unix_c/unix_tcflush_job.c index 9287ca8e8..d76bf1ee1 100644 --- a/src/unix/unix_c/unix_tcflush_job.c +++ b/src/unix/unix_c/unix_tcflush_job.c @@ -35,7 +35,7 @@ +-----------------------------------------------------------------+ */ /* Table mapping constructors of ocaml type Unix.flush_queue to C values. */ -static int flush_queue_table[] = { +static const int flush_queue_table[] = { /* Constructor TCIFLUSH. */ TCIFLUSH, /* Constructor TCOFLUSH. */ diff --git a/src/unix/unix_c/unix_tcsetattr_job.c b/src/unix/unix_c/unix_tcsetattr_job.c index b699b5069..9d3029db2 100644 --- a/src/unix/unix_c/unix_tcsetattr_job.c +++ b/src/unix/unix_c/unix_tcsetattr_job.c @@ -24,7 +24,7 @@ struct job_tcsetattr { int error_code; }; -static int when_flag_table[] = {TCSANOW, TCSADRAIN, TCSAFLUSH}; +static const int when_flag_table[] = {TCSANOW, TCSADRAIN, TCSAFLUSH}; static void worker_tcsetattr(struct job_tcsetattr *job) { diff --git a/src/unix/unix_c/unix_termios_conversion.c b/src/unix/unix_c/unix_termios_conversion.c index e9b82f8e6..d47eb6857 100644 --- a/src/unix/unix_c/unix_termios_conversion.c +++ b/src/unix/unix_c/unix_termios_conversion.c @@ -20,7 +20,7 @@ enum { Iflags, Oflags, Cflags, Lflags }; /* Structure of the terminal_io record. Cf. unix.mli */ -static long terminal_io_descr[] = { +static const long terminal_io_descr[] = { /* Input modes */ Bool, Iflags, IGNBRK, Bool, Iflags, BRKINT, Bool, Iflags, IGNPAR, Bool, Iflags, PARMRK, Bool, Iflags, INPCK, Bool, Iflags, ISTRIP, Bool, Iflags, @@ -40,7 +40,7 @@ static long terminal_io_descr[] = { Char, VINTR, Char, VQUIT, Char, VERASE, Char, VKILL, Char, VEOF, Char, VEOL, Char, VMIN, Char, VTIME, Char, VSTART, Char, VSTOP, End}; -static struct { +static const struct { speed_t speed; int baud; } speedtable[] = {{B50, 50}, @@ -152,10 +152,9 @@ static tcflag_t *choose_field(struct termios *terminal_status, long field) void encode_terminal_status(struct termios *terminal_status, volatile value *dst) { - long *pc; int i; - for (pc = terminal_io_descr; *pc != End; dst++) { + for (const long *pc = terminal_io_descr; *pc != End; dst++) { switch (*pc++) { case Bool: { tcflag_t *src = choose_field(terminal_status, *pc++); @@ -209,10 +208,9 @@ void encode_terminal_status(struct termios *terminal_status, volatile value *dst int decode_terminal_status(struct termios *terminal_status, volatile value *src) { - long *pc; int i; - for (pc = terminal_io_descr; *pc != End; src++) { + for (const long *pc = terminal_io_descr; *pc != End; src++) { switch (*pc++) { case Bool: { tcflag_t *dst = choose_field(terminal_status, *pc++); diff --git a/src/unix/unix_c/unix_wait4.c b/src/unix/unix_c/unix_wait4.c index daa9ab4f4..eae1aa6ad 100644 --- a/src/unix/unix_c/unix_wait4.c +++ b/src/unix/unix_c/unix_wait4.c @@ -58,7 +58,7 @@ static value alloc_process_status(int status) return st; } -static int wait_flag_table[] = {WNOHANG, WUNTRACED}; +static const int wait_flag_table[] = {WNOHANG, WUNTRACED}; value lwt_unix_wait4(value flags, value pid_req) { From 7485bbcd8d19314acce79418adde12450a1db1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Fri, 2 Feb 2024 14:09:48 +0100 Subject: [PATCH 43/62] Reduce scope of local variable --- src/unix/unix_c/unix_termios_conversion.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/unix/unix_c/unix_termios_conversion.c b/src/unix/unix_c/unix_termios_conversion.c index d47eb6857..cee17025f 100644 --- a/src/unix/unix_c/unix_termios_conversion.c +++ b/src/unix/unix_c/unix_termios_conversion.c @@ -152,8 +152,6 @@ static tcflag_t *choose_field(struct termios *terminal_status, long field) void encode_terminal_status(struct termios *terminal_status, volatile value *dst) { - int i; - for (const long *pc = terminal_io_descr; *pc != End; dst++) { switch (*pc++) { case Bool: { @@ -167,7 +165,7 @@ void encode_terminal_status(struct termios *terminal_status, volatile value *dst int ofs = *pc++; int num = *pc++; tcflag_t msk = *pc++; - for (i = 0; i < num; i++) { + for (int i = 0; i < num; i++) { if ((*src & msk) == pc[i]) { *dst = Val_int(i + ofs); break; @@ -189,7 +187,7 @@ void encode_terminal_status(struct termios *terminal_status, volatile value *dst speed = cfgetispeed(terminal_status); break; } - for (i = 0; i < NSPEEDS; i++) { + for (int i = 0; i < NSPEEDS; i++) { if (speed == speedtable[i].speed) { *dst = Val_int(speedtable[i].baud); break; @@ -208,8 +206,6 @@ void encode_terminal_status(struct termios *terminal_status, volatile value *dst int decode_terminal_status(struct termios *terminal_status, volatile value *src) { - int i; - for (const long *pc = terminal_io_descr; *pc != End; src++) { switch (*pc++) { case Bool: { @@ -226,7 +222,7 @@ int decode_terminal_status(struct termios *terminal_status, volatile value *src) int ofs = *pc++; int num = *pc++; tcflag_t msk = *pc++; - i = Int_val(*src) - ofs; + int i = Int_val(*src) - ofs; if (i >= 0 && i < num) { *dst = (*dst & ~msk) | pc[i]; } else { @@ -240,7 +236,7 @@ int decode_terminal_status(struct termios *terminal_status, volatile value *src) int which = *pc++; int baud = Int_val(*src); int res = 0; - for (i = 0; i < NSPEEDS; i++) { + for (int i = 0; i < NSPEEDS; i++) { if (baud == speedtable[i].baud) { switch (which) { case Output: From 5978227cc5baa3327cb365cc5e0d0cec77bc1be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Fri, 2 Feb 2024 14:22:45 +0100 Subject: [PATCH 44/62] Constify function parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Antonin Décimo --- src/unix/unix_c/unix_getaddrinfo_job.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unix/unix_c/unix_getaddrinfo_job.c b/src/unix/unix_c/unix_getaddrinfo_job.c index 395335d15..d7b1f8d0d 100644 --- a/src/unix/unix_c/unix_getaddrinfo_job.c +++ b/src/unix/unix_c/unix_getaddrinfo_job.c @@ -29,7 +29,7 @@ struct job_getaddrinfo { char data[]; }; -static value cst_to_constr(int n, int *tbl, int size, int deflt) +static value cst_to_constr(int n, const int *tbl, int size, int deflt) { int i; for (i = 0; i < size; i++) @@ -37,7 +37,7 @@ static value cst_to_constr(int n, int *tbl, int size, int deflt) return Val_int(deflt); } -static value convert_addrinfo(struct addrinfo *a) +static value convert_addrinfo(const struct addrinfo *a) { CAMLparam0(); CAMLlocal3(vres, vaddr, vcanonname); From 98607c46c3ab253d80ef715f3028bec0ea3c574c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Fri, 2 Feb 2024 14:23:42 +0100 Subject: [PATCH 45/62] Constify caml_unix_socket_{domain,type}_table in OCaml 5.03 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Antonin Décimo --- src/unix/unix_c/unix_recv_send_utils.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/unix/unix_c/unix_recv_send_utils.h b/src/unix/unix_c/unix_recv_send_utils.h index 97cea4fcc..407091f6e 100644 --- a/src/unix/unix_c/unix_recv_send_utils.h +++ b/src/unix/unix_c/unix_recv_send_utils.h @@ -36,9 +36,15 @@ #define caml_unix_socket_type_table socket_type_table #endif -extern const int msg_flag_table[]; +#if OCAML_VERSION < 50300 extern int caml_unix_socket_domain_table[]; extern int caml_unix_socket_type_table[]; +#else +extern const int caml_unix_socket_domain_table[]; +extern const int caml_unix_socket_type_table[]; +#endif + +extern const int msg_flag_table[]; extern void get_sockaddr(value mladdr, union sock_addr_union *addr /*out*/, socklen_t *addr_len /*out*/); value wrapper_recv_msg(int fd, int n_iovs, struct iovec *iovs); From f1bce0115bf41219e994d2f4fb0250c973682a0d Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Mon, 29 Apr 2024 22:24:52 +0900 Subject: [PATCH 46/62] Remove useless ppx_let-test-deps from Makefile Signed-off-by: Sora Morimoto --- .github/workflows/workflow.yml | 4 ++-- Makefile | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index a380be715..ea67163e8 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -95,7 +95,7 @@ jobs: - run: opam install lwt_react lwt --deps-only --with-test - - run: opam install lwt_ppx --deps-only --with-test + - run: opam install lwt_ppx lwt_ppx_let --deps-only --with-test if: ${{ matrix.ppx == true }} - run: opam exec -- dune build --only-packages lwt_react,lwt @@ -108,7 +108,7 @@ jobs: - run: opam exec -- dune runtest --only-packages lwt_ppx if: ${{ matrix.ppx == true }} - - run: opam exec -- make ppx_let-test-deps ppx_let-test + - run: opam exec -- make ppx_let-test if: ${{ matrix.ppx == true }} lint-opam: diff --git a/Makefile b/Makefile index 594b7606f..29ef2277e 100644 --- a/Makefile +++ b/Makefile @@ -76,10 +76,6 @@ ppx_let-test : dune build test/ppx_let/test.exe dune exec test/ppx_let/test.exe -.PHONY : ppx_let-test-deps -ppx_let-test-deps : - opam install --yes --unset-root ppx_let - .PHONY: clean clean : dune clean From 4ad6bbbd467e479d6ff23378b308ab108f73dc8e Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Tue, 30 Apr 2024 18:48:37 +0900 Subject: [PATCH 47/62] Update GitHub Actions with Dependabot Signed-off-by: Sora Morimoto --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..ca79ca5b4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly From bf0cb79efd5dfffc198028137ab83501193b864d Mon Sep 17 00:00:00 2001 From: Samer Abdallah Date: Wed, 8 May 2024 16:48:16 +0100 Subject: [PATCH 48/62] Replace of_list with lazier version, and to_list to tail recursive version --- src/core/lwt_seq.ml | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/core/lwt_seq.ml b/src/core/lwt_seq.ml index a263ad0f0..fe40eadfb 100644 --- a/src/core/lwt_seq.ml +++ b/src/core/lwt_seq.ml @@ -283,22 +283,16 @@ let unfold_lwt f u () = | None -> return_nil | Some (x, u') -> Lwt.return (Cons (x, unfold_lwt f u')) -let rec of_list = function - | [] -> empty - | h :: t -> cons h (of_list t) - -let rec to_list seq = - seq () >>= function - | Nil -> Lwt.return_nil - | Cons (x, next) -> - let+ l = to_list next in - x :: l -let to_list seq = - Lwt.apply seq () >>= function - | Nil -> Lwt.return_nil - | Cons (x, next) -> - let+ l = to_list next in - x :: l +let rec of_list l () = + Lwt.return (match l with [] -> Nil | h :: t -> Cons (h, of_list t)) + +let to_list (seq : 'a t) = + let rec aux f seq = + Lwt.bind (seq ()) (function + | Nil -> Lwt.return (f []) + | Cons (h, t) -> aux (fun x -> f (h :: x)) t) + in + aux (fun x -> x) (Lwt.apply seq) let rec of_seq seq () = match seq () with From d25b1df8ca4ee661795fd066313b76c6dd10b5ea Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Fri, 17 May 2024 14:03:45 +0900 Subject: [PATCH 49/62] Add OCaml 5.2 to CI Signed-off-by: Sora Morimoto --- .github/workflows/workflow.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index ea67163e8..ca13f70fb 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -24,6 +24,7 @@ jobs: - "4.14" - "5.0" - "5.1" + - "5.2" libev: - true - false @@ -34,7 +35,7 @@ jobs: *.opam include: - os: ubuntu-latest - ocaml-compiler: ocaml-variants.5.1.1+options,ocaml-option-flambda,ocaml-option-musl,ocaml-option-static,ocaml-option-no-compression + ocaml-compiler: ocaml-variants.5.2.0+options,ocaml-option-flambda,ocaml-option-musl,ocaml-option-static,ocaml-option-no-compression libev: false ppx: true local-packages: | @@ -121,7 +122,7 @@ jobs: - name: Set-up OCaml uses: ocaml/setup-ocaml@v2 with: - ocaml-compiler: "5.1" + ocaml-compiler: "5.2" allow-prerelease-opam: true dune-cache: true From d51f93ecbe832d029879108a001175a2d0801d2b Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Tue, 11 Jun 2024 17:47:55 +0900 Subject: [PATCH 50/62] ocaml/setup-ocaml@v3.0.0-alpha Signed-off-by: Sora Morimoto --- .github/workflows/workflow.yml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index ca13f70fb..12c431e99 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -41,33 +41,33 @@ jobs: local-packages: | *.opam - os: macos-latest - ocaml-compiler: "4.14" + ocaml-compiler: "5.2" libev: true ppx: true local-packages: | *.opam - os: windows-latest - ocaml-compiler: "4.14" + ocaml-compiler: "5.2" libev: false ppx: true local-packages: | *.opam - os: ubuntu-latest - ocaml-compiler: "4.14" + ocaml-compiler: "5.2" libev: true ppx: false local-packages: | *.opam !lwt_ppx.opam - os: macos-latest - ocaml-compiler: "4.14" + ocaml-compiler: "5.2" libev: true ppx: false local-packages: | *.opam !lwt_ppx.opam - os: windows-latest - ocaml-compiler: "4.14" + ocaml-compiler: "5.2" libev: false ppx: false local-packages: | @@ -77,17 +77,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - - name: Hack Git CRLF for ocaml/setup-ocaml#529 issue - run: git config --global core.autocrlf input - - - name: Checkout code + - name: Checkout tree uses: actions/checkout@v4 - name: Set-up OCaml - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3.0.0-alpha with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test opam-local-packages: ${{ matrix.local-packages }} allow-prerelease-opam: true @@ -120,10 +116,10 @@ jobs: uses: actions/checkout@v4 - name: Set-up OCaml - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3.0.0-alpha with: ocaml-compiler: "5.2" allow-prerelease-opam: true dune-cache: true - - uses: ocaml/setup-ocaml/lint-opam@v2 + - uses: ocaml/setup-ocaml/lint-opam@v3.0.0-alpha From 0f89761d372fd00e6a87bed898dcc3c15fbec8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Mon, 13 May 2024 13:49:15 +0200 Subject: [PATCH 51/62] Fix discover old-style function declarations --- src/unix/config/discover.ml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/unix/config/discover.ml b/src/unix/config/discover.ml index 931efd593..da27a6459 100644 --- a/src/unix/config/discover.ml +++ b/src/unix/config/discover.ml @@ -165,7 +165,7 @@ sig val add_link_flags : string list -> unit end = struct - let c_flags = ref [] + let c_flags = ref ["-Wall"; "-fdiagnostics-color=always"] let link_flags = ref [] let extend c_flags' link_flags' = @@ -424,7 +424,7 @@ struct let code = {| #include - int main() + int main(void) { ev_default_loop(0); return 0; @@ -452,7 +452,7 @@ struct let code = {| #include - int main() + int main(void) { pthread_create(0, 0, 0, 0); return 0; @@ -494,7 +494,7 @@ struct compiles context {| #include - int main() + int main(void) { eventfd(0, 0); return 0; @@ -511,7 +511,7 @@ struct #include #include - int main() + int main(void) { struct msghdr msg; msg.msg_controllen = 0; @@ -531,7 +531,7 @@ struct #define _GNU_SOURCE #include - int main() + int main(void) { sched_getcpu(); return 0; @@ -549,7 +549,7 @@ struct #define _GNU_SOURCE #include - int main() + int main(void) { sched_getaffinity(0, 0, 0); return 0; @@ -562,7 +562,7 @@ struct #include #include - int main() + int main(void) { struct |} ^ struct_name ^ {| cred; socklen_t cred_len = sizeof(cred); @@ -612,7 +612,7 @@ struct #include #include - int main() + int main(void) { uid_t euid; gid_t egid; @@ -630,7 +630,7 @@ struct compiles context {| #include - int main() + int main(void) { int (*fdatasyncp)(int) = fdatasync; fdatasyncp(0); @@ -650,7 +650,7 @@ struct #include #include - int main() + int main(void) { int x; x = @@ -733,7 +733,7 @@ struct #define NON_R_GETHOSTBYNAME 1 #endif - int main() + int main(void) { #if defined(NON_R_GETHOSTBYNAME) || defined(NON_R_GETHOSTBYNAME) #error "not available" @@ -750,7 +750,7 @@ struct #include #include - int main() { + int main(void) { struct stat *buf; double a, m, c; a = (double)buf->st_a|} ^ projection ^ {|; @@ -790,7 +790,7 @@ struct #include #include - int main() + int main(void) { int (*mincore_ptr)(void*, size_t, unsigned char*) = mincore; return (int)(mincore_ptr == NULL); @@ -808,7 +808,7 @@ struct #include #include - int main() + int main(void) { accept4(0, NULL, 0, 0); return 0; From 87538122f2e80d1261f4366b8f6e9c320556d3a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Mon, 13 May 2024 13:49:15 +0200 Subject: [PATCH 52/62] Fix HAVE_BSD_MINCORE test BSD mincore last parameter is a char *. Linux mincore last parameter is an unsigned char *. The test had it reversed. https://man.freebsd.org/cgi/man.cgi?query=mincore https://docs.oracle.com/cd/E88353_01/html/E37841/mincore-2.html https://linux.die.net/man/2/mincore --- src/unix/config/discover.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/config/discover.ml b/src/unix/config/discover.ml index da27a6459..e7b81fcf6 100644 --- a/src/unix/config/discover.ml +++ b/src/unix/config/discover.ml @@ -792,7 +792,7 @@ struct int main(void) { - int (*mincore_ptr)(void*, size_t, unsigned char*) = mincore; + int (*mincore_ptr)(const void*, size_t, char*) = mincore; return (int)(mincore_ptr == NULL); } |} From c24634b4dab416780d9b8e3dfb6681a25f4a33ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Mon, 13 May 2024 13:49:15 +0200 Subject: [PATCH 53/62] Fix minor signed/unsigned comparison warning --- src/unix/unix_c/unix_termios_conversion.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/unix_c/unix_termios_conversion.c b/src/unix/unix_c/unix_termios_conversion.c index cee17025f..2cd5f2eef 100644 --- a/src/unix/unix_c/unix_termios_conversion.c +++ b/src/unix/unix_c/unix_termios_conversion.c @@ -132,7 +132,7 @@ static const struct { #endif }; -#define NSPEEDS (sizeof(speedtable) / sizeof(speedtable[0])) +#define NSPEEDS (int)(sizeof(speedtable) / sizeof(speedtable[0])) static tcflag_t *choose_field(struct termios *terminal_status, long field) { From b5a9587dedae02ee2dd51e8a0aa50ee8d227f19c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Thu, 20 Jun 2024 10:26:38 +0200 Subject: [PATCH 54/62] Change include style of lwt_unix.h --- src/unix/lwt_process_stubs.c | 2 +- src/unix/unix_c/unix_access_job.c | 2 +- src/unix/unix_c/unix_chdir_job.c | 2 +- src/unix/unix_c/unix_chmod_job.c | 2 +- src/unix/unix_c/unix_chown_job.c | 2 +- src/unix/unix_c/unix_chroot_job.c | 2 +- src/unix/unix_c/unix_close_job.c | 2 +- src/unix/unix_c/unix_fchmod_job.c | 2 +- src/unix/unix_c/unix_fchown_job.c | 2 +- src/unix/unix_c/unix_fdatasync_job.c | 2 +- src/unix/unix_c/unix_fsync_job.c | 2 +- src/unix/unix_c/unix_ftruncate_job.c | 2 +- src/unix/unix_c/unix_link_job.c | 2 +- src/unix/unix_c/unix_lseek_job.c | 2 +- src/unix/unix_c/unix_mkdir_job.c | 2 +- src/unix/unix_c/unix_mkfifo_job.c | 2 +- src/unix/unix_c/unix_rename_job.c | 2 +- src/unix/unix_c/unix_rmdir_job.c | 2 +- src/unix/unix_c/unix_symlink_job.c | 2 +- src/unix/unix_c/unix_tcdrain_job.c | 2 +- src/unix/unix_c/unix_tcflow_job.c | 2 +- src/unix/unix_c/unix_tcflush_job.c | 2 +- src/unix/unix_c/unix_tcsendbreak_job.c | 2 +- src/unix/unix_c/unix_truncate_job.c | 2 +- src/unix/unix_c/unix_unlink_job.c | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/unix/lwt_process_stubs.c b/src/unix/lwt_process_stubs.c index c57066452..629682bac 100644 --- a/src/unix/lwt_process_stubs.c +++ b/src/unix/lwt_process_stubs.c @@ -7,7 +7,7 @@ #if defined(LWT_ON_WINDOWS) -#include +#include "lwt_unix.h" #if OCAML_VERSION < 41300 #define CAML_INTERNALS diff --git a/src/unix/unix_c/unix_access_job.c b/src/unix/unix_c/unix_access_job.c index 0a607686e..e4f578fa0 100644 --- a/src/unix/unix_c/unix_access_job.c +++ b/src/unix/unix_c/unix_access_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_chdir_job.c b/src/unix/unix_c/unix_chdir_job.c index 87826c8bd..a4458d9cd 100644 --- a/src/unix/unix_c/unix_chdir_job.c +++ b/src/unix/unix_c/unix_chdir_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_chmod_job.c b/src/unix/unix_c/unix_chmod_job.c index 75dadf88b..e71dea4c5 100644 --- a/src/unix/unix_c/unix_chmod_job.c +++ b/src/unix/unix_c/unix_chmod_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_chown_job.c b/src/unix/unix_c/unix_chown_job.c index 4b484825c..dc33f06c6 100644 --- a/src/unix/unix_c/unix_chown_job.c +++ b/src/unix/unix_c/unix_chown_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_chroot_job.c b/src/unix/unix_c/unix_chroot_job.c index 3f836b1ac..bef5b7ce7 100644 --- a/src/unix/unix_c/unix_chroot_job.c +++ b/src/unix/unix_c/unix_chroot_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_close_job.c b/src/unix/unix_c/unix_close_job.c index 483a85e3e..a121ef989 100644 --- a/src/unix/unix_c/unix_close_job.c +++ b/src/unix/unix_c/unix_close_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_fchmod_job.c b/src/unix/unix_c/unix_fchmod_job.c index a4095a154..d6afc0107 100644 --- a/src/unix/unix_c/unix_fchmod_job.c +++ b/src/unix/unix_c/unix_fchmod_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_fchown_job.c b/src/unix/unix_c/unix_fchown_job.c index ecba81441..65ed1272c 100644 --- a/src/unix/unix_c/unix_fchown_job.c +++ b/src/unix/unix_c/unix_fchown_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_fdatasync_job.c b/src/unix/unix_c/unix_fdatasync_job.c index 40c1011a9..1fc9702b2 100644 --- a/src/unix/unix_c/unix_fdatasync_job.c +++ b/src/unix/unix_c/unix_fdatasync_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_fsync_job.c b/src/unix/unix_c/unix_fsync_job.c index 1f05e72de..8da0fda1e 100644 --- a/src/unix/unix_c/unix_fsync_job.c +++ b/src/unix/unix_c/unix_fsync_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_ftruncate_job.c b/src/unix/unix_c/unix_ftruncate_job.c index cf8cf00a4..e564ad4c8 100644 --- a/src/unix/unix_c/unix_ftruncate_job.c +++ b/src/unix/unix_c/unix_ftruncate_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_link_job.c b/src/unix/unix_c/unix_link_job.c index 80ad2d38d..0d0c5b0ce 100644 --- a/src/unix/unix_c/unix_link_job.c +++ b/src/unix/unix_c/unix_link_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_lseek_job.c b/src/unix/unix_c/unix_lseek_job.c index 291611a84..2239beefd 100644 --- a/src/unix/unix_c/unix_lseek_job.c +++ b/src/unix/unix_c/unix_lseek_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_mkdir_job.c b/src/unix/unix_c/unix_mkdir_job.c index 872432a83..85831f94f 100644 --- a/src/unix/unix_c/unix_mkdir_job.c +++ b/src/unix/unix_c/unix_mkdir_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_mkfifo_job.c b/src/unix/unix_c/unix_mkfifo_job.c index 98838c614..4f66fa5fd 100644 --- a/src/unix/unix_c/unix_mkfifo_job.c +++ b/src/unix/unix_c/unix_mkfifo_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_rename_job.c b/src/unix/unix_c/unix_rename_job.c index 35cd4f8ab..bdaf27fbd 100644 --- a/src/unix/unix_c/unix_rename_job.c +++ b/src/unix/unix_c/unix_rename_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_rmdir_job.c b/src/unix/unix_c/unix_rmdir_job.c index ad813a7ee..4ac6d9d52 100644 --- a/src/unix/unix_c/unix_rmdir_job.c +++ b/src/unix/unix_c/unix_rmdir_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_symlink_job.c b/src/unix/unix_c/unix_symlink_job.c index e66e0cec0..f2929dd7c 100644 --- a/src/unix/unix_c/unix_symlink_job.c +++ b/src/unix/unix_c/unix_symlink_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_tcdrain_job.c b/src/unix/unix_c/unix_tcdrain_job.c index fc2b41f99..fb6843d97 100644 --- a/src/unix/unix_c/unix_tcdrain_job.c +++ b/src/unix/unix_c/unix_tcdrain_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_tcflow_job.c b/src/unix/unix_c/unix_tcflow_job.c index 24042b80c..574916049 100644 --- a/src/unix/unix_c/unix_tcflow_job.c +++ b/src/unix/unix_c/unix_tcflow_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_tcflush_job.c b/src/unix/unix_c/unix_tcflush_job.c index d76bf1ee1..f8414b2f1 100644 --- a/src/unix/unix_c/unix_tcflush_job.c +++ b/src/unix/unix_c/unix_tcflush_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_tcsendbreak_job.c b/src/unix/unix_c/unix_tcsendbreak_job.c index 23fa74919..b1cb6930c 100644 --- a/src/unix/unix_c/unix_tcsendbreak_job.c +++ b/src/unix/unix_c/unix_tcsendbreak_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_truncate_job.c b/src/unix/unix_c/unix_truncate_job.c index ea88f1de2..c7911dbb1 100644 --- a/src/unix/unix_c/unix_truncate_job.c +++ b/src/unix/unix_c/unix_truncate_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include diff --git a/src/unix/unix_c/unix_unlink_job.c b/src/unix/unix_c/unix_unlink_job.c index 8dbdb8c76..5fb583d23 100644 --- a/src/unix/unix_c/unix_unlink_job.c +++ b/src/unix/unix_c/unix_unlink_job.c @@ -16,7 +16,7 @@ */ /* Caml headers. */ -#include +#include "lwt_unix.h" #include #include #include From cd4aaddfc90eb0db69f9d08bd1b6c3d9d5091f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Thu, 20 Jun 2024 10:35:10 +0200 Subject: [PATCH 55/62] Include lwt_config.h before and lwt_unix.h after caml headers This allows configuring caml headers first, and then overwriting declarations (for compatibility). --- src/unix/lwt_libev_stubs.c | 5 ++++- src/unix/unix_c/unix_access_job.c | 3 ++- src/unix/unix_c/unix_bytes_recv.c | 1 + src/unix/unix_c/unix_bytes_recvfrom.c | 1 + src/unix/unix_c/unix_bytes_send.c | 1 + src/unix/unix_c/unix_bytes_sendto.c | 1 + src/unix/unix_c/unix_chdir_job.c | 3 ++- src/unix/unix_c/unix_chmod_job.c | 3 ++- src/unix/unix_c/unix_chown_job.c | 3 ++- src/unix/unix_c/unix_chroot_job.c | 3 ++- src/unix/unix_c/unix_close_job.c | 3 ++- src/unix/unix_c/unix_fchmod_job.c | 3 ++- src/unix/unix_c/unix_fchown_job.c | 3 ++- src/unix/unix_c/unix_fdatasync_job.c | 3 ++- src/unix/unix_c/unix_fsync_job.c | 3 ++- src/unix/unix_c/unix_ftruncate_job.c | 3 ++- src/unix/unix_c/unix_link_job.c | 3 ++- src/unix/unix_c/unix_lseek_job.c | 3 ++- src/unix/unix_c/unix_mkdir_job.c | 3 ++- src/unix/unix_c/unix_mkfifo_job.c | 3 ++- src/unix/unix_c/unix_recv.c | 1 + src/unix/unix_c/unix_recvfrom.c | 1 + src/unix/unix_c/unix_rename_job.c | 3 ++- src/unix/unix_c/unix_rmdir_job.c | 3 ++- src/unix/unix_c/unix_send.c | 1 + src/unix/unix_c/unix_sendto.c | 1 + src/unix/unix_c/unix_symlink_job.c | 3 ++- src/unix/unix_c/unix_tcdrain_job.c | 3 ++- src/unix/unix_c/unix_tcflow_job.c | 3 ++- src/unix/unix_c/unix_tcflush_job.c | 3 ++- src/unix/unix_c/unix_tcsendbreak_job.c | 3 ++- src/unix/unix_c/unix_truncate_job.c | 3 ++- src/unix/unix_c/unix_unlink_job.c | 3 ++- src/unix/unix_c/unix_wait4.c | 1 + 34 files changed, 61 insertions(+), 25 deletions(-) diff --git a/src/unix/lwt_libev_stubs.c b/src/unix/lwt_libev_stubs.c index 70238fe1d..22af6d60f 100644 --- a/src/unix/lwt_libev_stubs.c +++ b/src/unix/lwt_libev_stubs.c @@ -6,7 +6,6 @@ /* Stubs for libev */ #include "lwt_config.h" -#include "lwt_unix.h" #if defined(HAVE_LIBEV) @@ -21,6 +20,8 @@ #include #include +#include "lwt_unix.h" + /* +-----------------------------------------------------------------+ | Backend types | +-----------------------------------------------------------------+ */ @@ -221,6 +222,8 @@ CAMLprim value lwt_libev_timer_stop(value loop, value val_watcher) { #else +#include "lwt_unix.h" + LWT_NOT_AVAILABLE1(libev_init) LWT_NOT_AVAILABLE1(libev_stop) LWT_NOT_AVAILABLE2(libev_loop) diff --git a/src/unix/unix_c/unix_access_job.c b/src/unix/unix_c/unix_access_job.c index e4f578fa0..2e7409356 100644 --- a/src/unix/unix_c/unix_access_job.c +++ b/src/unix/unix_c/unix_access_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_bytes_recv.c b/src/unix/unix_c/unix_bytes_recv.c index 9de910bee..fac0ebda8 100644 --- a/src/unix/unix_c/unix_bytes_recv.c +++ b/src/unix/unix_c/unix_bytes_recv.c @@ -14,6 +14,7 @@ #include #include +#include "lwt_unix.h" #include "unix_recv_send_utils.h" value lwt_unix_bytes_recv(value fd, value buf, value ofs, value len, diff --git a/src/unix/unix_c/unix_bytes_recvfrom.c b/src/unix/unix_c/unix_bytes_recvfrom.c index db91759f7..1ae1c65a1 100644 --- a/src/unix/unix_c/unix_bytes_recvfrom.c +++ b/src/unix/unix_c/unix_bytes_recvfrom.c @@ -16,6 +16,7 @@ #include #include +#include "lwt_unix.h" #include "unix_recv_send_utils.h" value lwt_unix_bytes_recvfrom(value fd, value buf, value ofs, value len, diff --git a/src/unix/unix_c/unix_bytes_send.c b/src/unix/unix_c/unix_bytes_send.c index 9cd5a4428..2bdd44a34 100644 --- a/src/unix/unix_c/unix_bytes_send.c +++ b/src/unix/unix_c/unix_bytes_send.c @@ -14,6 +14,7 @@ #include #include +#include "lwt_unix.h" #include "unix_recv_send_utils.h" value lwt_unix_bytes_send(value fd, value buf, value ofs, value len, diff --git a/src/unix/unix_c/unix_bytes_sendto.c b/src/unix/unix_c/unix_bytes_sendto.c index ea34097d4..e9610b895 100644 --- a/src/unix/unix_c/unix_bytes_sendto.c +++ b/src/unix/unix_c/unix_bytes_sendto.c @@ -15,6 +15,7 @@ #include #include +#include "lwt_unix.h" #include "unix_recv_send_utils.h" value lwt_unix_bytes_sendto(value fd, value buf, value ofs, value len, diff --git a/src/unix/unix_c/unix_chdir_job.c b/src/unix/unix_c/unix_chdir_job.c index a4458d9cd..7832e51a9 100644 --- a/src/unix/unix_c/unix_chdir_job.c +++ b/src/unix/unix_c/unix_chdir_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_chmod_job.c b/src/unix/unix_c/unix_chmod_job.c index e71dea4c5..b84117ea4 100644 --- a/src/unix/unix_c/unix_chmod_job.c +++ b/src/unix/unix_c/unix_chmod_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_chown_job.c b/src/unix/unix_c/unix_chown_job.c index dc33f06c6..118801262 100644 --- a/src/unix/unix_c/unix_chown_job.c +++ b/src/unix/unix_c/unix_chown_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_chroot_job.c b/src/unix/unix_c/unix_chroot_job.c index bef5b7ce7..7fe625043 100644 --- a/src/unix/unix_c/unix_chroot_job.c +++ b/src/unix/unix_c/unix_chroot_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_close_job.c b/src/unix/unix_c/unix_close_job.c index a121ef989..31f26263e 100644 --- a/src/unix/unix_c/unix_close_job.c +++ b/src/unix/unix_c/unix_close_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_fchmod_job.c b/src/unix/unix_c/unix_fchmod_job.c index d6afc0107..063a1f294 100644 --- a/src/unix/unix_c/unix_fchmod_job.c +++ b/src/unix/unix_c/unix_fchmod_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_fchown_job.c b/src/unix/unix_c/unix_fchown_job.c index 65ed1272c..9df088217 100644 --- a/src/unix/unix_c/unix_fchown_job.c +++ b/src/unix/unix_c/unix_fchown_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_fdatasync_job.c b/src/unix/unix_c/unix_fdatasync_job.c index 1fc9702b2..6d3c71a63 100644 --- a/src/unix/unix_c/unix_fdatasync_job.c +++ b/src/unix/unix_c/unix_fdatasync_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) && defined(HAVE_FDATASYNC) diff --git a/src/unix/unix_c/unix_fsync_job.c b/src/unix/unix_c/unix_fsync_job.c index 8da0fda1e..5d932403f 100644 --- a/src/unix/unix_c/unix_fsync_job.c +++ b/src/unix/unix_c/unix_fsync_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_ftruncate_job.c b/src/unix/unix_c/unix_ftruncate_job.c index e564ad4c8..fdac1721d 100644 --- a/src/unix/unix_c/unix_ftruncate_job.c +++ b/src/unix/unix_c/unix_ftruncate_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_link_job.c b/src/unix/unix_c/unix_link_job.c index 0d0c5b0ce..3d51a240a 100644 --- a/src/unix/unix_c/unix_link_job.c +++ b/src/unix/unix_c/unix_link_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_lseek_job.c b/src/unix/unix_c/unix_lseek_job.c index 2239beefd..7b8a4ac62 100644 --- a/src/unix/unix_c/unix_lseek_job.c +++ b/src/unix/unix_c/unix_lseek_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_mkdir_job.c b/src/unix/unix_c/unix_mkdir_job.c index 85831f94f..3000ada80 100644 --- a/src/unix/unix_c/unix_mkdir_job.c +++ b/src/unix/unix_c/unix_mkdir_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_mkfifo_job.c b/src/unix/unix_c/unix_mkfifo_job.c index 4f66fa5fd..ff1bf2f79 100644 --- a/src/unix/unix_c/unix_mkfifo_job.c +++ b/src/unix/unix_c/unix_mkfifo_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_recv.c b/src/unix/unix_c/unix_recv.c index 7ea757611..4456d7421 100644 --- a/src/unix/unix_c/unix_recv.c +++ b/src/unix/unix_c/unix_recv.c @@ -13,6 +13,7 @@ #include #include +#include "lwt_unix.h" #include "unix_recv_send_utils.h" value lwt_unix_recv(value fd, value buf, value ofs, value len, value flags) diff --git a/src/unix/unix_c/unix_recvfrom.c b/src/unix/unix_c/unix_recvfrom.c index d55a646cc..fd86c247a 100644 --- a/src/unix/unix_c/unix_recvfrom.c +++ b/src/unix/unix_c/unix_recvfrom.c @@ -15,6 +15,7 @@ #include #include +#include "lwt_unix.h" #include "unix_recv_send_utils.h" value lwt_unix_recvfrom(value fd, value buf, value ofs, value len, value flags) diff --git a/src/unix/unix_c/unix_rename_job.c b/src/unix/unix_c/unix_rename_job.c index bdaf27fbd..a8a3d4ff8 100644 --- a/src/unix/unix_c/unix_rename_job.c +++ b/src/unix/unix_c/unix_rename_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_rmdir_job.c b/src/unix/unix_c/unix_rmdir_job.c index 4ac6d9d52..5a9d88a66 100644 --- a/src/unix/unix_c/unix_rmdir_job.c +++ b/src/unix/unix_c/unix_rmdir_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_send.c b/src/unix/unix_c/unix_send.c index de053de7b..fc4ba34be 100644 --- a/src/unix/unix_c/unix_send.c +++ b/src/unix/unix_c/unix_send.c @@ -13,6 +13,7 @@ #include #include +#include "lwt_unix.h" #include "unix_recv_send_utils.h" value lwt_unix_send(value fd, value buf, value ofs, value len, value flags) diff --git a/src/unix/unix_c/unix_sendto.c b/src/unix/unix_c/unix_sendto.c index 6c0ac8488..8a993e669 100644 --- a/src/unix/unix_c/unix_sendto.c +++ b/src/unix/unix_c/unix_sendto.c @@ -14,6 +14,7 @@ #include #include +#include "lwt_unix.h" #include "unix_recv_send_utils.h" value lwt_unix_sendto(value fd, value buf, value ofs, value len, value flags, diff --git a/src/unix/unix_c/unix_symlink_job.c b/src/unix/unix_c/unix_symlink_job.c index f2929dd7c..337ee2484 100644 --- a/src/unix/unix_c/unix_symlink_job.c +++ b/src/unix/unix_c/unix_symlink_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_tcdrain_job.c b/src/unix/unix_c/unix_tcdrain_job.c index fb6843d97..8b7787749 100644 --- a/src/unix/unix_c/unix_tcdrain_job.c +++ b/src/unix/unix_c/unix_tcdrain_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) && !defined(__ANDROID__) diff --git a/src/unix/unix_c/unix_tcflow_job.c b/src/unix/unix_c/unix_tcflow_job.c index 574916049..69e76f979 100644 --- a/src/unix/unix_c/unix_tcflow_job.c +++ b/src/unix/unix_c/unix_tcflow_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_tcflush_job.c b/src/unix/unix_c/unix_tcflush_job.c index f8414b2f1..151485ac1 100644 --- a/src/unix/unix_c/unix_tcflush_job.c +++ b/src/unix/unix_c/unix_tcflush_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_tcsendbreak_job.c b/src/unix/unix_c/unix_tcsendbreak_job.c index b1cb6930c..6b7c26c38 100644 --- a/src/unix/unix_c/unix_tcsendbreak_job.c +++ b/src/unix/unix_c/unix_tcsendbreak_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_truncate_job.c b/src/unix/unix_c/unix_truncate_job.c index c7911dbb1..9f647aa6c 100644 --- a/src/unix/unix_c/unix_truncate_job.c +++ b/src/unix/unix_c/unix_truncate_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_unlink_job.c b/src/unix/unix_c/unix_unlink_job.c index 5fb583d23..5385f53cd 100644 --- a/src/unix/unix_c/unix_unlink_job.c +++ b/src/unix/unix_c/unix_unlink_job.c @@ -16,11 +16,12 @@ */ /* Caml headers. */ -#include "lwt_unix.h" +#include "lwt_config.h" #include #include #include #include +#include "lwt_unix.h" #if !defined(LWT_ON_WINDOWS) diff --git a/src/unix/unix_c/unix_wait4.c b/src/unix/unix_c/unix_wait4.c index eae1aa6ad..ee0acf5e8 100644 --- a/src/unix/unix_c/unix_wait4.c +++ b/src/unix/unix_c/unix_wait4.c @@ -12,6 +12,7 @@ #include #include #include +#include "lwt_unix.h" #include #include From 0da9177c7983a2f7142efaab0fec00d59aa85c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonin=20D=C3=A9cimo?= Date: Thu, 20 Jun 2024 10:43:35 +0200 Subject: [PATCH 56/62] Compatibility definition for caml_convert_flag_list --- src/unix/lwt_unix.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/unix/lwt_unix.h b/src/unix/lwt_unix.h index 31d0e0472..47f093016 100644 --- a/src/unix/lwt_unix.h +++ b/src/unix/lwt_unix.h @@ -14,6 +14,11 @@ #include #include +#if OCAML_VERSION < 50000 +#define caml_convert_flag_list(flags, table) \ + caml_convert_flag_list((flags), (int *)(table)) +#endif + /* The macro to get the file-descriptor from a value. */ #if defined(LWT_ON_WINDOWS) #define FD_val(value) win_CRT_fd_of_filedescr(value) From 82b1792488690d51346c9e725ddd0050ffa2e955 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Jun 2024 12:28:52 +0000 Subject: [PATCH 57/62] Bump ocaml/setup-ocaml from 3.0.0.pre.alpha to 3.0.0.pre.beta Bumps [ocaml/setup-ocaml](https://github.com/ocaml/setup-ocaml) from 3.0.0.pre.alpha to 3.0.0.pre.beta. - [Release notes](https://github.com/ocaml/setup-ocaml/releases) - [Changelog](https://github.com/ocaml/setup-ocaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/ocaml/setup-ocaml/compare/v3.0.0-alpha...v3.0.0-beta) --- updated-dependencies: - dependency-name: ocaml/setup-ocaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/workflow.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 12c431e99..afb8b265b 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -81,7 +81,7 @@ jobs: uses: actions/checkout@v4 - name: Set-up OCaml - uses: ocaml/setup-ocaml@v3.0.0-alpha + uses: ocaml/setup-ocaml@v3.0.0-beta with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-local-packages: ${{ matrix.local-packages }} @@ -116,10 +116,10 @@ jobs: uses: actions/checkout@v4 - name: Set-up OCaml - uses: ocaml/setup-ocaml@v3.0.0-alpha + uses: ocaml/setup-ocaml@v3.0.0-beta with: ocaml-compiler: "5.2" allow-prerelease-opam: true dune-cache: true - - uses: ocaml/setup-ocaml/lint-opam@v3.0.0-alpha + - uses: ocaml/setup-ocaml/lint-opam@v3.0.0-beta From d66a862dbed968ad02d8c3bda27af54115a697c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 21:54:09 +0000 Subject: [PATCH 58/62] Bump ocaml/setup-ocaml from 3.0.0.pre.beta to 3.0.0.pre.beta1 Bumps [ocaml/setup-ocaml](https://github.com/ocaml/setup-ocaml) from 3.0.0.pre.beta to 3.0.0.pre.beta1. - [Release notes](https://github.com/ocaml/setup-ocaml/releases) - [Changelog](https://github.com/ocaml/setup-ocaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/ocaml/setup-ocaml/compare/v3.0.0-beta...v3.0.0-beta1) --- updated-dependencies: - dependency-name: ocaml/setup-ocaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/workflow.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index afb8b265b..28d245c31 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -81,7 +81,7 @@ jobs: uses: actions/checkout@v4 - name: Set-up OCaml - uses: ocaml/setup-ocaml@v3.0.0-beta + uses: ocaml/setup-ocaml@v3.0.0-beta1 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-local-packages: ${{ matrix.local-packages }} @@ -116,10 +116,10 @@ jobs: uses: actions/checkout@v4 - name: Set-up OCaml - uses: ocaml/setup-ocaml@v3.0.0-beta + uses: ocaml/setup-ocaml@v3.0.0-beta1 with: ocaml-compiler: "5.2" allow-prerelease-opam: true dune-cache: true - - uses: ocaml/setup-ocaml/lint-opam@v3.0.0-beta + - uses: ocaml/setup-ocaml/lint-opam@v3.0.0-beta1 From ae202d91e002e571b91df0a5a83e5379235e032d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Wed, 7 Feb 2024 11:22:41 +0100 Subject: [PATCH 59/62] Use raise and reraise instead of fail in more places in core --- src/core/lwt_pool.ml | 2 +- src/core/lwt_seq.ml | 6 +++--- src/core/lwt_stream.ml | 23 +++++++++++++-------- test/core/test_lwt.ml | 39 ++++++++++++++++++++++-------------- test/core/test_lwt_pool.ml | 14 ++++++------- test/core/test_lwt_result.ml | 2 +- test/core/test_lwt_stream.ml | 10 ++++----- 7 files changed, 56 insertions(+), 40 deletions(-) diff --git a/src/core/lwt_pool.ml b/src/core/lwt_pool.ml index 1605bc89f..bf06787ab 100644 --- a/src/core/lwt_pool.ml +++ b/src/core/lwt_pool.ml @@ -108,7 +108,7 @@ let validate_and_return p c = resolver is waiting. *) dispose p c >>= fun () -> replace_disposed p; - Lwt.fail e) + Lwt.reraise e) (* Acquire a pool member. *) let acquire p = diff --git a/src/core/lwt_seq.ml b/src/core/lwt_seq.ml index fe40eadfb..dd3f0396f 100644 --- a/src/core/lwt_seq.ml +++ b/src/core/lwt_seq.ml @@ -270,7 +270,7 @@ let rec unfold f u () = match f u with | None -> return_nil | Some (x, u') -> Lwt.return (Cons (x, unfold f u')) - | exception exc when Lwt.Exception_filter.run exc -> Lwt.fail exc + | exception exc when Lwt.Exception_filter.run exc -> Lwt.reraise exc let rec unfold_lwt f u () = let* x = f u in @@ -299,7 +299,7 @@ let rec of_seq seq () = | Seq.Nil -> return_nil | Seq.Cons (x, next) -> Lwt.return (Cons (x, (of_seq next))) - | exception exn when Lwt.Exception_filter.run exn -> Lwt.fail exn + | exception exn when Lwt.Exception_filter.run exn -> Lwt.reraise exn let rec of_seq_lwt (seq: 'a Lwt.t Seq.t): 'a t = fun () -> match seq () with @@ -315,4 +315,4 @@ let of_seq_lwt (seq: 'a Lwt.t Seq.t): 'a t = fun () -> let+ x = x in let next = of_seq_lwt next in Cons (x, next) - | exception exc when Lwt.Exception_filter.run exc -> Lwt.fail exc + | exception exc when Lwt.Exception_filter.run exc -> Lwt.reraise exc diff --git a/src/core/lwt_stream.ml b/src/core/lwt_stream.ml index 88e83f449..cb7539405 100644 --- a/src/core/lwt_stream.ml +++ b/src/core/lwt_stream.ml @@ -279,9 +279,9 @@ class ['a] bounded_push_impl (info : 'a push_bounded) wakener_cell last close = let waiter, wakener = Lwt.task () in info.pushb_push_waiter <- waiter; info.pushb_push_wakener <- wakener; - Lwt.fail exn + Lwt.reraise exn | _ -> - Lwt.fail exn) + Lwt.reraise exn) end else begin (* Push the element at the end of the queue. *) enqueue' (Some x) last; @@ -367,11 +367,18 @@ let feed s = else begin (* Otherwise request a new element. *) let thread = - from.from_create () >>= fun x -> - (* Push the element to the end of the queue. *) - enqueue x s; - if x = None then Lwt.wakeup s.close (); - Lwt.return_unit + (* The function [from_create] can raise an exception (with + [raise], rather than returning a failed promise with + [Lwt.fail]). In this case, we have to catch the exception + and turn it into a safe failed promise. *) + Lwt.catch + (fun () -> + from.from_create () >>= fun x -> + (* Push the element to the end of the queue. *) + enqueue x s; + if x = None then Lwt.wakeup s.close (); + Lwt.return_unit) + Lwt.reraise in (* Allow other threads to access this thread. *) from.from_thread <- thread; @@ -1070,7 +1077,7 @@ let parse s f = (fun () -> f s) (fun exn -> s.node <- node; - Lwt.fail exn) + Lwt.reraise exn) let hexdump stream = let buf = Buffer.create 80 and num = ref 0 in diff --git a/test/core/test_lwt.ml b/test/core/test_lwt.ml index 3a08fdc32..2ba5ab564 100644 --- a/test/core/test_lwt.ml +++ b/test/core/test_lwt.ml @@ -549,6 +549,15 @@ let catch_tests = suite "catch" [ state_is (Lwt.Return Exception) p end; + test "rejected (raise)" begin fun () -> + let p = + Lwt.catch + (fun () -> raise Exception) + (fun exn -> Lwt.return exn) + in + state_is (Lwt.Return Exception) p + end; + (* This is an analog of the "bind quirk," see https://github.com/ocsigen/lwt/issues/329 *) @@ -696,7 +705,7 @@ let backtrace_catch_tests = suite "backtrace_catch" [ test "rejected" begin fun () -> let p = Lwt.backtrace_catch add_loc - (fun () -> Lwt.fail Exception) + (fun () -> raise Exception) (fun exn -> Lwt.return exn) in state_is (Lwt.Return Exception) p @@ -789,7 +798,7 @@ let try_bind_tests = suite "try_bind" [ test "rejected" begin fun () -> let p = Lwt.try_bind - (fun () -> Lwt.fail Exception) + (fun () -> raise Exception) (fun _ -> Lwt.return Exit) (fun exn -> Lwt.return exn) in @@ -810,7 +819,7 @@ let try_bind_tests = suite "try_bind" [ test "rejected, h raises" begin fun () -> try ignore @@ Lwt.try_bind - (fun () -> Lwt.fail Exit) + (fun () -> raise Exit) (fun _ -> Lwt.return_unit) (fun _ -> raise Exception); Lwt.return_false @@ -961,7 +970,7 @@ let backtrace_try_bind_tests = suite "backtrace_try_bind" [ test "rejected" begin fun () -> let p = Lwt.backtrace_try_bind add_loc - (fun () -> Lwt.fail Exception) + (fun () -> raise Exception) (fun _ -> Lwt.return Exit) (fun exn -> Lwt.return exn) in @@ -1132,7 +1141,7 @@ let finalize_tests = suite "finalize" [ test "rejected, f' raises" begin fun () -> try ignore @@ Lwt.finalize - (fun () -> Lwt.fail Exit) + (fun () -> raise Exit) (fun () -> raise Exception); Lwt.return_false with Exception -> @@ -1169,7 +1178,7 @@ let finalize_tests = suite "finalize" [ let p = Lwt.finalize (fun () -> p) - (fun () -> Lwt.fail Exception) + (fun () -> raise Exception) in Lwt.wakeup r (); state_is (Lwt.Fail Exception) p @@ -1232,7 +1241,7 @@ let finalize_tests = suite "finalize" [ let p = Lwt.finalize (fun () -> p) - (fun () -> Lwt.fail Exception) + (fun () -> raise Exception) in Lwt.wakeup_exn r Exit; state_is (Lwt.Fail Exception) p @@ -1347,7 +1356,7 @@ let backtrace_finalize_tests = suite "backtrace_finalize" [ let f'_ran = ref false in let p = Lwt.backtrace_finalize add_loc - (fun () -> Lwt.fail Exception) + (fun () -> raise Exception) (fun () -> f'_ran := true; Lwt.return_unit) in Lwt.bind (state_is (Lwt.Fail Exception) p) (fun correct -> @@ -1367,7 +1376,7 @@ let backtrace_finalize_tests = suite "backtrace_finalize" [ test "rejected, f' raises" begin fun () -> try ignore @@ Lwt.backtrace_finalize add_loc - (fun () -> Lwt.fail Exit) + (fun () -> raise Exit) (fun () -> raise Exception); Lwt.return_false with Exception -> @@ -1404,7 +1413,7 @@ let backtrace_finalize_tests = suite "backtrace_finalize" [ let p = Lwt.backtrace_finalize add_loc (fun () -> p) - (fun () -> Lwt.fail Exception) + (fun () -> raise Exception) in Lwt.wakeup r (); state_is (Lwt.Fail Exception) p @@ -1439,7 +1448,7 @@ let backtrace_finalize_tests = suite "backtrace_finalize" [ let p = Lwt.backtrace_finalize add_loc (fun () -> p) - (fun () -> Lwt.fail Exception) + (fun () -> raise Exception) in Lwt.wakeup_exn r Exit; state_is (Lwt.Fail Exception) p @@ -1803,7 +1812,7 @@ let async_tests = suite "async" [ let saw = ref None in let restore = set_async_exception_hook (fun exn -> saw := Some exn) in - Lwt.async (fun () -> Lwt.fail Exception); + Lwt.async (fun () -> raise Exception); later (fun () -> restore (); !saw = Some Exception) @@ -1852,7 +1861,7 @@ let dont_wait_tests = suite "dont_wait" [ test "rejected" begin fun () -> let saw = ref None in Lwt.dont_wait - (fun () -> Lwt.fail Exception) + (fun () -> raise Exception) (fun exn -> saw := Some exn); later (fun () -> !saw = Some Exception) end; @@ -3371,7 +3380,7 @@ let cancel_catch_tests = suite "cancel catch" [ test "task, pending, canceled, on_cancel, forwarded" begin fun () -> let on_cancel_2_ran = ref false in let p, _ = Lwt.task () in - let p' = Lwt.catch (fun () -> p) Lwt.fail in + let p' = Lwt.catch (fun () -> p) Lwt.reraise in Lwt.on_cancel p' (fun () -> on_cancel_2_ran := true); Lwt.cancel p'; Lwt.return @@ -3895,7 +3904,7 @@ let storage_tests = suite "storage" [ Lwt.with_value key (Some 42) (fun () -> let p' = Lwt.with_value key (Some 1337) (fun () -> - Lwt.try_bind (fun () -> p) f Lwt.fail) + Lwt.try_bind (fun () -> p) f Lwt.reraise) in Lwt.wakeup r (); Lwt.return diff --git a/test/core/test_lwt_pool.ml b/test/core/test_lwt_pool.ml index cd7bf2706..942ca0d7e 100644 --- a/test/core/test_lwt_pool.ml +++ b/test/core/test_lwt_pool.ml @@ -16,7 +16,7 @@ let suite = suite "lwt_pool" [ end; test "creator exception" begin fun () -> - let gen = fun () -> Lwt.fail Dummy_error in + let gen = fun () -> raise Dummy_error in let p = Lwt_pool.create 1 gen in let u = Lwt_pool.use p (fun _ -> Lwt.return 0) in Lwt.return (Lwt.state u = Lwt.Fail Dummy_error) @@ -42,7 +42,7 @@ let suite = suite "lwt_pool" [ test "validation exceptions are propagated to users" begin fun () -> let c = Lwt_condition.create () in let gen = (fun () -> let l = ref 0 in Lwt.return l) in - let v l = if !l = 0 then Lwt.return_true else Lwt.fail Dummy_error in + let v l = if !l = 0 then Lwt.return_true else raise Dummy_error in let p = Lwt_pool.create 1 ~validate:v gen in let u1 = Lwt_pool.use p (fun l -> l := 1; Lwt_condition.wait c) in let u2 = Lwt_pool.use p (fun l -> Lwt.return !l) in @@ -106,7 +106,7 @@ let suite = suite "lwt_pool" [ test "waiter are notified on replacement" begin fun () -> let c = Lwt_condition.create () in let gen = (fun () -> let l = ref 0 in Lwt.return l) in - let v l = if !l = 0 then Lwt.return_true else Lwt.fail Dummy_error in + let v l = if !l = 0 then Lwt.return_true else raise Dummy_error in let p = Lwt_pool.create 1 ~validate:v gen in let u1 = Lwt_pool.use p (fun l -> l := 1; Lwt_condition.wait c) in let u2 = Lwt_pool.use p (fun l -> Lwt.return !l) in @@ -128,9 +128,9 @@ let suite = suite "lwt_pool" [ if !k then let l = ref 0 in Lwt.return l else - Lwt.fail Dummy_error + raise Dummy_error in - let v l = if !l = 0 then Lwt.return_true else Lwt.fail Dummy_error in + let v l = if !l = 0 then Lwt.return_true else raise Dummy_error in let p = Lwt_pool.create 1 ~validate:v gen in let u1 = Lwt_pool.use p (fun l -> l := 1; k:= false; Lwt_condition.wait c) in let u2 = Lwt_pool.use p (fun l -> Lwt.return !l) in @@ -156,7 +156,7 @@ let suite = suite "lwt_pool" [ let cond = Lwt_condition.create() in let p = Lwt_pool.create 1 ~validate:v ~check:c gen in let _ = Lwt_pool.use p (fun l -> l := 1; Lwt_condition.wait cond) in - let _ = Lwt_pool.use p (fun l -> l := 2; Lwt.fail Dummy_error) in + let _ = Lwt_pool.use p (fun l -> l := 2; raise Dummy_error) in let u3 = Lwt_pool.use p (fun l -> Lwt.return !l) in let () = Lwt_condition.signal cond "done" in Lwt.bind u3 (fun v -> @@ -169,7 +169,7 @@ let suite = suite "lwt_pool" [ let p = Lwt_pool.create 1 gen in let _ = Lwt_pool.use p (fun l -> Lwt.bind (Lwt_condition.wait cond) - (fun _ -> l:= 1; Lwt.fail Dummy_error)) in + (fun _ -> l:= 1; raise Dummy_error)) in let u2 = Lwt_pool.use p (fun l -> Lwt.return !l) in let () = Lwt_condition.signal cond "done" in Lwt.bind u2 (fun v -> diff --git a/test/core/test_lwt_result.ml b/test/core/test_lwt_result.ml index 8e354b630..2f91ef5e6 100644 --- a/test/core/test_lwt_result.ml +++ b/test/core/test_lwt_result.ml @@ -93,7 +93,7 @@ let suite = test "catch, error case" (fun () -> - let x () = Lwt.fail Dummy_error in + let x () = raise Dummy_error in Lwt.return (Lwt_result.catch x = Lwt_result.fail Dummy_error) ); diff --git a/test/core/test_lwt_stream.ml b/test/core/test_lwt_stream.ml index a19f0b3df..3e5193dc8 100644 --- a/test/core/test_lwt_stream.ml +++ b/test/core/test_lwt_stream.ml @@ -11,7 +11,7 @@ let expect_exit f = Lwt.return_false) (function | Exit -> Lwt.return_true - | e -> Lwt.fail e) + | e -> Lwt.reraise e) let suite = suite "lwt_stream" [ test "from" @@ -351,7 +351,7 @@ let suite = suite "lwt_stream" [ return (Some x) | (Result.Error e)::l -> q := l; - Lwt.fail e) + raise e) in Lwt_stream.to_list (Lwt_stream.wrap_exn stream) >>= fun l' -> return (l = l')); @@ -418,7 +418,7 @@ let suite = suite "lwt_stream" [ test "exception passing: basic, from" (fun () -> - let stream = Lwt_stream.from (fun () -> Lwt.fail Exit) in + let stream = Lwt_stream.from (fun () -> raise Exit) in expect_exit (fun () -> Lwt_stream.get stream)); test "exception passing: basic, from_direct" @@ -428,12 +428,12 @@ let suite = suite "lwt_stream" [ test "exception passing: to_list" (fun () -> - let stream = Lwt_stream.from (fun () -> Lwt.fail Exit) in + let stream = Lwt_stream.from (fun () -> raise Exit) in expect_exit (fun () -> Lwt_stream.to_list stream)); test "exception passing: mapped" (fun () -> - let stream = Lwt_stream.from (fun () -> Lwt.fail Exit) in + let stream = Lwt_stream.from (fun () -> raise Exit) in let stream = Lwt_stream.map (fun v -> v) stream in expect_exit (fun () -> Lwt_stream.get stream)); From 3bfbb5c23466269f4a6451922006896190c24e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Wed, 7 Feb 2024 11:23:36 +0100 Subject: [PATCH 60/62] Use raise and reraise instead of fail in more places in unix --- src/unix/lwt_io.ml | 34 ++++++++++++++--------------- src/unix/lwt_process.cppo.ml | 10 ++++----- src/unix/lwt_unix.cppo.ml | 18 +++++++-------- test/unix/test_lwt_bytes.ml | 6 ++--- test/unix/test_lwt_engine.ml | 2 +- test/unix/test_lwt_io.ml | 6 ++--- test/unix/test_lwt_unix.ml | 18 +++++++-------- test/unix/test_mcast.ml | 6 ++--- test/unix/test_sleep_and_timeout.ml | 4 ++-- 9 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/unix/lwt_io.ml b/src/unix/lwt_io.ml index cf0b18bf0..8b3033fae 100644 --- a/src/unix/lwt_io.ml +++ b/src/unix/lwt_io.ml @@ -231,7 +231,7 @@ let perform_io : type mode. mode _channel -> int Lwt.t = fun ch -> (function | Unix.Unix_error (Unix.EPIPE, _, _) -> Lwt.return 0 - | exn -> Lwt.fail exn) [@ocaml.warning "-4"] + | exn -> Lwt.reraise exn) [@ocaml.warning "-4"] else perform ch.buffer ptr len in @@ -525,7 +525,7 @@ let make : max = (match mode with | Input -> 0 | Output -> size); - close = lazy(Lwt.catch close Lwt.fail); + close = lazy(Lwt.catch close Lwt.reraise); abort_waiter = abort_waiter; abort_wakener = abort_wakener; main = wrapper; @@ -537,7 +537,7 @@ let make : perform_io, fun pos cmd -> try seek pos cmd - with e when Lwt.Exception_filter.run e -> Lwt.fail e + with e when Lwt.Exception_filter.run e -> Lwt.reraise e ); } and wrapper = { state = Idle; @@ -678,7 +678,7 @@ struct let ptr = ic.ptr in if ptr = ic.max then refill ic >>= function - | 0 -> Lwt.fail End_of_file + | 0 -> raise End_of_file | _ -> read_char ic else begin ic.ptr <- ptr + 1; @@ -690,7 +690,7 @@ struct (fun () -> read_char ic >|= fun ch -> Some ch) (function | End_of_file -> Lwt.return_none - | exn -> Lwt.fail exn) + | exn -> Lwt.reraise exn) let read_line ic = let buf = Buffer.create 128 in @@ -711,7 +711,7 @@ struct if cr_read then Buffer.add_char buf '\r'; Lwt.return(Buffer.contents buf) | exn -> - Lwt.fail exn) + Lwt.reraise exn) in read_char ic >>= function | '\r' -> loop true @@ -723,7 +723,7 @@ struct (fun () -> read_line ic >|= fun ch -> Some ch) (function | End_of_file -> Lwt.return_none - | exn -> Lwt.fail exn) + | exn -> Lwt.reraise exn) let unsafe_read_into' ic blit buf ofs len = let avail = ic.max - ic.ptr in @@ -771,7 +771,7 @@ struct let rec loop ic buf ofs len = read_into ic buf ofs len >>= function | 0 -> - Lwt.fail End_of_file + raise End_of_file | n -> let len = len - n in if len = 0 then @@ -985,7 +985,7 @@ struct if ic.max - ic.ptr < size then refill ic >>= function | 0 -> - Lwt.fail End_of_file + raise End_of_file | _ -> read_block_unsafe ic size f else begin @@ -1440,7 +1440,7 @@ let open_temp_file ?buffer ?flags ?perm ?temp_dir ?prefix ?(suffix = "") () = Lwt.return (fname, chan)) (function | Unix.Unix_error _ when n < 1000 -> attempt (n + 1) - | exn -> Lwt.fail exn) + | exn -> Lwt.reraise exn) in attempt 0 @@ -1468,7 +1468,7 @@ let create_temp_dir Lwt.return name) (function | Unix.Unix_error (Unix.EEXIST, _, _) when n < 1000 -> attempt (n + 1) - | exn -> Lwt.fail exn) + | exn -> Lwt.reraise exn) in attempt 0 @@ -1489,10 +1489,10 @@ let win32_unlink fn = (* If everything succeeded but the final removal still failed, restore original permissions *) Lwt_unix.chmod fn st_perm >>= fun () -> - Lwt.fail exn) + Lwt.reraise exn) ) - (fun _ -> Lwt.fail exn) - | exn -> Lwt.fail exn) + (fun _ -> Lwt.reraise exn) + | exn -> Lwt.reraise exn) let unlink = if Sys.win32 then @@ -1549,7 +1549,7 @@ let close_socket fd = (function (* Occurs if the peer closes the connection first. *) | Unix.Unix_error (Unix.ENOTCONN, _, _) -> Lwt.return_unit - | exn -> Lwt.fail exn) [@ocaml.warning "-4"]) + | exn -> Lwt.reraise exn) [@ocaml.warning "-4"]) (fun () -> Lwt_unix.close fd) @@ -1574,7 +1574,7 @@ let open_connection ?fd ?in_buffer ?out_buffer sockaddr = ~mode:output (Lwt_bytes.write fd))) (fun exn -> Lwt_unix.close fd >>= fun () -> - Lwt.fail exn) + Lwt.reraise exn) let with_close_connection f (ic, oc) = (* If the user already tried to close the socket and got an exception, we @@ -1639,7 +1639,7 @@ let establish_server_generic (function | Unix.Unix_error (Unix.ECONNABORTED, _, _) -> Lwt.return `Try_again - | e -> Lwt.fail e) + | e -> Lwt.reraise e) in Lwt.pick [try_to_accept; should_stop] >>= function diff --git a/src/unix/lwt_process.cppo.ml b/src/unix/lwt_process.cppo.ml index 791e38ba7..ceb86be23 100644 --- a/src/unix/lwt_process.cppo.ml +++ b/src/unix/lwt_process.cppo.ml @@ -378,7 +378,7 @@ let read_opt read ic = (function | Unix.Unix_error (Unix.EPIPE, _, _) | End_of_file -> Lwt.return_none - | exn -> Lwt.fail exn) [@ocaml.warning "-4"] + | exn -> Lwt.reraise exn) [@ocaml.warning "-4"] let recv_chars pr = let ic = pr#stdout in @@ -512,8 +512,8 @@ let pmap ?timeout ?env ?cwd ?stderr cmd text = | Lwt.Canceled as exn -> (* Cancel the getter if the sender was canceled. *) Lwt.cancel getter; - Lwt.fail exn - | exn -> Lwt.fail exn) + Lwt.reraise exn + | exn -> Lwt.reraise exn) let pmap_chars ?timeout ?env ?cwd ?stderr cmd chars = let pr = open_process ?timeout ?env ?cwd ?stderr cmd in @@ -534,8 +534,8 @@ let pmap_line ?timeout ?env ?cwd ?stderr cmd line = | Lwt.Canceled as exn -> (* Cancel the getter if the sender was canceled. *) Lwt.cancel getter; - Lwt.fail exn - | exn -> Lwt.fail exn) + Lwt.reraise exn + | exn -> Lwt.reraise exn) let pmap_lines ?timeout ?env ?cwd ?stderr cmd lines = let pr = open_process ?timeout ?env ?cwd ?stderr cmd in diff --git a/src/unix/lwt_unix.cppo.ml b/src/unix/lwt_unix.cppo.ml index 20f73378e..5edb4b83d 100644 --- a/src/unix/lwt_unix.cppo.ml +++ b/src/unix/lwt_unix.cppo.ml @@ -147,7 +147,7 @@ let auto_pause timeout = exception Timeout -let timeout d = sleep d >>= fun () -> Lwt.fail Timeout +let timeout d = sleep d >>= fun () -> raise Timeout let with_timeout d f = Lwt.pick [timeout d; Lwt.apply f ()] @@ -582,7 +582,7 @@ let wrap_syscall event ch action = | Retry_write -> register_action Write ch action | e when Lwt.Exception_filter.run e -> - Lwt.fail e + Lwt.reraise e (* +-----------------------------------------------------------------+ | Basic file input/output | @@ -636,7 +636,7 @@ let wait_read ch = Lwt.return_unit else register_action Read ch ignore) - Lwt.fail + Lwt.reraise external stub_read : Unix.file_descr -> Bytes.t -> int -> int -> int = "lwt_unix_read" external read_job : Unix.file_descr -> Bytes.t -> int -> int -> int job = "lwt_unix_read_job" @@ -694,7 +694,7 @@ let wait_write ch = Lwt.return_unit else register_action Write ch ignore) - Lwt.fail + Lwt.reraise external stub_write : Unix.file_descr -> Bytes.t -> int -> int -> int = "lwt_unix_write" external write_job : Unix.file_descr -> Bytes.t -> int -> int -> int job = "lwt_unix_write_job" @@ -1034,7 +1034,7 @@ let file_exists name = (fun e -> match e with | Unix.Unix_error _ -> Lwt.return_false - | _ -> Lwt.fail e) [@ocaml.warning "-4"] + | _ -> Lwt.reraise e) [@ocaml.warning "-4"] external utimes_job : string -> float -> float -> unit job = "lwt_unix_utimes_job" @@ -1140,7 +1140,7 @@ struct (fun e -> match e with | Unix.Unix_error _ -> Lwt.return_false - | _ -> Lwt.fail e) [@ocaml.warning "-4"] + | _ -> Lwt.reraise e) [@ocaml.warning "-4"] end @@ -1408,7 +1408,7 @@ let files_of_directory path = (fun () -> readdir_n handle chunk_size) (fun exn -> closedir handle >>= fun () -> - Lwt.fail exn) >>= fun entries -> + Lwt.reraise exn) >>= fun entries -> if Array.length entries < chunk_size then begin state := LDS_done; closedir handle >>= fun () -> @@ -1423,7 +1423,7 @@ let files_of_directory path = (fun () -> readdir_n handle chunk_size) (fun exn -> closedir handle >>= fun () -> - Lwt.fail exn) >>= fun entries -> + Lwt.reraise exn) >>= fun entries -> if Array.length entries < chunk_size then begin state := LDS_done; closedir handle >>= fun () -> @@ -2395,7 +2395,7 @@ let () = let _waitpid flags pid = Lwt.catch (fun () -> Lwt.return (Unix.waitpid flags pid)) - Lwt.fail + Lwt.reraise let waitpid = if Sys.win32 then diff --git a/test/unix/test_lwt_bytes.ml b/test/unix/test_lwt_bytes.ml index e29843ca8..6de438b8d 100644 --- a/test/unix/test_lwt_bytes.ml +++ b/test/unix/test_lwt_bytes.ml @@ -793,7 +793,7 @@ let suite = suite "lwt_bytes" [ ) (function | Invalid_argument _message -> Lwt.return_true - | exn -> Lwt.fail exn + | exn -> Lwt.reraise exn ) end; @@ -825,7 +825,7 @@ let suite = suite "lwt_bytes" [ ) (function | Invalid_argument _message -> Lwt.return_true - | exn -> Lwt.fail exn + | exn -> Lwt.reraise exn ) end; @@ -839,7 +839,7 @@ let suite = suite "lwt_bytes" [ ) (function | Invalid_argument _message -> Lwt.return_true - | exn -> Lwt.fail exn + | exn -> Lwt.reraise exn ) end; ] diff --git a/test/unix/test_lwt_engine.ml b/test/unix/test_lwt_engine.ml index 38c7dcff1..3b6a5d231 100644 --- a/test/unix/test_lwt_engine.ml +++ b/test/unix/test_lwt_engine.ml @@ -49,7 +49,7 @@ let timing_tests = [ | Lwt_unix.Timeout -> Lwt.return (Unix.gettimeofday ()) | exn -> - Lwt.fail exn) + Lwt.reraise exn) >>= fun stop -> diff --git a/test/unix/test_lwt_io.ml b/test/unix/test_lwt_io.ml index f6bad19cd..f902f5e3e 100644 --- a/test/unix/test_lwt_io.ml +++ b/test/unix/test_lwt_io.ml @@ -320,7 +320,7 @@ let suite = suite "lwt_io" [ exceptions_observed := !exceptions_observed + 1; Lwt.return_unit | exn -> - Lwt.fail exn) [@ocaml.warning "-4"] + Lwt.reraise exn) [@ocaml.warning "-4"] in let fd_r, fd_w = Lwt_unix.pipe () in @@ -353,7 +353,7 @@ let suite = suite "lwt_io" [ let filename = ref "." in let wrap f (filename', chan) = filename := filename'; f chan in let write_data chan = Lwt_io.write chan "test file content" in - let write_data_fail _ = Lwt.fail Dummy_error in + let write_data_fail _ = Lwt.fail Dummy_error in Lwt_io.with_temp_file (wrap write_data) ~prefix >>= fun _ -> let no_temps1 = not (Sys.file_exists !filename) in Lwt.catch @@ -422,7 +422,7 @@ let suite = suite "lwt_io" [ (function | Unix.Unix_error (Unix.EISDIR, "file_length", ".") -> Lwt.return_true - | exn -> Lwt.fail exn) + | exn -> Lwt.reraise exn) end; test "input channel of_bytes initial position" diff --git a/test/unix/test_lwt_unix.ml b/test/unix/test_lwt_unix.ml index b802d867d..609ca3c2a 100644 --- a/test/unix/test_lwt_unix.ml +++ b/test/unix/test_lwt_unix.ml @@ -172,7 +172,7 @@ let utimes_tests = [ (function | Unix.Unix_error (Unix.ENOENT, "utimes", _) -> Lwt.return_unit | Unix.Unix_error (Unix.EUNKNOWNERR _, "utimes", _) -> Lwt.return_unit - | e -> Lwt.fail e) [@ocaml.warning "-4"] >>= fun () -> + | e -> Lwt.reraise e) [@ocaml.warning "-4"] >>= fun () -> Lwt.return_true); ] @@ -218,7 +218,7 @@ let readdir_tests = Lwt.return (Some filename)) (function | End_of_file -> Lwt.return_none - | exn -> Lwt.fail exn) + | exn -> Lwt.reraise exn) >>= function | None -> Lwt.return acc | Some filename -> loop (filename::acc) @@ -305,7 +305,7 @@ let readdir_tests = (function | Unix.Unix_error (Unix.EBADF, tag', _) when tag' = tag -> Lwt.return_true - | exn -> Lwt.fail exn) [@ocaml.warning "-4"] + | exn -> Lwt.reraise exn) [@ocaml.warning "-4"] in Lwt_list.for_all_s (fun (tag, t) -> expect_ebadf tag t) @@ -711,7 +711,7 @@ let writev_tests = Lwt.return_false) (function | Invalid_argument _ -> Lwt.return_true - | e -> Lwt.fail e) + | e -> Lwt.reraise e) in let close write_fd = fun () -> @@ -963,7 +963,7 @@ let bind_tests = | Unix.Unix_error (Unix.EADDRINUSE, "bind", _) | Unix.Unix_error (Unix.EISDIR, "bind", _) as exn -> if attempts <= 1 then - Lwt.fail exn + Lwt.reraise exn else bind_loop (attempts - 1) | Unix.Unix_error (Unix.EPERM, "bind", _) -> @@ -972,8 +972,8 @@ let bind_tests = test should add a check for WSL by checking for the existence of /proc/version, reading it, and checking its contents for the string "WSL". *) - Lwt.fail Skip - | e -> Lwt.fail e) [@ocaml.warning "-4"] + raise Skip + | e -> Lwt.reraise e) [@ocaml.warning "-4"] in Lwt.finalize @@ -1014,7 +1014,7 @@ let bind_tests = Lwt.return_false) (function | Unix.Unix_error (Unix.EBADF, _, _) -> Lwt.return_true - | e -> Lwt.fail e) [@ocaml.warning "-4"]); + | e -> Lwt.reraise e) [@ocaml.warning "-4"]); test "bind: aborted" (fun () -> @@ -1028,7 +1028,7 @@ let bind_tests = Lwt.return_false) (function | Exit -> Lwt.return_true - | e -> Lwt.fail e)) + | e -> Lwt.reraise e)) (fun () -> Lwt_unix.close socket)); ] diff --git a/test/unix/test_mcast.ml b/test/unix/test_mcast.ml index dbdfd446c..a44838f55 100644 --- a/test/unix/test_mcast.ml +++ b/test/unix/test_mcast.ml @@ -26,7 +26,7 @@ let child mcast_addr join fd = if debug then Printf.printf "\nReceived multicast message %S\n%!" (Bytes.unsafe_to_string (Bytes.sub buf 0 n)); if Bytes.sub buf 0 n <> hello then - Lwt.fail (Failure "unexpected multicast message") + raise (Failure "unexpected multicast message") else Lwt.return_unit @@ -61,9 +61,9 @@ let test_mcast name join set_loop = | Unix.Unix_error (Unix.EINVAL, "send", _) | Unix.Unix_error (Unix.ENODEV, "setsockopt", _) | Unix.Unix_error (Unix.ENETUNREACH, "send", _) -> - Lwt.fail Skip + raise Skip | e -> - Lwt.fail e + Lwt.reraise e ) in Lwt.finalize t (fun () -> Lwt.join [Lwt_unix.close fd1; Lwt_unix.close fd2]) diff --git a/test/unix/test_sleep_and_timeout.ml b/test/unix/test_sleep_and_timeout.ml index 40acf1385..70831ea57 100644 --- a/test/unix/test_sleep_and_timeout.ml +++ b/test/unix/test_sleep_and_timeout.ml @@ -35,7 +35,7 @@ let suite = suite "Lwt_unix sleep and timeout" [ | Lwt_unix.Timeout -> let check = cmp_elapsed_time "timeout" start_time duration in Lwt.return check - | exn -> Lwt.fail exn + | exn -> Lwt.reraise exn ) end; @@ -61,7 +61,7 @@ let suite = suite "Lwt_unix sleep and timeout" [ let check = cmp_elapsed_time "with_timeout : timeout" start_time duration in Lwt.return check - | exn -> Lwt.fail exn + | exn -> Lwt.reraise exn ) end; From 4d98a0a934c677833d68fc00a61f1cfd7320fa12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Proust?= Date: Wed, 7 Feb 2024 11:24:17 +0100 Subject: [PATCH 61/62] Use reraise instead of fail in more places in ppx --- src/ppx/ppx_lwt.ml | 6 +++--- src/ppx/ppx_lwt.mli | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ppx/ppx_lwt.ml b/src/ppx/ppx_lwt.ml index c2030be23..e77486c1a 100644 --- a/src/ppx/ppx_lwt.ml +++ b/src/ppx/ppx_lwt.ml @@ -23,7 +23,7 @@ let add_wildcard_case cases = if not has_wildcard then cases @ (let loc = Location.none in - [case ~lhs:[%pat? exn] ~guard:None ~rhs:[%expr Lwt.fail exn]]) + [case ~lhs:[%pat? exn] ~guard:None ~rhs:[%expr Lwt.reraise exn]]) else cases (** {3 Internal names} *) @@ -154,11 +154,11 @@ let lwt_expression mapper exp attributes ext_loc = Some (mapper#expression { new_exp with pexp_attributes }) (* [assert%lwt $e$] ≡ - [try Lwt.return (assert $e$) with exn -> Lwt.fail exn] *) + [try Lwt.return (assert $e$) with exn -> Lwt.reraise exn] *) | Pexp_assert e -> let new_exp = let loc = !default_loc in - [%expr try Lwt.return (assert [%e e]) with exn -> Lwt.fail exn] + [%expr try Lwt.return (assert [%e e]) with exn -> Lwt.reraise exn] in Some (mapper#expression { new_exp with pexp_attributes }) diff --git a/src/ppx/ppx_lwt.mli b/src/ppx/ppx_lwt.mli index 1d3a541e6..0912c07e0 100644 --- a/src/ppx/ppx_lwt.mli +++ b/src/ppx/ppx_lwt.mli @@ -80,10 +80,10 @@ catch (fun () -> f x) prerr_endline msg; return () | exn -> - Lwt.fail exn) + Lwt.reraise exn) ]} - Note that the [exn -> Lwt.fail exn] branch is automatically added + Note that the [exn -> Lwt.reraise exn] branch is automatically added when needed. - finalizer: From 87f9b73b4c11ed9f35e39d4aeda19594ed1cde22 Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Tue, 2 Jul 2024 09:37:34 +0900 Subject: [PATCH 62/62] Update ocaml/setup-ocaml to v3 Signed-off-by: Sora Morimoto --- .github/workflows/workflow.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 28d245c31..d0200de66 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -81,11 +81,10 @@ jobs: uses: actions/checkout@v4 - name: Set-up OCaml - uses: ocaml/setup-ocaml@v3.0.0-beta1 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-local-packages: ${{ matrix.local-packages }} - allow-prerelease-opam: true - run: opam install conf-libev if: ${{ matrix.libev == true }} @@ -116,10 +115,9 @@ jobs: uses: actions/checkout@v4 - name: Set-up OCaml - uses: ocaml/setup-ocaml@v3.0.0-beta1 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: "5.2" - allow-prerelease-opam: true dune-cache: true - - uses: ocaml/setup-ocaml/lint-opam@v3.0.0-beta1 + - uses: ocaml/setup-ocaml/lint-opam@v3