1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
//! # Quick Start Guide //! //! This document describes some of the core concepts of Heph as quick (10-20 //! minute) introduction to Heph. //! //! ## Actors //! //! The most important concept of Heph is an actor. The "actor" terminology //! comes from the [actor model], in which an actor is an entity that can do //! three things: //! * Receive and process messages. //! * Send messages to other actors. //! * Spawn new actors. //! //! In Heph actors can come in one of three different kinds, however for now //! we'll use the simplest kind: thread-local actors. To learn more about actors //! in Heph see the [actor] module. The simplest way to implement an actor is //! using an asynchronous function, which looks like the following. //! //! ``` //! # use heph::actor; //! # use heph::rt::ThreadLocal; //! // The `ThreadLocal` means we're running a thread-local actor, see the //! // `actor` module for more information about the different kinds of actors. //! async fn actor(mut ctx: actor::Context<String, ThreadLocal>) { //! // Messages can be received from the `actor::Context`. In this example we //! // can receive messages of the type `String`. //! if let Ok(msg) = ctx.receive_next().await { //! // Process the message. //! println!("got a message: {}", msg); //! } //! } //! # drop(actor); // Silence dead code warnings. //! ``` //! //! The example above also shows how an actor can receive and process messages. //! //! [actor model]: https://en.wikipedia.org/wiki/Actor_model //! [actor]: crate::actor //! //! ### Sending messages //! //! Sending messages is done using the [`ActorRef`] type. An actor reference //! (`ActorRef`) is a reference to an actor and can be used to send it messages, //! make a Remote Procedure Call (RPC) or check if it's still alive. The example //! below shows how to send a message to an actor. //! //! ``` //! # use heph::actor_ref::ActorRef; //! // Later on we'll see how we can get an `ActorRef`. //! async fn send_message(actor_ref: ActorRef<String>) { //! // Send a message to the actor referenced by `ActorRef`. //! let msg = "Hello world!".to_owned(); //! # let _ = // Silence unused `Result` warning. //! actor_ref.send(msg).await; //! } //! # drop(send_message); // Silence dead code warnings. //! ``` //! //! See the [actor reference] module for more information about what actor //! references can do, including sending messages and RPC. //! //! [`ActorRef`]: crate::actor_ref::ActorRef //! [actor reference]: crate::actor_ref //! //! ### Spawning Actors //! //! To run an actor it must be spawned. How to spawn an actor is defined by the //! [`Spawn`] trait, which is implemented on most runtime types, such as //! [`Runtime`] and [`RuntimeRef`], but we'll get to those types in the next //! section. //! //! To spawn an actor we need four things: //! * A [`Supervisor`]. Each actor needs supervision to determine what to do if //! the actor hits an error. For more information see the [supervisor] module. //! * The actor to start, or more specifically the [`NewActor`] implementation. //! For now we're going to ignore that detail and use an asynchronous function //! as actor which implements the `NewActor` trait for us. //! * The [starting argument(s)] for the actor, see the example below. //! * Finally we need [`ActorOptions`]. For now we'll ignore these as they are //! for more advanced use cases, out of scope for this quick start guide. //! //! Let's take a look at an example that spawns an actor. //! //! ``` //! # use std::io::{self, stdout, Write}; //! # use heph::actor; //! # use heph::actor_ref::ActorRef; //! # use heph::rt::{RuntimeRef, ThreadLocal}; //! # use heph::spawn::options::ActorOptions; //! # use heph::supervisor::SupervisorStrategy; //! # use log::warn; //! // Later on we'll see where we can get a `RuntimeRef`. //! fn spawn_actor(mut runtime_ref: RuntimeRef) { //! // Our supervisor for the error. //! let supervisor = supervisor; //! // An unfortunate implementation detail requires us to convert our actor //! // into a *function pointer* to implement the required traits. //! let actor = actor as fn(_, _, _) -> _; //! // The arguments passed to the actor, see the `actor` implementation below. //! // Since we want to pass multiple arguments we must do so in the tuple //! // notation. //! let arguments = ("Hello", true); //! // For this example we'll use the default options. //! let options = ActorOptions::default(); //! let actor_ref: ActorRef<String> = runtime_ref.spawn_local(supervisor, actor, arguments, options); //! //! // Now we can use `actor_ref` to send the actor messages, as shown by the //! // previous example. //! # drop(actor_ref); // Silence unused variable warning. //! } //! //! /// Supervisor for [`actor`]. //! fn supervisor(err: io::Error) -> SupervisorStrategy<(&'static str, bool)> { //! // First we need to handle the error, in this case we'll log it (always a //! // good idea). //! warn!("Actor hit an error: {}", err); //! //! // The we need to decide if we want to stop or restart the actor. For this //! // example we'll stop the actor. //! SupervisorStrategy::Stop //! } //! //! /// Our actor with two starting arguments: `greeting` and `on_mars`. //! async fn actor( //! mut ctx: actor::Context<String, ThreadLocal>, //! // Our starting arguments, passed as tuple (i.e. `(greeting, on_mars)`) to //! // the spawn method. //! greeting: &'static str, //! on_mars: bool //! ) -> io::Result<()> { //! while let Ok(name) = ctx.receive_next().await { //! if on_mars { //! write!(stdout(), "{} {} to mars!", greeting, name)?; //! } else { //! write!(stdout(), "{} {} to earth!", greeting, name)?; //! } //! } //! Ok(()) //! } //! # drop(spawn_actor); // Silence dead code warnings. //! ``` //! //! See the [`Spawn`] trait for more information about spawning actors and see //! the [supervisor] module for more information about actor supervision, e.g. //! when to stop and when to restart an actor. //! //! [`Spawn`]: crate::spawn::Spawn //! [`Runtime`]: crate::rt::Runtime //! [`RuntimeRef`]: crate::rt::RuntimeRef //! [`Supervisor`]: crate::supervisor::Supervisor //! [supervisor]: crate::supervisor //! [starting argument(s)]: crate::actor::NewActor::Argument //! [`ActorOptions`]: crate::spawn::options::ActorOptions //! [`NewActor`]: crate::actor::NewActor //! //! ## The Heph runtime //! //! To run all the actors we need a runtime. Luckily Heph provides one! A //! runtime can be created by first creating a runtime setup ([`rt::Setup`]), //! building the runtime ([`Runtime`]) from it and finally starting the runtime. //! The following example shows how to do all of the above. //! //! ``` //! # #![feature(never_type)] //! # //! # use heph::rt::{self, Runtime, RuntimeRef}; //! fn main() -> Result<(), rt::Error> { //! // First we setup the runtime and configure it. //! let mut runtime = Runtime::setup() //! // For this example we'll use two worker threads. Meaning we'll use //! // two threads to run all actors. //! .num_threads(2) //! // And we build our configured runtime. //! .build()?; //! //! // Run `spawn_actor` on each worker thread (both of them) to spawn our //! // thread-local actor. //! runtime.run_on_workers(spawn_actor)?; //! //! // Start the runtime. //! // This will wait until all actors are finished running. //! runtime.start() //! } //! //! // This is the same function as the one in the previous example. //! fn spawn_actor(runtime_ref: RuntimeRef) -> Result<(), !> { //! // ... //! # drop(runtime_ref); // Silence unused variable warning. //! # Ok(()) //! } //! ``` //! //! For more information about setting up and using the runtime see the //! [runtime] module. Also take a look at some of the options available on the //! [`rt::Setup`] type. //! //! Now you know the core concepts of Heph! //! //! If you want to look at some more example take a look at the [examples] in //! the examples directory of the source code. //! //! [`rt::Setup`]: crate::rt::Setup //! [runtime]: crate::rt //! [examples]: https://github.com/Thomasdezeeuw/heph/blob/master/examples/README.md