forked from riot-ml/riot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
47 changed files
with
410 additions
and
151 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
esy.lock | ||
_esy |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
(executable | ||
(name hello_world) | ||
(name main) | ||
(libraries riot)) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Riot.run @@ fun () -> print_endline "Hello, Joe!" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 () |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
(executable | ||
(name spawn_many) | ||
(name main) | ||
(libraries riot)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
2 changes: 1 addition & 1 deletion
2
examples/0-hello-world/dune → examples/4-long-lived-processes/dune
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
(executable | ||
(name hello_world) | ||
(name main) | ||
(libraries riot)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") |
Oops, something went wrong.