diff --git a/README.md b/README.md index 631a3327..c261d668 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,14 @@ An actor-model multi-core scheduler for OCaml 5.

+

+ Quick Start | + + Tutorial | + Reference +    +

+ Riot is an [actor-model][actors] multi-core scheduler for OCaml 5. It brings [Erlang][erlang]-style concurrency to the language, where lightweight processes communicate via message-passing. @@ -60,9 +68,7 @@ several of its use-cases, like: ## Quick Start ``` -git clone https://github.com/leostera/riot -cd riot -opam install . +opam install riot ``` After that, you can use any of the [examples](./examples) as a base for your app, and run them: diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 00000000..e7cf319e --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,2 @@ +esy.lock +_esy diff --git a/examples/0-hello-world/README.md b/examples/0-hello-world/README.md deleted file mode 100644 index 8b760cf1..00000000 --- a/examples/0-hello-world/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# `1-hello-world` - -A basic project that spins up 1 process and logs a message from it. - -Normally, Riot programs will being by opening up the `Riot` module, which -exposes a lot of common functions to work with Processes, sending messages, and -receiving message. - -```ocaml -open Riot -``` - -Every Riot program begins with a call to `Riot.run`. `Riot.run` takes a single -function as input, and _does not terminate_. This is because Riot programs are -expected to _run forever_. - -If you want to terminate the Riot runtime, you can call `Riot.shutdown ()` from -anywhere in the program. Keep in mind that this will not await for all -processes to terminate. We will cover graceful-shutdowns of applications later -in this tutorial. - -```ocaml -let () = Riot.run @@ fun () -> -``` - -A Logger is included with Riot that is multi-core friendly, configurable at a -global and local level, and _non-blocking_. This means we can use this logger -from anywhere in the application, and always get messages in a reasonable -order, printed in a readable way (none of those pesky write-conflicts). - -```ocaml - Logger.start () |> Result.get_ok; -``` - -Riot has a `spawn` function that can be used to create a new process. Riot -processes are _cheap_, and Riot programs can have millions of processes. They -are not like Operating System processes (or threads), and are closer to -green-threads or fibers. - -`spawn` takes a `unit -> unit` function as an input, and will give us back a -_pid_. A Pid is a Process Identifier. Pids are unique during the execution of a -program and can be used to send messages to processes, to check if they are -still alive, and to terminate them. - -```ocaml - let pid1 = spawn say_hello in -``` - -A common scenario is waiting for a number of pids to terminate. For this Riot -offers a `wait_pids` function that will return after all the pids are finished. - -Be mindful that if the pids do not terminate, this function will get the caller -process stuck in that loop. We will see later in this tutorial more flexible -mechanisms for detecting when other processes terminate. - -```ocaml - wait_pids [pid1]; -``` - - -Finally, after our spawned process has terminated, we will simply log again. - -```ocaml - Logger.info (fun f -> f "%a has terminated" Pid.pp pid1) -``` - -Notice that when you run the program, the program itself _does not terminate_. -This is because we haven't ran `shutdown ()`. Calling it has the unfortunate -property that our Logger may not finish writing what it is meant to write. diff --git a/examples/0-hello-world/hello_world.ml b/examples/0-hello-world/hello_world.ml deleted file mode 100644 index da274aa4..00000000 --- a/examples/0-hello-world/hello_world.ml +++ /dev/null @@ -1 +0,0 @@ -Riot.run @@ fun () -> Stdlib.exit 0 diff --git a/examples/1-hello-world/README.md b/examples/1-hello-world/README.md index 8b760cf1..2492743b 100644 --- a/examples/1-hello-world/README.md +++ b/examples/1-hello-world/README.md @@ -1,13 +1,9 @@ # `1-hello-world` -A basic project that spins up 1 process and logs a message from it. - -Normally, Riot programs will being by opening up the `Riot` module, which -exposes a lot of common functions to work with Processes, sending messages, and -receiving message. +A project so basic that fits on a single line! ```ocaml -open Riot +Riot.run @@ fun () -> print_endline "Hello, Joe!" ``` Every Riot program begins with a call to `Riot.run`. `Riot.run` takes a single @@ -20,50 +16,17 @@ processes to terminate. We will cover graceful-shutdowns of applications later in this tutorial. ```ocaml -let () = Riot.run @@ fun () -> -``` - -A Logger is included with Riot that is multi-core friendly, configurable at a -global and local level, and _non-blocking_. This means we can use this logger -from anywhere in the application, and always get messages in a reasonable -order, printed in a readable way (none of those pesky write-conflicts). - -```ocaml - Logger.start () |> Result.get_ok; +Riot.run @@ fun () -> + print_endline "Hello, Joe!"; + Riot.shutdown () ``` -Riot has a `spawn` function that can be used to create a new process. Riot -processes are _cheap_, and Riot programs can have millions of processes. They -are not like Operating System processes (or threads), and are closer to -green-threads or fibers. - -`spawn` takes a `unit -> unit` function as an input, and will give us back a -_pid_. A Pid is a Process Identifier. Pids are unique during the execution of a -program and can be used to send messages to processes, to check if they are -still alive, and to terminate them. +The smallest Riot program, that starts and ends immediately, is then: ```ocaml - let pid1 = spawn say_hello in +Riot.(run shutdown) ``` -A common scenario is waiting for a number of pids to terminate. For this Riot -offers a `wait_pids` function that will return after all the pids are finished. - -Be mindful that if the pids do not terminate, this function will get the caller -process stuck in that loop. We will see later in this tutorial more flexible -mechanisms for detecting when other processes terminate. - -```ocaml - wait_pids [pid1]; -``` - - -Finally, after our spawned process has terminated, we will simply log again. - -```ocaml - Logger.info (fun f -> f "%a has terminated" Pid.pp pid1) -``` +## Next Steps -Notice that when you run the program, the program itself _does not terminate_. -This is because we haven't ran `shutdown ()`. Calling it has the unfortunate -property that our Logger may not finish writing what it is meant to write. +* the [next step](../2-spawn-process/) introduces you to Processes diff --git a/examples/1-hello-world/dune b/examples/1-hello-world/dune index dede377b..86da1383 100644 --- a/examples/1-hello-world/dune +++ b/examples/1-hello-world/dune @@ -1,3 +1,3 @@ (executable - (name hello_world) + (name main) (libraries riot)) diff --git a/examples/1-hello-world/hello_world.ml b/examples/1-hello-world/hello_world.ml deleted file mode 100644 index f6ea2f56..00000000 --- a/examples/1-hello-world/hello_world.ml +++ /dev/null @@ -1,16 +0,0 @@ -open Riot - -let say_hello () = - Logger.info (fun f -> f "hello from process %a" Pid.pp (self ())) - -let () = - Riot.run @@ fun () -> - let _ = Logger.start () |> Result.get_ok in - - say_hello (); - - let pid1 = spawn say_hello in - - wait_pids [ pid1 ]; - - Logger.info (fun f -> f "%a has terminated" Pid.pp pid1) diff --git a/examples/1-hello-world/main.ml b/examples/1-hello-world/main.ml new file mode 100644 index 00000000..765f4b58 --- /dev/null +++ b/examples/1-hello-world/main.ml @@ -0,0 +1 @@ +Riot.run @@ fun () -> print_endline "Hello, Joe!" diff --git a/examples/2-spawn-process/README.md b/examples/2-spawn-process/README.md new file mode 100644 index 00000000..af8a1722 --- /dev/null +++ b/examples/2-spawn-process/README.md @@ -0,0 +1,50 @@ +# `2-spawn-process` + +A _Process_ is a long-lived function, that has access to a mailbox to receive +messages. + +Here's how we can create a process to print out the message from the last tutorial: + +```ocaml +Riot.run @@ fun () -> + let open Riot in + let pid = spawn (fun () -> print_endline "Hello, Joe!") in +``` + +Riot has a `spawn` function that can be used to create a new process. Riot +processes are _cheap_, and Riot programs can have millions of processes. They +are not like Operating System processes (or threads), and are closer to +green-threads or fibers. + +`spawn` takes a `unit -> unit` function as an input, and will give us back a +_pid_. A `Pid` is a Process Identifier. Pids are unique during the execution of a +program and can be used to send messages to processes, to check if they are +still alive, and to terminate them. + +```ocaml + let pid = spawn (fun () -> print_endline "Hello, Joe!") in +``` + +Inside of a process, we can get the pid of the process by calling `self ()`. A +Pid can also be pretty-printed with `Pid.pp` but it is not serializable. + +```ocaml + let pid = spawn (fun () -> Printf.printff "Hello, %a!" Pid.pp (self ())) in +``` + +A common scenario is waiting for a number of pids to terminate. For this Riot +offers a `wait_pids` function that will return after all the pids are finished. + +Be mindful that if the pids do not terminate, this function will get the caller +process stuck in that wait loop. We will see later in this tutorial more +flexible mechanisms for detecting when other processes terminate. + +```ocaml + wait_pids [pid] +``` + +And as before, if we want the runtime to finish, we should call `shutdown ()`. + +## Next Steps + +* the [next step](../3-message-passing/) introduces you to communicating processes and Message passing diff --git a/examples/4-gen-server/dune b/examples/2-spawn-process/dune similarity index 100% rename from examples/4-gen-server/dune rename to examples/2-spawn-process/dune diff --git a/examples/2-spawn-process/main.ml b/examples/2-spawn-process/main.ml new file mode 100644 index 00000000..86864e40 --- /dev/null +++ b/examples/2-spawn-process/main.ml @@ -0,0 +1,5 @@ +Riot.run @@ fun () -> +let open Riot in +let pid = spawn (fun () -> Format.printf "Hello, %a!" Pid.pp (self ())) in +wait_pids [ pid ]; +shutdown () diff --git a/examples/3-message-passing/README.md b/examples/3-message-passing/README.md new file mode 100644 index 00000000..fb3e4bf0 --- /dev/null +++ b/examples/3-message-passing/README.md @@ -0,0 +1,47 @@ +# `3-message-passing` + +Now that we've learned to spawn processes, we can start sending messages to +them. + +Every message in Riot is _typed_. And all messages form part of the `Message.t` +type. To define a new message, you can write: + +```ocaml +type Message.t += Hello_world +``` + +Your message can have any shape you want, so long as it fits into this message +type. Once a message is defined, we can start a process that knows how to +receive them. To receive a message we use the `receive` function, like this: + +```ocaml +match receive () with +| Hello_world -> print_endline "Hello, World! :D" +``` + +`receive ()` will try to get a message from the _current process mailbox_. If +the mailbox is empty, `receive ()` _will suspend the process_ until a message +is delivered. + +Since messages are represented with an open variant, when we pattern match on +`receive ()` we will have to make sure to handle or ignore _other messages_. + +```ocaml +match receive () with +| Hello_world -> print_endline "Hello, World! :D" +| _ -> print_endline "Oh no, an unhandled message! D:" +``` + +Within a process, it is okay for us to do a _partial match_, since a process crashing isn't going to take the runtime down. So an alternative way to write this is: + +```ocaml +match[@warning "-8"] receive () with +| Hello_world -> print_endline "Hello, World! :D" +``` + +Although I would recommend you to be careful where you disable this warning, +since exhaustive pattern-matching is one of OCaml's best features. + +## Next Steps + +* the [next step](../4-long-lived-processes/) introduces you to long lived processes diff --git a/examples/dune b/examples/3-message-passing/dune similarity index 62% rename from examples/dune rename to examples/3-message-passing/dune index b91fa3a8..86da1383 100644 --- a/examples/dune +++ b/examples/3-message-passing/dune @@ -1,3 +1,3 @@ (executable - (name spawn_many) + (name main) (libraries riot)) diff --git a/examples/3-message-passing/main.ml b/examples/3-message-passing/main.ml new file mode 100644 index 00000000..fc8e434b --- /dev/null +++ b/examples/3-message-passing/main.ml @@ -0,0 +1,12 @@ +open Riot + +type Message.t += Hello_world + +let () = + Riot.run @@ fun () -> + let pid = + spawn (fun () -> + match[@warning "-8"] receive () with + | Hello_world -> print_endline "Hello, World! :D") + in + send pid Hello_world diff --git a/examples/4-long-lived-processes/README.md b/examples/4-long-lived-processes/README.md new file mode 100644 index 00000000..2996a802 --- /dev/null +++ b/examples/4-long-lived-processes/README.md @@ -0,0 +1,50 @@ +# `4-long-lived-processes` + +Up until now, we have only dealt with processes that start, do some work, and +immediately die: either prints something and terminates, or waits for a message +and once the message arrives, it terminates. + +But real systems are built out of long-living processes that can handle more +than a single message. How do we build those? Recursion, baby! + +```ocaml +let pid = spawn (fun () -> loop ()) in +(* ... *) +``` + +To make a process that will live indefinitely, you just need to make a +recursive function. This has some advantages: + +1. it is a very familiar way of programming in OCaml +2. it gives us State in a functional way (no mutation required!) + +So let's do this! We'll write a process that recieves a message, says Hello to +someone, and continues awaiting. + +```ocaml +let rec loop () = + (match receive () with + | Hello name -> print_endline ("Hello, " ^ name ^ "! :D") + | _ -> print_endline "Oh no, an unhandled message! D:"); + loop () +``` + +As we saw on the [message-passing tutorial](/3-message-passing/), processes can +receive all sorts of messages that we don't know about, although they will all +be typed, so we include a little catch-all to ignore unhandleable messages. + +One caveat is that because function application can't be interrupted, we need +to make sure we _yield_ control back to the scheduler at some point before +recursing. Otherwise one of the cores will be _blocked_ by this process until it yields. + +In our example, this is done automatically when we call `receive` + +In fact, we are strategically placing yields all through the standard library +to make it as seamless as possible to write Riot programs without thinking +about scheduler starvation. + +## Next Steps + +* the [next step](../5-links-and-monitors/) introduces you to links and + monitors, to keep track of the lifecycle of a process or to make the +lifecycle of a process be linked to another diff --git a/examples/0-hello-world/dune b/examples/4-long-lived-processes/dune similarity index 60% rename from examples/0-hello-world/dune rename to examples/4-long-lived-processes/dune index dede377b..86da1383 100644 --- a/examples/0-hello-world/dune +++ b/examples/4-long-lived-processes/dune @@ -1,3 +1,3 @@ (executable - (name hello_world) + (name main) (libraries riot)) diff --git a/examples/4-long-lived-processes/main.ml b/examples/4-long-lived-processes/main.ml new file mode 100644 index 00000000..acd4e95c --- /dev/null +++ b/examples/4-long-lived-processes/main.ml @@ -0,0 +1,16 @@ +open Riot + +type Message.t += Hello of string + +let () = + Riot.run @@ fun () -> + let rec loop () = + (match receive () with + | Hello name -> print_endline ("Hello, " ^ name ^ "! :D") + | _ -> print_endline "Oh no, an unhandled message! D:"); + loop () + in + let pid = spawn loop in + send pid (Hello "Joe"); + send pid (Hello "Mike"); + send pid (Hello "Robert") diff --git a/examples/5-links-and-monitors/README.md b/examples/5-links-and-monitors/README.md new file mode 100644 index 00000000..3bdb0b07 --- /dev/null +++ b/examples/5-links-and-monitors/README.md @@ -0,0 +1,110 @@ +# `5-links-and-monitors` + +When dealing with long-lived processes, it is useful to know when they +_terminate_ to either act upon it, or to have other processes terminate +together as a unit. + +For these use-cases, Riot has _monitors_ and _links_. One key difference +between these two is that _links_ are bidirectional and transitive. _Monitors_ +are not. + +## Monitors + +A *monitor* lets one process (`pid1`) be notified when another process (`pid2`) +terminates. Monitors are created with the function `monitor pid1 pid2`, like +this: + +```ocaml +let pid1 = spawn loop in +let pid2 = + spawn (fun () -> + monitor (self ()) pid1; + await_monitor_message ()) +in +(* ... *) +``` + +In our example, our second process is looping using a function that awaits a +specific monitoring message called `Process_down`, which includes the Pid of +the process that died. + +```ocaml +let rec await_monitor_message () = + match receive () with + | Process.Messages.Monitor (Process_down pid) -> + Format.printf "uh-oh! Process %a terminated\n%!" Pid.pp pid + | _ -> await_monitor_message () +``` + +If you've followed the tutorial so far, this should look familiar. `await_monitor_message` is: +* a recursive function +* that suspends the process until a message is received +* and prints out a message when the monitored process goes down + +To actively terminate our first process here, we can use `exit pid Normal`, +which tells the runtime to terminate this process with the reason `Normal`. +Exit reasons are a way to capture _why_ a process terminated, and include: + +* `Normal` – used when a process' exit is expected, and is the default when a + process function finishes +* `Exception exn` – used when a process ended because of an unhandled exception + +## Links + +A *link*, in contrast to a monitor, will actually link together the lifecycle +of 2 processes. This means that if `pid1` goes down, _so does `pid2`_. + +This is incredibly useful to split up work into several processes while still +being able to deal with all of them as a single unit of work. + +In our example, we create 2 more processes (`pid3` and `pid4`): + +```ocaml +let pid3 = spawn loop in +let pid4 = + spawn (fun () -> + link pid3; + loop ()) +in +(* ... *) +``` + +Two differences to note here are: +1. both of processes are using a regular infinite-loop function, no need to match on a specific message +2. the `link pid` function always links the _current process_ with another process. + +Since links are bidirectional, linking one end of 2 processes is enough. + +Links are also _transitive_, which means that if we add a new process `pid5` +and link it to `pid4`, then exiting `pid3` will terminate `pid4` which will +terminate `pid5`. + +Hopefully this gives you an idea of how links can be used to group processes +and establish dependencies between them. + +### Realistic Example + +For example, a "slack-to-discord" tunnel would need to read from slack, and +write to discord. Each of those tasks can be done in a separate process. But if +the slack connection goes down, it doesn't make sense to keep the discord +connection open. We can model our tunnel with links like this: + +```mermaid +graph TD + S2DT[Tunnel] + SConn[Slack Connection] + DConn[Discord Connection] + S2DT -->|link| SConn & DConn +``` + +So that when the Slack Connection process terminates, it will also terminate +the Tunnel process, which will also terminate the Discord Connection. + +In the next chapters we will learn about supervision, which will help us +_restart_ this tunnel and recreate our processes from a known-state, using +different strategies. + +## Next Steps + +* the [next step](../6-supervisors) introduces you to supervisors, a concept + built on top of links to orchestrate processes using certain strategies. diff --git a/examples/5-links-and-monitors/dune b/examples/5-links-and-monitors/dune new file mode 100644 index 00000000..86da1383 --- /dev/null +++ b/examples/5-links-and-monitors/dune @@ -0,0 +1,3 @@ +(executable + (name main) + (libraries riot)) diff --git a/examples/5-links-and-monitors/main.ml b/examples/5-links-and-monitors/main.ml new file mode 100644 index 00000000..080f20e3 --- /dev/null +++ b/examples/5-links-and-monitors/main.ml @@ -0,0 +1,37 @@ +open Riot + +let rec await_monitor_message () = + match receive () with + | Process.Messages.Monitor (Process_down pid) -> + Format.printf "uh-oh! Process %a terminated\n%!" Pid.pp pid + | _ -> await_monitor_message () + +let rec loop () = + yield (); + loop () + +let () = + Riot.run @@ fun () -> + (* monitor *) + let pid1 = spawn loop in + let pid2 = + spawn (fun () -> + monitor (self ()) pid1; + await_monitor_message ()) + in + sleep 0.1; + exit pid1 Normal; + wait_pids [ pid1; pid2 ]; + + (* link *) + let pid3 = spawn loop in + let pid4 = + spawn (fun () -> + link pid3; + loop ()) + in + sleep 0.2; + exit pid3 Normal; + wait_pids [ pid3; pid4 ]; + Format.printf "both processes (%a,%a) have terminated\n%!" Pid.pp pid3 Pid.pp + pid4 diff --git a/examples/6-supervisors/README.md b/examples/6-supervisors/README.md new file mode 100644 index 00000000..98b1a51b --- /dev/null +++ b/examples/6-supervisors/README.md @@ -0,0 +1,7 @@ +# `6-supervisors` + + +## Next Steps + +* the [next step](../7-supervision-trees/) will show you how to use supervisors + to build hierarchies of processes that collaborate to get work done. diff --git a/examples/6-supervisors/dune b/examples/6-supervisors/dune new file mode 100644 index 00000000..86da1383 --- /dev/null +++ b/examples/6-supervisors/dune @@ -0,0 +1,3 @@ +(executable + (name main) + (libraries riot)) diff --git a/examples/6-supervisors/main.ml b/examples/6-supervisors/main.ml new file mode 100644 index 00000000..e69de29b diff --git a/examples/7-supervision-trees/README.md b/examples/7-supervision-trees/README.md new file mode 100644 index 00000000..b39d4015 --- /dev/null +++ b/examples/7-supervision-trees/README.md @@ -0,0 +1,8 @@ +# `7-supervision-trees` + + +## Next Steps + +* the [next step](../8-applications/) will show you how to encapsulate your + supervision trees into _applications_, which can be used to orchestrate + startup and shutdown of your entire system. diff --git a/examples/7-supervision-trees/dune b/examples/7-supervision-trees/dune new file mode 100644 index 00000000..86da1383 --- /dev/null +++ b/examples/7-supervision-trees/dune @@ -0,0 +1,3 @@ +(executable + (name main) + (libraries riot)) diff --git a/examples/7-supervision-trees/main.ml b/examples/7-supervision-trees/main.ml new file mode 100644 index 00000000..e69de29b diff --git a/examples/8-applications/README.md b/examples/8-applications/README.md new file mode 100644 index 00000000..ae216949 --- /dev/null +++ b/examples/8-applications/README.md @@ -0,0 +1 @@ +# `8-applications` diff --git a/examples/8-applications/dune b/examples/8-applications/dune new file mode 100644 index 00000000..86da1383 --- /dev/null +++ b/examples/8-applications/dune @@ -0,0 +1,3 @@ +(executable + (name main) + (libraries riot)) diff --git a/examples/8-applications/main.ml b/examples/8-applications/main.ml new file mode 100644 index 00000000..e69de29b diff --git a/examples/9-logger/README.md b/examples/9-logger/README.md new file mode 100644 index 00000000..701e6ecd --- /dev/null +++ b/examples/9-logger/README.md @@ -0,0 +1 @@ +# `9-logger` diff --git a/examples/9-logger/dune b/examples/9-logger/dune new file mode 100644 index 00000000..86da1383 --- /dev/null +++ b/examples/9-logger/dune @@ -0,0 +1,3 @@ +(executable + (name main) + (libraries riot)) diff --git a/examples/9-logger/main.ml b/examples/9-logger/main.ml new file mode 100644 index 00000000..e69de29b diff --git a/examples/README.md b/examples/README.md index ee4878be..7b6a2ed7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,17 +4,28 @@ Hi! 👋 Excited to have you here. Riot's examples serve as a tutorial for building libraries and applications. Each example is a small complete project, with its own README, and next steps to take. -You can start at [./0-hello-world](./0-hello-world) and work your way up. +You can start at [./1-hello-world](./1-hello-world) and work your way up. -* [0-hello-world](./0-hello-world) – the smallest possible Riot program -* [1-spawn](./1-spawn) - creating your first process +* [1-hello-world](./1-hello-world) – a small program to get you started +* [2-spawn-process](./2-spawn-process) - creating your first process +* [3-message-passing](./3-message-passing) - sending a message to a process +* [4-long-lived-processes](./4-long-lived-processes) - creating a process that runs forever +* [5-links-and-monitors](./5-links-and-monitors/) - getting to know when a process terminates ### Coming Up -In future releases, as new work is done on the runtime and the Riot standard libraries, we'll be covering more topics such as: +The following tutorials are in the works: +* [6-supervisors](./6-supervisors/) - how to keep processes alive +* [7-supervision-trees](./7-supervision-trees/) - using supervisors to structure work +* [8-applications](./8-applications/) - packaging an application +* [9-logger](./9-logger) - using the built-in Logger + +And in future releases, as new work is done on the runtime and the Riot +standard libraries, we'll be covering more topics such as: + +* Gen servers * Agents * Binary-string matching * Best-practices around typing messages -* Gen servers * Application-level tracing diff --git a/riot/riot.mli b/riot/riot.mli index 6fe07635..e40d7752 100644 --- a/riot/riot.mli +++ b/riot/riot.mli @@ -53,6 +53,13 @@ module Pid : sig end module Message : sig + type t = .. + (** [t] is the type of all messages in a Riot program. + + Since this type is extensible, you can make sure different parts of your + program can see the constructors that are relevant for them. + *) + (** [select_marker] is used in a _selective receive_ call to match the exact messages you are looking for. This is useful to skip ahead in your mailbox without having to consume all the messages in it. @@ -61,13 +68,6 @@ module Message : sig | Take (** use [Take] to mark a message as selected *) | Skip (** use [Skip] to requeue for later consumption *) | Drop (** use [Drop] to remove this message while selecting *) - - type t = .. - (** [t] is the type of all messages in a Riot program. - - Since this type is extensible, you can make sure different parts of your - program can see the constructors that are relevant for them. - *) end module Process : sig diff --git a/test/application_test.ml b/test/application_test.ml index de8f4e9f..c78a8999 100644 --- a/test/application_test.ml +++ b/test/application_test.ml @@ -6,7 +6,7 @@ module Test = struct let start () = let pid = spawn (fun () -> - Logger.info (fun f -> f "application_test: test started!"); + Logger.info (fun f -> f "app started!"); sleep 0.5; shutdown ()) in diff --git a/test/gen-servers/dune b/test/gen-servers/dune new file mode 100644 index 00000000..86da1383 --- /dev/null +++ b/test/gen-servers/dune @@ -0,0 +1,3 @@ +(executable + (name main) + (libraries riot)) diff --git a/examples/4-gen-server/main.ml b/test/gen-servers/main.ml similarity index 100% rename from examples/4-gen-server/main.ml rename to test/gen-servers/main.ml diff --git a/examples/4-gen-server/old.ml b/test/gen-servers/old.ml similarity index 100% rename from examples/4-gen-server/old.ml rename to test/gen-servers/old.ml diff --git a/examples/http_client/dune b/test/http_client/dune similarity index 100% rename from examples/http_client/dune rename to test/http_client/dune diff --git a/examples/http_client/main.ml b/test/http_client/main.ml similarity index 100% rename from examples/http_client/main.ml rename to test/http_client/main.ml diff --git a/examples/http_server/README.md b/test/http_server/README.md similarity index 100% rename from examples/http_server/README.md rename to test/http_server/README.md diff --git a/examples/http_server/dune b/test/http_server/dune similarity index 100% rename from examples/http_server/dune rename to test/http_server/dune diff --git a/examples/http_server/http_server.ml b/test/http_server/http_server.ml similarity index 100% rename from examples/http_server/http_server.ml rename to test/http_server/http_server.ml diff --git a/examples/http_server/main.ml b/test/http_server/main.ml similarity index 100% rename from examples/http_server/main.ml rename to test/http_server/main.ml diff --git a/examples/http_server/server.ml b/test/http_server/server.ml similarity index 100% rename from examples/http_server/server.ml rename to test/http_server/server.ml diff --git a/examples/spawn_many.erl b/test/spawn_many.erl similarity index 100% rename from examples/spawn_many.erl rename to test/spawn_many.erl diff --git a/examples/spawn_many.ml b/test/spawn_many.ml similarity index 100% rename from examples/spawn_many.ml rename to test/spawn_many.ml