Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
77dcee1
allow multiple schduler to run in parallel
raphael-proust Jun 4, 2025
dbe6815
shorter basic multidomain test output
raphael-proust Jun 6, 2025
cfc7243
addiiotnal multidomain test
raphael-proust Jun 6, 2025
288ea06
multidomain tests moving promises and resolvers across domains
raphael-proust Jun 10, 2025
f88057a
better domainworker test
raphael-proust Jun 10, 2025
802a58a
tests: terser output, relevant exit codes
raphael-proust Jun 10, 2025
76add85
bump dependency to 5.3
raphael-proust Jun 10, 2025
d8d2249
don't auto-init domains on main.run
raphael-proust Jun 10, 2025
fb102c5
a bit mroe doc
raphael-proust Jun 10, 2025
48b9b51
make storage a domain-local thing, allow cross domain cancel
raphael-proust Jun 10, 2025
836cce7
replace `run_in_main` with `run_in_domain` (and misc imporvements)
raphael-proust Jun 10, 2025
b53a4be
don't double register implicitly
raphael-proust Jun 23, 2025
77384ef
some fixes and some deactivate tests
raphael-proust Jun 24, 2025
4478ace
restore some unix tests
raphael-proust Jun 24, 2025
ba710d1
more refs become atomics
raphael-proust Jun 24, 2025
e266af5
remove test-only primitive
raphael-proust Jun 24, 2025
cd0e502
add the `lwt_direct` package, for direct-style control flow
c-cube Jul 8, 2025
7145d61
Apply suggestions from code review
c-cube Jul 10, 2025
8cab71e
some docs
c-cube Jul 10, 2025
0978471
doc
c-cube Jul 10, 2025
5b746aa
lwt: expose some storage primitives in `Private`
c-cube Jul 11, 2025
33527db
lwt_direct: expose basic storage primitives
c-cube Jul 11, 2025
b869daa
add tests for Lwt_direct
c-cube Jul 11, 2025
495852e
more tests for Lwt_direct
c-cube Jul 11, 2025
d776f41
CI: see if --best-effort helps
c-cube Jul 11, 2025
dc85027
CI
c-cube Jul 11, 2025
9bee283
only test lwt_direct if OCaml >= 5.0
c-cube Jul 11, 2025
c5aa6c5
fix test on 4.xx
c-cube Jul 11, 2025
cdf51ce
dune
c-cube Jul 11, 2025
6e408a8
some improvements as discussed in PR's review
raphael-proust Jul 11, 2025
020ae8b
purely cosmetics tweaks
raphael-proust Jul 11, 2025
92c1d3e
opam stuff
c-cube Jul 12, 2025
61f338d
tighten a bit Lwt_direct, use Lwt.async_exception_hook
c-cube Jul 12, 2025
05d3233
test: increase coverage
c-cube Jul 12, 2025
d696674
add TODO in comment
raphael-proust Jul 15, 2025
8587e96
CHAGNELOG
raphael-proust Jul 15, 2025
e133852
mark versions in dune file
raphael-proust Jul 15, 2025
09fba3a
rename `run` to `spawn`
c-cube Jul 17, 2025
7080eb2
Merge branch 'simon/lwt-direct' into lwt-6
raphael-proust Jul 18, 2025
fda2814
ocaml.4.14 compat (via domain_shims)
raphael-proust Jul 18, 2025
7dd7151
Merge branch 'domains' into lwt-6
raphael-proust Jul 18, 2025
392336e
make lwt-direct compatible with multi-domain
raphael-proust Jul 18, 2025
1d89906
also run CI on 4.14
raphael-proust Jul 18, 2025
65f8ebd
trying to fix the notification system
raphael-proust Jul 21, 2025
51236bd
fix jobs' domain_id not being initialised
raphael-proust Jul 22, 2025
2027bc1
restore windows (untested)
raphael-proust Jul 22, 2025
f56fcce
more fix windows
raphael-proust Jul 22, 2025
aa442e2
clean-up unneeded flag
raphael-proust Jul 22, 2025
3dfa600
Merge pull request #1064 from ocsigen/lwt-6-draft
raphael-proust Jul 22, 2025
7fe46b8
prepare for 6alpha01
raphael-proust Jul 22, 2025
ab07965
additional test for multidomain: pipe communications
raphael-proust Sep 5, 2025
9bd9f9d
Merge branch 'master' into lwt-6
raphael-proust Sep 5, 2025
3142c9e
remove coverage/bisect
raphael-proust Sep 5, 2025
6568544
test/direct: use `test` stanza so executable is associated to pkg
raphael-proust Sep 5, 2025
b06ed14
adapt workflow to mish mash of compatibility to ocaml versions
raphael-proust Sep 5, 2025
94a397f
restore `Lwt_engine.fake_event`
raphael-proust Sep 5, 2025
8722710
direct: rewrite some parts of the documentation
raphael-proust Sep 5, 2025
1cc2803
multidomain pipe test: deactivate on windows
raphael-proust Sep 8, 2025
a5c0881
better encapsulation of multidomain-sync internals
raphael-proust Sep 8, 2025
9a538c5
notifications now work with an abstract id
raphael-proust Sep 11, 2025
e6efdd7
better domain-specificity in preemptive
raphael-proust Sep 12, 2025
205581f
fix preemptive test: but needs new primitive in preemptive
raphael-proust Sep 15, 2025
5261258
make_notification's domain.id parameter now optional
raphael-proust Sep 16, 2025
f4aa151
address some TODOs and then make new ones
raphael-proust Sep 16, 2025
75ba144
Lwt_preemptive better name for worker termination
raphael-proust Sep 19, 2025
2bb5c46
Merge pull request #1077 from ocsigen/lwt-6-default-domain
raphael-proust Sep 19, 2025
aa2a346
remove Async_switch, fix documentation regarding async_method
raphael-proust Sep 19, 2025
47bcbfd
set version to 6beta0
raphael-proust Sep 20, 2025
8cd79b8
emit runtime events
raphael-proust Oct 13, 2025
300bfe0
demoing stall detector
raphael-proust Oct 13, 2025
f462038
stall detection demo: fix bugs, simplify things
raphael-proust Oct 13, 2025
8c66f74
stall detection: demo of attaching to existing lwt program
raphael-proust Oct 14, 2025
6011e30
events: cosmetics and tweaks
raphael-proust Oct 14, 2025
db63fda
stall detection: tweak
raphael-proust Oct 14, 2025
6e724a4
stall-detection: README and improved script
raphael-proust Oct 14, 2025
1409b9d
strip mutli-domain feature
raphael-proust Oct 14, 2025
5359f7c
ocaml.5.4 in CI workflow
raphael-proust Oct 14, 2025
2cda79e
relax ocaml version constraint to 5.1.0
raphael-proust Oct 14, 2025
2947b44
remove some leftover domain-related stuff
raphael-proust Oct 14, 2025
30e4f4d
stall-detector: tweaks and improvements
raphael-proust Oct 15, 2025
8985a41
runtime-events as optional dep (to be compatible with 4.14)
raphael-proust Oct 15, 2025
0924154
ppx: introduce runtime events on bind, plus tracing demo
raphael-proust Oct 16, 2025
91eb8eb
tail tracing demo: emit fuchsia trace
raphael-proust Oct 17, 2025
874ddd6
tracing: more principled simpler approach
raphael-proust Oct 18, 2025
de6715b
more tracing, use local store key for trace nameing
raphael-proust Oct 18, 2025
6c65bbe
add new lwt_runtime_events lib to workflow
raphael-proust Oct 20, 2025
dbf13b5
tracing: improvement, switch demo to json format
raphael-proust Oct 21, 2025
8a2a718
tracing: remove counters
raphael-proust Oct 21, 2025
1d851ff
tracing demo: remove unusued dependency
raphael-proust Oct 21, 2025
564ed6d
ppx: trace also for and while
raphael-proust Oct 31, 2025
9082966
tracing examples moved out of tests
raphael-proust Nov 3, 2025
24dab8a
Merge pull request #1083 from ocsigen/lwt-6-only-await-and-events
raphael-proust Nov 3, 2025
c58f8b0
Merge branch 'master' into lwt-6
raphael-proust Nov 3, 2025
3700475
make test/unix/ tests as (package lwt)
raphael-proust Nov 14, 2025
ef536f1
lwt_ppx generates code for lwt6+
raphael-proust Nov 14, 2025
84eb4c4
add version numbers
raphael-proust Nov 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,28 @@ jobs:
os:
- ubuntu-latest
ocaml-compiler:
- "4.08"
- "4.09"
- "4.10"
- "4.11"
- "4.12"
- "4.13"
- "4.14"
- "5.0"
- "5.1"
- "5.2"
- "5.3"
- "5.4"
libev:
- true
- false
include:
- os: ubuntu-24.04-arm
ocaml-compiler: "5.3"
ocaml-compiler: "5.4"
libev: false
- os: macos-latest
ocaml-compiler: "5.3"
ocaml-compiler: "5.4"
libev: false
- os: windows-latest
ocaml-compiler: "5.3"
ocaml-compiler: "5.4"
libev: false
- os: ubuntu-latest
ocaml-name: "5.3.0+32bit"
ocaml-compiler: "ocaml-variants.5.3.0+options,ocaml-option-32bit"
ocaml-name: "5.4.0+32bit"
ocaml-compiler: "ocaml-variants.5.4.0+options,ocaml-option-32bit"
libev: false


