Thanks for taking the time to contribute to Riot ✨ All contributions are welcomed! This includes:
- PRs with bug fixes or features
- Examples
- Doc fixes
- Bug reports
- Links blogs featuring Riot
- Links to projects using Riot that can serve as large examples
- Links to libraries that can be used with Riot
To install Riot from sources, make sure to include all its dependencies:
; opam pin config.0.0.2 git+https://github.com/ocaml-sys/config.ml -y
; opam pin libc.0.0.1 git+https://github.com/ocaml-sys/libc.ml -y
; opam pin rio.0.0.8 git+https://github.com/riot-ml/riot -y
; opam pin bytestring.0.0.8 git+https://github.com/riot-ml/riot -y
; opam pin gluon.0.0.8 git+https://github.com/riot-ml/riot -y
; opam pin riot.0.0.8 git+https://github.com/riot-ml/riot -y
You can run builds with:
; dune build
You can run all tests with:
; dune test
The only requirement is that you have nix installed with flakes enabled.
To build the project you can run:
; nix build
To enter a dev shell with all deps, utop, lsp, and dune installed, simply run:
; nix develop
Or if you're using direnv:
; echo 'use flake' >> .envrc && direnv allow
An example repo for creating new projects using riot and nix intended for those new to nix can be found here.
If you want to add a test, you can do so by creating a new OCaml file in the
test
folder and updating test/dune
to include a stanza for your test. The
boilerplate we use for a test is:
[@@@warning "-8"]
open Riot
let main () =
let (Ok _) = Logger.start () in
(* you can change this log level to Debug while you debug your tests *)
Logger.set_log_level (Some Info);
(* your test code *)
let passed = true in
match passed with
| true ->
Logger.info (fun f -> f "print that everything went well");
shutdown ()
| _ ->
Logger.error (fun f -> f "print that something went wrong");
sleep 0.1;
Stdlib.exit 1
let () = Riot.run @@ main
Ideally tests will run Riot.run
without configuring it too much, but it can
be helpful to reduce the number of schedulers you use while you're creating a
test to begin with, or if you're testing behavior of a single scheduler. To do
that you can set the ~workers
argument to 0
, so that no new schedulers are
created and you run only the main thread.
let () = Riot.run ~workers:0 @@ main
For the moment we rely on dune test
to execute the small battery of tests.
Some of them take a long time (hello spawn_many
), but they are helpful in
determining if we have bugs in a moderate number of processes (eg. 1M of them).
If you find or introduce a bug into Riot, a quick way to debug what is
happening is to enable TRACE logging in the runtime. Right now this is done
by manually setting the default log level to (Some Trace)
.
Riot.Runtime.Log.set_log_level (Some Trace);
Once you do this, running your Riot program will emit a lot of logs. And it will also run a lot slower. Trace logs (and any low-level logs) are implemented using a lock over a stdout formatter, to ensure the outputs are consistent and isn't being overwritten by other threads.
To make sense of these logs I recommend to:
- Redirect this output to a file:
dune exec ./my_test.exe > logs
- Open the logs in your favorite editor to find the Pid that went bad (usually that got stuck in a loop)
- Filter logs by pid:
cat logs | grep "0\.992\.0" | head -n 100
This usually helps me find the sequence of actions for a Pid that tell me how it got into its state.
You may find you need more information than is available. Feel free to add more
Log.trace
calls all over Riot wherever you see fit, and submit them in a PR
if you think they'll help other people find bugs too.
Log.*
functions are cheap if the logs are disabled, since the function you
pass to them only is evaluated when that log level is enabled.
For doing performance work, it helps to use the olly
tracer from the
runtime_events_tools
package.
; opam install runtime_events_tools -y
; olly trace riot.trace _build/default/examples/http_server/main.exe
olly
will crate a trace file called riot.trace
and you can open this file
in 2 steps:
- run
./tools/trace_processor --httpd ./riot.trace
to preprocess the file (takes a bit) - go to
https://ui.perfetto.dev/
and click YES on the "Trace Processor Native Acceleration" dialogue
Typically you'll want to find all the instances of a certain operation that is slow. You can most likely see them straight on in the viewer, like this:
- click on a Process to see it expand
- click on the trace name you're interested in (say
major_slice
) - in the details tab below click on the name
- click on "Slices with the same name"
- click on Duration and sort by Highest First
That should show you the list of all the instances of the trace you're looking for, sorted by the slowest ones.