Expand All @@ -63,7 +58,7 @@ jobs:
ocaml-compiler: ${{ matrix.ocaml-compiler }}

- name: set ppx-related variables
id: configppx
id: configpackages
shell: bash
run: |
opam exec -- ocaml src/util/letppx.ml
Expand All @@ -75,14 +70,32 @@ jobs:
- run: opam install ./lwt.opam ./lwt_react.opam ./lwt_retry.opam ./lwt_ppx.opam --deps-only --with-test

- run: opam install ./lwt_ppx__ppx_let_tests.opam --deps-only --with-test
if: ${{ fromJSON(steps.configppx.outputs.letppx) }}
if: ${{ fromJSON(steps.configpackages.outputs.letppx) }}

- run: opam install ./lwt_direct.opam --deps-only --with-test
if: ${{ fromJSON(steps.configpackages.outputs.direct) }}

- run: opam install ./lwt_runtime_events.opam --deps-only --with-test
if: ${{ fromJSON(steps.configpackages.outputs.runtime_events) }}

- run: opam exec -- dune build --only-packages lwt,lwt_react,lwt_retry

- run: opam exec -- dune build --only-packages lwt,lwt_ppx__ppx_let_tests
if: ${{ fromJSON(steps.configppx.outputs.letppx) }}
if: ${{ fromJSON(steps.configpackages.outputs.letppx) }}

- run: opam exec -- dune build --only-packages lwt,lwt_direct
if: ${{ fromJSON(steps.configpackages.outputs.direct) }}

- run: opam exec -- dune build --only-packages lwt,lwt_runtime_events
if: ${{ fromJSON(steps.configpackages.outputs.runtime_events) }}

- run: opam exec -- dune runtest --only-packages lwt,lwt_react,lwt_retry,lwt_ppx

- run: opam exec -- dune runtest --only-packages lwt,lwt_ppx__ppx_let_tests
if: ${{ fromJSON(steps.configppx.outputs.letppx) }}
if: ${{ fromJSON(steps.configpackages.outputs.letppx) }}

- run: opam exec -- dune runtest --only-packages lwt,lwt_direct
if: ${{ fromJSON(steps.configpackages.outputs.direct) }}

- run: opam exec -- dune runtest --only-packages lwt,lwt_runtime_events
if: ${{ fromJSON(steps.configpackages.outputs.runtime_events) }}
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ src/unix/discover_arguments
# OPAM 2.0 local switches.
_opam

# Coverage analysis.
bisect*.out
_coverage/

# For local work, tests, etc.
scratch/

Expand Down
12 changes: 11 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
===== 5.10.0 =====
===== 6.0.0+dev =====

====== Additions ======

* Lwt_direct using Lwt in direct-style. (Simon Cruanes, #1060)

* Support multiple scheduler running in parallel in separate domains.

* Exception filter defaults to letting systems exceptions through.

===== 5.10.0+dev =====

====== Misc

Expand Down
8 changes: 0 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ clean :
dune clean
rm -fr docs/api
rm -f src/unix/discover_arguments
rm -rf _coverage/

EXPECTED_FILES := \
--expect src/core/ \
Expand All @@ -65,10 +64,3 @@ EXPECTED_FILES := \
--do-not-expect src/unix/lwt_gc.ml \
--do-not-expect src/unix/lwt_throttle.ml \
--do-not-expect src/unix/unix_c/

.PHONY: coverage
coverage :
dune runtest --instrument-with bisect_ppx --force
bisect-ppx-report html $(EXPECTED_FILES)
bisect-ppx-report summary
@echo See _coverage/index.html
3 changes: 3 additions & 0 deletions dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
;; marking those as data-only so that CIs and other automated systems don't try
;; to build
(data_only_dirs examples)
29 changes: 24 additions & 5 deletions dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@

(package
(name lwt_ppx)
(version 5.9.2+dev)
(version 6.0.0-beta01)
(synopsis "PPX syntax for Lwt, providing something similar to async/await from JavaScript")
(depends
(ocaml (>= 4.08))
(ppxlib (>= 0.36))
(lwt (>= 5.7))))
(lwt (>= 6))))

(package
(name lwt_ppx__ppx_let_tests)
Expand All @@ -52,9 +52,28 @@
(lwt (>= 3.0))
(react (>= 1.0))))

(package
(name lwt_direct)
(version 6.0.0-beta01)
(synopsis "Direct-style control-flow and `await` for Lwt")
(authors "Simon Cruanes")
(depends
(ocaml (>= 5.0))
base-unix
(lwt (>= 6))))

(package
(name lwt_runtime_events)
(version 6.0.0-beta01)
(synopsis "Emit runtime events to trace the execution of Lwt programs")
(authors "Raphaël Proust")
(depends
(ocaml (>= 5.1))
))

(package
(name lwt)
(version 5.9.2+dev)
(version 6.0.0-beta01)
(synopsis "Promises and event-driven I/O")
(description "A promise is a value that may become determined in the future.

Expand All @@ -66,10 +85,10 @@ 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))
(ocaml (>= 4.14))
(cppo (and :build (>= 1.1)))
(ocamlfind (and :dev (>= 1.7.3-1)))
(odoc (and :with-doc (>= 2.3)))
dune-configurator
ocplib-endian)
(depopts base-threads base-unix conf-libev))
(depopts base-threads base-unix conf-libev lwt_runtime_events))
26 changes: 26 additions & 0 deletions examples/stall_detection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Stall detector

In this directory is a demo for live-monitoring runtime-events and detecting
stalls (extended periods of time when the Lwt main loop is not entered).

Stalls happen when too many of the promises make too many blocking calls. In the
example here (`Stallerlib.stall`) it's one promise which does increasingly long
blocking calls to `Unix.sleepf`. Stalls can also happen with many short blocking
calls (e.g., many promises logging to a file in a blocking way). Stalls
can also happen if promises execute long computations.

## Detection

The detection relies on two mechanism. The first mechanism polls the runtime
event ring to record the latest occurrence of Lwt scheduler's events. The second
mechanism checks that this latest occurrence is recent enough.

## selfdetector

The `selfdetector.ml` file shows how to run the detector in the same process as
the Lwt code. It runs in a separate domain so it executes in parallel.

## staller + detector

The `stall-detect.sh` runs separate processes for the Lwt-based program which
stalls and a program which monitors the events of the first process.
2 changes: 2 additions & 0 deletions examples/stall_detection/detector.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
let ringopt = (Sys.argv.(1), int_of_string Sys.argv.(2))
let () = Stallerlib.detect ~ringopt ()
14 changes: 14 additions & 0 deletions examples/stall_detection/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
(executable
(name staller)
(enabled_if (>= %{ocaml_version} 5.4))
(libraries lwt lwt.unix))

(executable
(name detector)
(enabled_if (>= %{ocaml_version} 5.4))
(libraries lwt lwt.unix runtime_events lwt_runtime_events))

(executable
(name selfdetector)
(enabled_if (>= %{ocaml_version} 5.4))
(libraries unix lwt lwt.unix runtime_events lwt_runtime_events))
7 changes: 7 additions & 0 deletions examples/stall_detection/selfdetector.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
let () = Runtime_events.start ()

let _ : _ Domain.t = Domain.spawn (fun () -> Stallerlib.detect ())

let () =
Printf.printf "start\n"; flush stdout;
Lwt_main.run (Stallerlib.stall 0.)
24 changes: 24 additions & 0 deletions examples/stall_detection/stall-detect.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash
set -euo pipefail

PROJECT_ROOT=./"$(git rev-parse --show-cdup)"
dune build "$PROJECT_ROOT/_build/default/test/stall_detection/staller.exe"
dune build "$PROJECT_ROOT/_build/default/test/stall_detection/detector.exe"

RING_DIR=$(mktemp -d -t staller-detector.XXXXXX)

OCAML_RUNTIME_EVENTS_DIR="$RING_DIR" "$PROJECT_ROOT"/_build/default/test/stall_detection/staller.exe &
STALLER_PID=$!

echo "staller started"

sleep 0.1

"$PROJECT_ROOT/_build/default/test/stall_detection/detector.exe" "$RING_DIR" "$STALLER_PID"

echo "detector started"

# Optional: wait for both processes to finish
wait

rm -f "$STALLER_PID.events"
4 changes: 4 additions & 0 deletions examples/stall_detection/staller.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
let () = Runtime_events.start ()

let () = Unix.sleep 2
let () = Lwt_main.run (Stallerlib.stall 0.)
62 changes: 62 additions & 0 deletions examples/stall_detection/stallerlib.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
(* STALLING *)

let () = Random.self_init ()

let to_s_ns f =
let ns = int_of_float (f *. 1_000_000_000.) in
(ns / 1_000_000_000 , ns mod 1_000_000_000)

let rec stall d =
let open Lwt.Syntax in
let s, ns = to_s_ns d in
Printf.printf "stalling for %d.%09d\n" s ns;
flush stdout;
Unix.sleepf d;
let* () = Lwt.pause () in
if d > 3. then exit 2 else stall (1.2*.(d+. Random.float 0.05))


(* DETECTING *)

(* set to maxint to avoid the first hit being a false positive *)
let last_lap = ref Int64.max_int
let alarmed = ref false

let detect ?ringopt () =
begin match ringopt with
| None -> Printf.printf "starting detection on self (%d)\n" (Unix.getpid ())
| Some (path, pid) -> Printf.printf "starting detection on %s/%d.events\n" path pid
end;
flush stdout;
let cursor = Runtime_events.create_cursor ringopt in
let is_stall t =
let delta = Int64.sub (Runtime_events.Timestamp.to_int64 t) !last_lap in
if delta > 1_000_000_000L (* 1 second *) then begin
Printf.printf "ALARM: stall 1s+ CRASHING\n"; flush stdout;
(match ringopt with
| None -> exit 1
| Some (_, pid) -> Unix.kill pid Sys.sigkill)
end;
if not !alarmed && delta > 500_000_000L (* 0.5 second *) then begin
alarmed := true;
Printf.printf "ALARM: stall .5s+\n"; flush stdout
end
in
let cb =
Runtime_events.Callbacks.create ()
|> Runtime_events.Callbacks.add_user_event
Runtime_events.Type.unit
(fun _ t e () ->
match Runtime_events.User.tag e with
| Lwt_runtime_events.Scheduler_lap ->
alarmed := false;
last_lap := Runtime_events.Timestamp.to_int64 t
| _ -> ())
in
let rec detect () =
Unix.sleepf 0.01;
let _ : int = Runtime_events.read_poll cursor cb None in
is_stall (Runtime_events.Timestamp.get_current ());
detect ()
in
detect ()
9 changes: 9 additions & 0 deletions examples/tracing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Tail tracing

This is a demo for producing a tail-trace for an Lwt program. The Lwt program
emits tracing events as introduced by the ppx. A separate program monitors the
execution of the Lwt program process and, upon a crash, it gets the content of
the events ring-buffer and dumps it into a perfetto-compatible format.

The produced trace shows the trace of execution for the end of the program, the
part leading to its crash. This is called "tail tracing."
11 changes: 11 additions & 0 deletions examples/tracing/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(executable
(name work_and_crash)
(enabled_if (>= %{ocaml_version} 5.4))
(libraries lwt lwt.unix)
(preprocess
(pps lwt_ppx)))

(executable
(name tailgate)
(enabled_if (>= %{ocaml_version} 5.4))
(libraries unix runtime_events lwt_runtime_events))
Loading
Loading