├── rustfmt.toml ├── .gitignore ├── src ├── actors │ ├── mod.rs │ ├── mocker.rs │ └── signal.rs ├── clock.rs ├── fut │ ├── from_err.rs │ ├── stream_finish.rs │ ├── stream_map_err.rs │ ├── either.rs │ ├── map.rs │ ├── helpers.rs │ ├── and_then.rs │ ├── then.rs │ ├── timeout.rs │ ├── stream_map.rs │ ├── chain.rs │ ├── stream_timeout.rs │ ├── stream_and_then.rs │ ├── map_err.rs │ ├── stream_then.rs │ ├── stream_fold.rs │ └── result.rs ├── mailbox.rs ├── address │ ├── envelope.rs │ ├── message.rs │ └── queue.rs ├── msgs.rs ├── context.rs ├── supervisor.rs ├── contextitems.rs ├── stream.rs ├── utils.rs ├── lib.rs ├── sync.rs ├── handler.rs └── registry.rs ├── examples ├── chat │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── main.rs │ │ ├── codec.rs │ │ ├── client.rs │ │ ├── session.rs │ │ └── server.rs ├── ping.rs ├── fibonacci.rs └── ring.rs ├── Makefile ├── .appveyor.yml ├── LICENSE-MIT ├── MIGRATION.md ├── tests ├── test_execute.rs ├── test_supervisor.rs ├── test_actors.rs ├── test_fut.rs ├── test_arbiter.rs ├── test_sync.rs ├── derive.rs ├── test_actor.rs └── test_lifecycle.rs ├── .travis.yml ├── Cargo.toml ├── CODE_OF_CONDUCT.md ├── CHANGES.md └── README.md /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 89 2 | reorder_imports = true 3 | #wrap_comments = true 4 | fn_args_density = "Compressed" 5 | #use_small_heuristics = false 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | /gh-pages 3 | __pycache__ 4 | 5 | *.so 6 | *.out 7 | *.pyc 8 | *.pid 9 | *.sock 10 | *~ 11 | target/ 12 | examples/chat/target/ 13 | -------------------------------------------------------------------------------- /src/actors/mod.rs: -------------------------------------------------------------------------------- 1 | //! Helper actors 2 | 3 | pub mod mocker; 4 | 5 | #[cfg(feature = "resolver")] 6 | pub mod resolver; 7 | 8 | #[cfg(feature = "signal")] 9 | pub mod signal; 10 | -------------------------------------------------------------------------------- /src/clock.rs: -------------------------------------------------------------------------------- 1 | //! A configurable source of time. 2 | //! 3 | //! This module provides an API to get the current instant in such a way that 4 | //! the source of time may be configured. This allows mocking out the source of 5 | //! time in tests. 6 | //! 7 | //! See [Module `tokio_timer::clock`] for full documentation. 8 | //! 9 | //! [Module `tokio_timer::clock`]: https://docs.rs/tokio-timer/latest/tokio_timer/clock/index.html 10 | 11 | pub use tokio_timer::clock::{now, Clock, Now}; 12 | -------------------------------------------------------------------------------- /examples/chat/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chat" 3 | version = "0.1.0" 4 | authors = ["Nikolay Kim "] 5 | workspace = "../../" 6 | 7 | [[bin]] 8 | name = "server" 9 | path = "src/main.rs" 10 | 11 | [[bin]] 12 | name = "client" 13 | path = "src/client.rs" 14 | 15 | [dependencies] 16 | rand = "0.3" 17 | bytes = "0.4" 18 | byteorder = "1.1" 19 | futures = "0.1" 20 | tokio = "0.1" 21 | tokio-codec = "0.1" 22 | tokio-io = "0.1" 23 | tokio-tcp = "0.1" 24 | 25 | serde = "1.0" 26 | serde_json = "1.0" 27 | serde_derive = "1.0" 28 | 29 | actix = { path = "../../" } 30 | -------------------------------------------------------------------------------- /examples/chat/README.md: -------------------------------------------------------------------------------- 1 | # Chat example 2 | 3 | 4 | ## Server 5 | 6 | Chat server listens for incoming tcp connections. Server can access several types of message: 7 | 8 | * `/list` - list all available rooms 9 | * `/join name` - join room, if room does not exist, create new one 10 | * `some message` - just string, send message to all peers in same room 11 | * client has to send heartbeat `Ping` messages, if server does not receive a heartbeat 12 | message for 10 seconds connection gets dropped 13 | 14 | To start server use command: `cargo run --bin server` 15 | 16 | ## Client 17 | 18 | Client connects to server. Reads input from stdin and sends to server. 19 | 20 | To run client use command: `cargo run --bin client` 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default build test doc clean 2 | 3 | CARGO_FLAGS := --features "$(FEATURES)" 4 | 5 | default: test 6 | 7 | build: 8 | cargo build $(CARGO_FLAGS) 9 | 10 | test: build 11 | cargo test $(CARGO_FLAGS) 12 | cd examples/chat && cargo build 13 | 14 | skeptic: 15 | USE_SKEPTIC=1 cargo test $(CARGO_FLAGS) 16 | 17 | # cd examples/word-count && python setup.py install && pytest -v tests 18 | 19 | clippy: 20 | if $$CLIPPY; then cargo clippy $(CARGO_FLAGS); fi 21 | 22 | doc: build 23 | cargo doc --no-deps $(CARGO_FLAGS) 24 | cd guide; mdbook build -d ../target/doc/guide/; cd .. 25 | 26 | clean: 27 | rm -r target 28 | 29 | .PHONY: gh-pages-doc 30 | gh-pages-doc: doc 31 | cd gh-pages && git pull 32 | rm -r gh-pages/doc 33 | cp -r target/doc gh-pages/ 34 | rm gh-pages/doc/.lock 35 | cd gh-pages && git add . 36 | cd gh-pages && git commit -m "Update documentation" 37 | 38 | publish: default gh-pages-doc 39 | cargo publish 40 | cd gh-pages && git push 41 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | global: 3 | PROJECT_NAME: actix 4 | matrix: 5 | # Stable channel 6 | - TARGET: i686-pc-windows-msvc 7 | CHANNEL: stable 8 | - TARGET: x86_64-pc-windows-gnu 9 | CHANNEL: stable 10 | - TARGET: x86_64-pc-windows-msvc 11 | CHANNEL: stable 12 | # Nightly channel 13 | - TARGET: i686-pc-windows-msvc 14 | CHANNEL: nightly 15 | - TARGET: x86_64-pc-windows-gnu 16 | CHANNEL: nightly 17 | - TARGET: x86_64-pc-windows-msvc 18 | CHANNEL: nightly 19 | 20 | # Install Rust and Cargo 21 | # (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml) 22 | install: 23 | - curl -sSf -o rustup-init.exe https://win.rustup.rs 24 | - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y 25 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 26 | - rustc -Vv 27 | - cargo -V 28 | 29 | # 'cargo test' takes care of building for us, so disable Appveyor's build stage. 30 | build: false 31 | 32 | # Equivalent to Travis' `script` phase 33 | test_script: 34 | - set RUST_BACKTRACE=1 35 | - cargo build 36 | - cargo test 37 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Nikilay Kim 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /src/fut/from_err.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll}; 2 | use std::marker::PhantomData; 3 | 4 | use actor::Actor; 5 | use fut::ActorFuture; 6 | 7 | /// Future for the `from_err` combinator, changing the error type of a future. 8 | /// 9 | /// This is created by the `Future::from_err` method. 10 | #[derive(Debug)] 11 | #[must_use = "futures do nothing unless polled"] 12 | pub struct FromErr 13 | where 14 | A: ActorFuture, 15 | { 16 | future: A, 17 | f: PhantomData, 18 | } 19 | 20 | pub fn new(future: A) -> FromErr 21 | where 22 | A: ActorFuture, 23 | { 24 | FromErr { 25 | future, 26 | f: PhantomData, 27 | } 28 | } 29 | 30 | impl> ActorFuture for FromErr { 31 | type Item = A::Item; 32 | type Error = E; 33 | type Actor = A::Actor; 34 | 35 | fn poll( 36 | &mut self, act: &mut A::Actor, ctx: &mut ::Context, 37 | ) -> Poll { 38 | let e = match self.future.poll(act, ctx) { 39 | Ok(Async::NotReady) => return Ok(Async::NotReady), 40 | other => other, 41 | }; 42 | e.map_err(From::from) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/fut/stream_finish.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll}; 2 | 3 | use actor::Actor; 4 | use fut::{ActorFuture, ActorStream}; 5 | 6 | /// A combinator used to convert stream into a future, future resolves 7 | /// when stream completes. 8 | /// 9 | /// This structure is produced by the `ActorStream::finish` method. 10 | #[derive(Debug)] 11 | #[must_use = "streams do nothing unless polled"] 12 | pub struct StreamFinish(S); 13 | 14 | pub fn new(s: S) -> StreamFinish 15 | where 16 | S: ActorStream, 17 | { 18 | StreamFinish(s) 19 | } 20 | 21 | impl ActorFuture for StreamFinish 22 | where 23 | S: ActorStream, 24 | { 25 | type Item = (); 26 | type Error = S::Error; 27 | type Actor = S::Actor; 28 | 29 | fn poll( 30 | &mut self, act: &mut S::Actor, ctx: &mut ::Context, 31 | ) -> Poll<(), S::Error> { 32 | loop { 33 | match self.0.poll(act, ctx) { 34 | Ok(Async::NotReady) => return Ok(Async::NotReady), 35 | Ok(Async::Ready(None)) => return Ok(Async::Ready(())), 36 | Ok(Async::Ready(Some(_))) => (), 37 | Err(err) => return Err(err), 38 | }; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/fut/stream_map_err.rs: -------------------------------------------------------------------------------- 1 | use futures::Poll; 2 | 3 | use actor::Actor; 4 | use fut::ActorStream; 5 | 6 | /// A stream combinator which will change the error type of a stream from one 7 | /// type to another. 8 | /// 9 | /// This is produced by the `ActorStream::map_err` method. 10 | #[derive(Debug)] 11 | #[must_use = "streams do nothing unless polled"] 12 | pub struct StreamMapErr { 13 | stream: S, 14 | f: F, 15 | } 16 | 17 | pub fn new(stream: S, f: F) -> StreamMapErr 18 | where 19 | S: ActorStream, 20 | F: FnMut(S::Error, &mut S::Actor, &mut ::Context) -> U, 21 | { 22 | StreamMapErr { stream, f } 23 | } 24 | 25 | impl ActorStream for StreamMapErr 26 | where 27 | S: ActorStream, 28 | F: FnMut(S::Error, &mut S::Actor, &mut ::Context) -> U, 29 | { 30 | type Item = S::Item; 31 | type Error = U; 32 | type Actor = S::Actor; 33 | 34 | fn poll( 35 | &mut self, act: &mut Self::Actor, ctx: &mut ::Context, 36 | ) -> Poll, U> { 37 | match self.stream.poll(act, ctx) { 38 | Ok(ok) => Ok(ok), 39 | Err(e) => Err((self.f)(e, act, ctx)), 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /MIGRATION.md: -------------------------------------------------------------------------------- 1 | ## 0.7.9 2 | 3 | - `actix` no longer re-exports itself in `actix::prelude` to avoid conflicts with 2018 editions. Please access it through your `extern crate actix` import when necessary 4 | 5 | ## 0.7.6 6 | 7 | - `trust-dns-resolver` dependency was bumped to version 0.10.0. If you use a 8 | custom resolver, you will need to switch your `ResolverConfig` and 9 | `ResolverOpts` to `trust-dns-resolver` 0.10.0 instead of 0.9.1. 10 | 11 | ## 0.7 12 | 13 | * `Addr` get refactored. `Syn` and `Unsync` removed. all addresses are 14 | `Syn` now, only `Addr` exsits 15 | 16 | * `Arbiter` uses `tokio::runtime::current_thread` 17 | 18 | * `Arbiter::arbiter()` renamed to `Arbiter::current()` 19 | 20 | * `Arbiter::handle()` get removed use `Arbiter::spawn()` instead or you can send 21 | `Execute` message to the `System`. 22 | 23 | * `Arbiter::system_arbiter()` get removed use `System::arbiter()` instead. 24 | 25 | * Use `actix::actors::resolver` instead of 26 | `actix::actors::resolver::{Connect, ConnectAddr, Connector, ConnectorError, Resolve};` 27 | 28 | * `actix::actors::resolver::Connector` renamed to `actix::actors::resolver::Resolver` 29 | 30 | * `actix::actors::resolver::ConnectorError` renamed to `actix::actors::resolver::ResolverError` 31 | -------------------------------------------------------------------------------- /examples/ping.rs: -------------------------------------------------------------------------------- 1 | extern crate actix; 2 | extern crate futures; 3 | extern crate tokio; 4 | 5 | use actix::prelude::*; 6 | use futures::Future; 7 | 8 | /// Define `Ping` message 9 | struct Ping(usize); 10 | 11 | impl Message for Ping { 12 | type Result = usize; 13 | } 14 | 15 | /// Actor 16 | struct MyActor { 17 | count: usize, 18 | } 19 | 20 | /// Declare actor and its context 21 | impl Actor for MyActor { 22 | type Context = Context; 23 | } 24 | 25 | /// Handler for `Ping` message 26 | impl Handler for MyActor { 27 | type Result = usize; 28 | 29 | fn handle(&mut self, msg: Ping, _: &mut Context) -> Self::Result { 30 | self.count += msg.0; 31 | self.count 32 | } 33 | } 34 | 35 | fn main() { 36 | // start system, this is required step 37 | System::run(|| { 38 | // start new actor 39 | let addr = MyActor { count: 10 }.start(); 40 | 41 | // send message and get future for result 42 | let res = addr.send(Ping(10)); 43 | 44 | // handle() returns tokio handle 45 | tokio::spawn( 46 | res.map(|res| { 47 | println!("RESULT: {}", res == 20); 48 | 49 | // stop system and exit 50 | System::current().stop(); 51 | }).map_err(|_| ()), 52 | ); 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /src/fut/either.rs: -------------------------------------------------------------------------------- 1 | use futures::Poll; 2 | 3 | use actor::Actor; 4 | use fut::ActorFuture; 5 | 6 | /// Combines two different futures yielding the same item and error 7 | /// types into a single type. 8 | #[derive(Debug)] 9 | pub enum Either { 10 | /// First branch of the type 11 | A(A), 12 | /// Second branch of the type 13 | B(B), 14 | } 15 | 16 | impl Either<(T, A), (T, B)> { 17 | /// Splits out the homogeneous type from an either of tuples. 18 | /// 19 | /// This method is typically useful when combined with the `Future::select2` 20 | /// combinator. 21 | pub fn split(self) -> (T, Either) { 22 | match self { 23 | Either::A((a, b)) => (a, Either::A(b)), 24 | Either::B((a, b)) => (a, Either::B(b)), 25 | } 26 | } 27 | } 28 | 29 | impl ActorFuture for Either 30 | where 31 | A: ActorFuture, 32 | B: ActorFuture, 33 | { 34 | type Item = A::Item; 35 | type Error = A::Error; 36 | type Actor = A::Actor; 37 | 38 | fn poll( 39 | &mut self, act: &mut A::Actor, ctx: &mut ::Context, 40 | ) -> Poll { 41 | match *self { 42 | Either::A(ref mut a) => a.poll(act, ctx), 43 | Either::B(ref mut b) => b.poll(act, ctx), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/test_execute.rs: -------------------------------------------------------------------------------- 1 | extern crate actix; 2 | extern crate futures; 3 | extern crate tokio; 4 | use futures::{future, Future}; 5 | 6 | use actix::msgs::Execute; 7 | use actix::prelude::*; 8 | 9 | #[test] 10 | fn test_execute() { 11 | System::run(move || { 12 | let addr = Arbiter::new("exec-test"); 13 | 14 | tokio::spawn(addr.send(Execute::new(|| Ok(Arbiter::name()))).then( 15 | |res: Result, _>| { 16 | System::current().stop(); 17 | 18 | match res { 19 | Ok(Ok(name)) => assert_ne!(name, "test"), 20 | _ => assert!(false, "something is wrong"), 21 | } 22 | future::result(Ok(())) 23 | }, 24 | )); 25 | }); 26 | } 27 | 28 | #[test] 29 | fn test_system_execute() { 30 | System::run(move || { 31 | let addr = Arbiter::new("exec-test"); 32 | 33 | addr.do_send(Execute::new(|| -> Result<(), ()> { 34 | tokio::spawn(futures::lazy(|| { 35 | System::current().arbiter().do_send(Execute::new( 36 | || -> Result<(), ()> { 37 | System::current().stop(); 38 | Ok(()) 39 | }, 40 | )); 41 | future::ok(()) 42 | })); 43 | Ok(()) 44 | })); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /src/fut/map.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll}; 2 | 3 | use actor::Actor; 4 | use fut::ActorFuture; 5 | 6 | /// Future for the `map` combinator, changing the type of a future. 7 | /// 8 | /// This is created by the `ActorFuture::map` method. 9 | #[derive(Debug)] 10 | #[must_use = "futures do nothing unless polled"] 11 | pub struct Map 12 | where 13 | A: ActorFuture, 14 | { 15 | future: A, 16 | f: Option, 17 | } 18 | 19 | pub fn new(future: A, f: F) -> Map 20 | where 21 | A: ActorFuture, 22 | { 23 | Map { future, f: Some(f) } 24 | } 25 | 26 | impl ActorFuture for Map 27 | where 28 | A: ActorFuture, 29 | F: FnOnce(A::Item, &mut A::Actor, &mut ::Context) -> U, 30 | { 31 | type Item = U; 32 | type Error = A::Error; 33 | type Actor = A::Actor; 34 | 35 | fn poll( 36 | &mut self, act: &mut Self::Actor, ctx: &mut ::Context, 37 | ) -> Poll { 38 | let e = match self.future.poll(act, ctx) { 39 | Ok(Async::NotReady) => return Ok(Async::NotReady), 40 | Ok(Async::Ready(e)) => Ok(e), 41 | Err(e) => Err(e), 42 | }; 43 | match e { 44 | Ok(item) => Ok(Async::Ready(self.f.take().expect("cannot poll Map twice")( 45 | item, act, ctx, 46 | ))), 47 | Err(err) => Err(err), 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/fut/helpers.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Future, Poll, Stream}; 2 | 3 | /// Helper trait that adds the helper method `finish()` to stream objects. 4 | #[doc(hidden)] 5 | pub trait FinishStream: Sized { 6 | fn finish(self) -> Finish; 7 | } 8 | 9 | impl FinishStream for S { 10 | /// A combinator used to convert a stream into a future; the 11 | /// future resolves when the stream completes. 12 | fn finish(self) -> Finish { 13 | Finish::new(self) 14 | } 15 | } 16 | 17 | /// A combinator used to convert a stream into a future; the future 18 | /// resolves when the stream completes. 19 | /// 20 | /// This structure is produced by the `Stream::finish` method. 21 | #[derive(Debug)] 22 | #[must_use = "streams do nothing unless polled"] 23 | pub struct Finish(S); 24 | 25 | impl Finish { 26 | pub fn new(s: S) -> Finish { 27 | Finish(s) 28 | } 29 | } 30 | 31 | impl Future for Finish 32 | where 33 | S: Stream, 34 | { 35 | type Item = (); 36 | type Error = S::Error; 37 | 38 | fn poll(&mut self) -> Poll<(), S::Error> { 39 | loop { 40 | match self.0.poll() { 41 | Ok(Async::NotReady) => return Ok(Async::NotReady), 42 | Ok(Async::Ready(None)) => return Ok(Async::Ready(())), 43 | Ok(Async::Ready(Some(_))) => (), 44 | Err(err) => return Err(err), 45 | }; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/fut/and_then.rs: -------------------------------------------------------------------------------- 1 | use futures::Poll; 2 | 3 | use super::chain::Chain; 4 | use super::{ActorFuture, IntoActorFuture}; 5 | use actor::Actor; 6 | 7 | /// Future for the `and_then` combinator, chaining a computation onto the end of 8 | /// another future which completes successfully. 9 | /// 10 | /// This is created by the `Future::and_then` method. 11 | #[derive(Debug)] 12 | #[must_use = "futures do nothing unless polled"] 13 | pub struct AndThen 14 | where 15 | A: ActorFuture, 16 | B: IntoActorFuture, 17 | { 18 | state: Chain, 19 | } 20 | 21 | pub fn new(future: A, f: F) -> AndThen 22 | where 23 | A: ActorFuture, 24 | B: IntoActorFuture, 25 | { 26 | AndThen { 27 | state: Chain::new(future, f), 28 | } 29 | } 30 | 31 | impl ActorFuture for AndThen 32 | where 33 | A: ActorFuture, 34 | B: IntoActorFuture, 35 | F: FnOnce(A::Item, &mut A::Actor, &mut ::Context) -> B, 36 | { 37 | type Item = B::Item; 38 | type Error = B::Error; 39 | type Actor = A::Actor; 40 | 41 | fn poll( 42 | &mut self, act: &mut A::Actor, ctx: &mut ::Context, 43 | ) -> Poll { 44 | self.state.poll(act, ctx, |result, f, act, ctx| { 45 | result.map(|e| Err(f(e, act, ctx).into_future())) 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/fut/then.rs: -------------------------------------------------------------------------------- 1 | use futures::Poll; 2 | 3 | use actor::Actor; 4 | use fut::chain::Chain; 5 | use fut::{ActorFuture, IntoActorFuture}; 6 | 7 | /// Future for the `then` combinator, chaining computations on the end of 8 | /// another future regardless of its outcome. 9 | /// 10 | /// This is created by the `Future::then` method. 11 | #[derive(Debug)] 12 | #[must_use = "futures do nothing unless polled"] 13 | pub struct Then 14 | where 15 | A: ActorFuture, 16 | B: IntoActorFuture, 17 | { 18 | state: Chain, 19 | } 20 | 21 | pub fn new(future: A, f: F) -> Then 22 | where 23 | A: ActorFuture, 24 | B: IntoActorFuture, 25 | { 26 | Then { 27 | state: Chain::new(future, f), 28 | } 29 | } 30 | 31 | impl ActorFuture for Then 32 | where 33 | A: ActorFuture, 34 | B: IntoActorFuture, 35 | F: FnOnce( 36 | Result, 37 | &mut A::Actor, 38 | &mut ::Context, 39 | ) -> B, 40 | { 41 | type Item = B::Item; 42 | type Error = B::Error; 43 | type Actor = A::Actor; 44 | 45 | fn poll( 46 | &mut self, act: &mut A::Actor, ctx: &mut ::Context, 47 | ) -> Poll { 48 | self.state.poll(act, ctx, |a, f, act, ctx| { 49 | Ok(Err(f(a, act, ctx).into_future())) 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/fut/timeout.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use futures::{Async, Future, Poll}; 4 | use tokio_timer::Delay; 5 | 6 | use actor::Actor; 7 | use clock; 8 | use fut::ActorFuture; 9 | 10 | /// Future for the `timeout` combinator, interrupts computations if it takes 11 | /// more than `timeout`. 12 | /// 13 | /// This is created by the `ActorFuture::timeout()` method. 14 | #[derive(Debug)] 15 | #[must_use = "futures do nothing unless polled"] 16 | pub struct Timeout 17 | where 18 | F: ActorFuture, 19 | { 20 | fut: F, 21 | err: Option, 22 | timeout: Delay, 23 | } 24 | 25 | pub fn new(future: F, timeout: Duration, err: F::Error) -> Timeout 26 | where 27 | F: ActorFuture, 28 | { 29 | Timeout { 30 | fut: future, 31 | err: Some(err), 32 | timeout: Delay::new(clock::now() + timeout), 33 | } 34 | } 35 | 36 | impl ActorFuture for Timeout 37 | where 38 | F: ActorFuture, 39 | { 40 | type Item = F::Item; 41 | type Error = F::Error; 42 | type Actor = F::Actor; 43 | 44 | fn poll( 45 | &mut self, act: &mut F::Actor, ctx: &mut ::Context, 46 | ) -> Poll { 47 | // check timeout 48 | match self.timeout.poll() { 49 | Ok(Async::Ready(())) => return Err(self.err.take().unwrap()), 50 | Ok(Async::NotReady) => (), 51 | Err(_) => unreachable!(), 52 | } 53 | 54 | self.fut.poll(act, ctx) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/fut/stream_map.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll}; 2 | 3 | use actor::Actor; 4 | use fut::ActorStream; 5 | 6 | /// A stream combinator which will change the type of a stream from one 7 | /// type to another. 8 | /// 9 | /// This is produced by the `ActorStream::map` method. 10 | #[derive(Debug)] 11 | #[must_use = "streams do nothing unless polled"] 12 | pub struct StreamMap { 13 | stream: S, 14 | f: F, 15 | } 16 | 17 | pub fn new(stream: S, f: F) -> StreamMap 18 | where 19 | F: FnMut(S::Item, &mut S::Actor, &mut ::Context) -> U, 20 | S: ActorStream, 21 | { 22 | StreamMap { stream, f } 23 | } 24 | 25 | impl ActorStream for StreamMap 26 | where 27 | S: ActorStream, 28 | F: FnMut(S::Item, &mut S::Actor, &mut ::Context) -> U, 29 | { 30 | type Item = U; 31 | type Error = S::Error; 32 | type Actor = S::Actor; 33 | 34 | fn poll( 35 | &mut self, act: &mut Self::Actor, ctx: &mut ::Context, 36 | ) -> Poll, S::Error> { 37 | match self.stream.poll(act, ctx) { 38 | Ok(Async::NotReady) => Ok(Async::NotReady), 39 | Ok(Async::Ready(option)) => { 40 | if let Some(item) = option { 41 | Ok(Async::Ready(Some((self.f)(item, act, ctx)))) 42 | } else { 43 | Ok(Async::Ready(None)) 44 | } 45 | } 46 | Err(e) => Err(e), 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/fibonacci.rs: -------------------------------------------------------------------------------- 1 | //! Example of sync actor. It can be used for cpu bound tasks. Only one sync 2 | //! actor runs within arbiter's thread. Sync actor processes one message at a 3 | //! time. Sync arbiter can start multiple threads with separate instance of 4 | //! actor in each. 5 | 6 | extern crate actix; 7 | extern crate futures; 8 | extern crate tokio; 9 | 10 | use actix::prelude::*; 11 | 12 | struct Fibonacci(pub u32); 13 | 14 | impl Message for Fibonacci { 15 | type Result = Result; 16 | } 17 | 18 | struct SyncActor; 19 | 20 | impl Actor for SyncActor { 21 | type Context = SyncContext; 22 | } 23 | 24 | impl Handler for SyncActor { 25 | type Result = Result; 26 | 27 | fn handle(&mut self, msg: Fibonacci, _: &mut Self::Context) -> Self::Result { 28 | if msg.0 == 0 { 29 | Err(()) 30 | } else if msg.0 == 1 { 31 | Ok(1) 32 | } else { 33 | let mut i = 0; 34 | let mut sum = 0; 35 | let mut last = 0; 36 | let mut curr = 1; 37 | while i < msg.0 - 1 { 38 | sum = last + curr; 39 | last = curr; 40 | curr = sum; 41 | i += 1; 42 | } 43 | Ok(sum) 44 | } 45 | } 46 | } 47 | 48 | fn main() { 49 | System::run(|| { 50 | // start sync arbiter with 3 threads 51 | let addr = SyncArbiter::start(3, || SyncActor); 52 | 53 | // send 5 messages 54 | for n in 5..10 { 55 | addr.do_send(Fibonacci(n)); 56 | } 57 | 58 | tokio::spawn(futures::lazy(|| { 59 | System::current().stop(); 60 | futures::future::result(Ok(())) 61 | })); 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /src/fut/chain.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll}; 2 | use std::mem; 3 | 4 | use actor::Actor; 5 | use fut::ActorFuture; 6 | 7 | #[derive(Debug)] 8 | pub enum Chain 9 | where 10 | A: ActorFuture, 11 | { 12 | First(A, C), 13 | Second(B), 14 | Done, 15 | } 16 | 17 | impl Chain 18 | where 19 | A: ActorFuture, 20 | B: ActorFuture, 21 | { 22 | pub fn new(a: A, c: C) -> Chain { 23 | Chain::First(a, c) 24 | } 25 | 26 | pub fn poll( 27 | &mut self, srv: &mut A::Actor, ctx: &mut ::Context, f: F, 28 | ) -> Poll 29 | where 30 | F: FnOnce( 31 | Result, 32 | C, 33 | &mut A::Actor, 34 | &mut ::Context, 35 | ) -> Result, B::Error>, 36 | { 37 | let a_result = match *self { 38 | Chain::First(ref mut a, _) => match a.poll(srv, ctx) { 39 | Ok(Async::NotReady) => return Ok(Async::NotReady), 40 | Ok(Async::Ready(t)) => Ok(t), 41 | Err(e) => Err(e), 42 | }, 43 | Chain::Second(ref mut b) => return b.poll(srv, ctx), 44 | Chain::Done => panic!("cannot poll a chained future twice"), 45 | }; 46 | let data = match mem::replace(self, Chain::Done) { 47 | Chain::First(_, c) => c, 48 | _ => panic!(), 49 | }; 50 | match f(a_result, data, srv, ctx)? { 51 | Ok(e) => Ok(Async::Ready(e)), 52 | Err(mut b) => { 53 | let ret = b.poll(srv, ctx); 54 | *self = Chain::Second(b); 55 | ret 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | 7 | sudo: required 8 | dist: trusty 9 | 10 | env: 11 | global: 12 | - RUSTFLAGS="-C link-dead-code" 13 | 14 | addons: 15 | apt: 16 | packages: 17 | - libcurl4-openssl-dev 18 | - libelf-dev 19 | - libdw-dev 20 | - cmake 21 | - gcc 22 | - binutils-dev 23 | - libiberty-dev 24 | 25 | # Add clippy 26 | before_script: 27 | - | 28 | if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then 29 | ( ( cargo install clippy && export CLIPPY=true ) || export CLIPPY=false ); 30 | fi 31 | - export PATH=$PATH:~/.cargo/bin 32 | 33 | script: 34 | - | 35 | if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then 36 | USE_SKEPTIC=1 cargo test 37 | else 38 | cargo test 39 | cd examples/chat && cargo check && cd ../.. 40 | fi 41 | - | 42 | if [[ "$TRAVIS_RUST_VERSION" == "nightly" && $CLIPPY ]]; then 43 | cargo clippy 44 | fi 45 | 46 | # Upload docs 47 | after_success: 48 | - | 49 | if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" && "$TRAVIS_RUST_VERSION" == "beta" ]]; then 50 | cargo doc --no-deps && 51 | echo "" > target/doc/index.html && 52 | git clone https://github.com/davisp/ghp-import.git && 53 | ./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://"$GH_TOKEN"@github.com/"$TRAVIS_REPO_SLUG.git" target/doc && 54 | echo "Uploaded documentation" 55 | fi 56 | 57 | - | 58 | if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then 59 | RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install -f cargo-tarpaulin 60 | USE_SKEPTIC=1 cargo tarpaulin --out Xml --no-count 61 | bash <(curl -s https://codecov.io/bash) 62 | echo "Uploaded code coverage" 63 | fi 64 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actix" 3 | version = "0.7.11" 4 | authors = ["Nikolay Kim "] 5 | description = "Actor framework for Rust" 6 | readme = "README.md" 7 | keywords = ["actor", "futures", "actix", "async", "tokio"] 8 | homepage = "https://actix.rs" 9 | repository = "https://github.com/actix/actix.git" 10 | documentation = "https://docs.rs/actix/" 11 | categories = ["network-programming", "asynchronous"] 12 | license = "MIT/Apache-2.0" 13 | exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] 14 | 15 | [badges] 16 | travis-ci = { repository = "actix/actix", branch = "master" } 17 | appveyor = { repository = "fafhrd91/actix-n9e64" } 18 | codecov = { repository = "actix/actix", branch = "master", service = "github" } 19 | 20 | [lib] 21 | name = "actix" 22 | path = "src/lib.rs" 23 | 24 | [workspace] 25 | members = ["examples/chat"] 26 | 27 | [features] 28 | default = ["signal", "resolver"] 29 | 30 | # dns resolver 31 | resolver = ["trust-dns-resolver"] 32 | 33 | # signal handling 34 | signal = ["tokio-signal"] 35 | 36 | # Adds assertion to prevent processing too many messages on event loop 37 | mailbox_assert = [] 38 | 39 | [dependencies] 40 | actix_derive = "0.3" 41 | 42 | # io 43 | bytes = "0.4" 44 | futures = "0.1" 45 | tokio = "0.1.7" 46 | tokio-io = "0.1" 47 | tokio-codec = "0.1" 48 | tokio-executor = "0.1" 49 | tokio-reactor = "0.1" 50 | tokio-tcp = "0.1" 51 | tokio-timer = "0.2" 52 | 53 | # other 54 | log = "0.4" 55 | fnv = "1.0.5" 56 | failure = "0.1.1" 57 | bitflags = "1.0" 58 | smallvec = "0.6" 59 | crossbeam-channel = "0.3" 60 | parking_lot = "0.7" 61 | uuid = { version = "0.7", features = ["v4"] } 62 | 63 | # signal handling 64 | tokio-signal = { version = "0.2", optional = true } 65 | 66 | # dns resolver 67 | trust-dns-resolver = { version = "^0.10.0", optional = true } 68 | 69 | [target.'cfg(unix)'.dependencies] 70 | libc = "0.2" 71 | 72 | [profile.release] 73 | lto = true 74 | opt-level = 3 75 | codegen-units = 1 76 | -------------------------------------------------------------------------------- /src/fut/stream_timeout.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use futures::{Async, Future, Poll}; 4 | use tokio_timer::Delay; 5 | 6 | use actor::Actor; 7 | use clock; 8 | use fut::ActorStream; 9 | 10 | /// Future for the `timeout` combinator, interrupts computations if it takes 11 | /// more than `timeout`. 12 | /// 13 | /// This is created by the `ActorFuture::timeout()` method. 14 | #[derive(Debug)] 15 | #[must_use = "streams do nothing unless polled"] 16 | pub struct StreamTimeout 17 | where 18 | S: ActorStream, 19 | { 20 | stream: S, 21 | err: S::Error, 22 | dur: Duration, 23 | timeout: Option, 24 | } 25 | 26 | pub fn new(stream: S, timeout: Duration, err: S::Error) -> StreamTimeout 27 | where 28 | S: ActorStream, 29 | S::Error: Clone, 30 | { 31 | StreamTimeout { 32 | stream, 33 | err, 34 | dur: timeout, 35 | timeout: None, 36 | } 37 | } 38 | 39 | impl ActorStream for StreamTimeout 40 | where 41 | S: ActorStream, 42 | S::Error: Clone, 43 | { 44 | type Item = S::Item; 45 | type Error = S::Error; 46 | type Actor = S::Actor; 47 | 48 | fn poll( 49 | &mut self, act: &mut S::Actor, ctx: &mut ::Context, 50 | ) -> Poll, S::Error> { 51 | match self.stream.poll(act, ctx) { 52 | Ok(Async::Ready(res)) => { 53 | self.timeout.take(); 54 | return Ok(Async::Ready(res)); 55 | } 56 | Ok(Async::NotReady) => (), 57 | Err(err) => return Err(err), 58 | } 59 | 60 | if self.timeout.is_none() { 61 | self.timeout = Some(Delay::new(clock::now() + self.dur)); 62 | } 63 | 64 | // check timeout 65 | match self.timeout.as_mut().unwrap().poll() { 66 | Ok(Async::Ready(())) => (), 67 | Ok(Async::NotReady) => return Ok(Async::NotReady), 68 | Err(_) => unreachable!(), 69 | } 70 | self.timeout.take(); 71 | 72 | Err(self.err.clone()) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/fut/stream_and_then.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll}; 2 | 3 | use actor::Actor; 4 | use fut::{ActorFuture, ActorStream, IntoActorFuture}; 5 | 6 | /// A stream combinator which chains a computation onto values produced by a 7 | /// stream. 8 | /// 9 | /// This structure is produced by the `ActorStream::and_then` method. 10 | #[derive(Debug)] 11 | #[must_use = "streams do nothing unless polled"] 12 | pub struct StreamAndThen 13 | where 14 | U: IntoActorFuture, 15 | { 16 | stream: S, 17 | future: Option, 18 | f: F, 19 | } 20 | 21 | pub fn new(stream: S, f: F) -> StreamAndThen 22 | where 23 | S: ActorStream, 24 | F: FnMut(S::Item, &mut S::Actor, &mut ::Context) -> U, 25 | U: IntoActorFuture, 26 | { 27 | StreamAndThen { 28 | stream, 29 | f, 30 | future: None, 31 | } 32 | } 33 | 34 | impl ActorStream for StreamAndThen 35 | where 36 | S: ActorStream, 37 | F: FnMut(S::Item, &mut S::Actor, &mut ::Context) -> U, 38 | U: IntoActorFuture, 39 | { 40 | type Item = U::Item; 41 | type Error = S::Error; 42 | type Actor = S::Actor; 43 | 44 | fn poll( 45 | &mut self, act: &mut S::Actor, ctx: &mut ::Context, 46 | ) -> Poll, S::Error> { 47 | if self.future.is_none() { 48 | let item = match try_ready!(self.stream.poll(act, ctx)) { 49 | None => return Ok(Async::Ready(None)), 50 | Some(e) => e, 51 | }; 52 | self.future = Some((self.f)(item, act, ctx).into_future()); 53 | } 54 | assert!(self.future.is_some()); 55 | match self.future.as_mut().unwrap().poll(act, ctx) { 56 | Ok(Async::Ready(e)) => { 57 | self.future = None; 58 | Ok(Async::Ready(Some(e))) 59 | } 60 | Err(e) => { 61 | self.future = None; 62 | Err(e) 63 | } 64 | Ok(Async::NotReady) => Ok(Async::NotReady), 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/fut/map_err.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll}; 2 | 3 | use actor::Actor; 4 | use fut::ActorFuture; 5 | 6 | /// Future for the `map_err` combinator, changing the error type of a future. 7 | /// 8 | /// This is created by the `Future::map_err` method. 9 | #[derive(Debug)] 10 | #[must_use = "futures do nothing unless polled"] 11 | pub struct MapErr 12 | where 13 | A: ActorFuture, 14 | { 15 | future: A, 16 | f: Option, 17 | } 18 | 19 | pub fn new(future: A, f: F) -> MapErr 20 | where 21 | A: ActorFuture, 22 | { 23 | MapErr { future, f: Some(f) } 24 | } 25 | 26 | impl ActorFuture for MapErr 27 | where 28 | A: ActorFuture, 29 | F: FnOnce(A::Error, &mut A::Actor, &mut ::Context) -> U, 30 | { 31 | type Item = A::Item; 32 | type Error = U; 33 | type Actor = A::Actor; 34 | 35 | fn poll( 36 | &mut self, act: &mut A::Actor, ctx: &mut ::Context, 37 | ) -> Poll { 38 | let e = match self.future.poll(act, ctx) { 39 | Ok(Async::NotReady) => return Ok(Async::NotReady), 40 | other => other, 41 | }; 42 | match e { 43 | Err(e) => Err(self.f.take().expect("cannot poll MapErr twice")( 44 | e, act, ctx, 45 | )), 46 | Ok(err) => Ok(err), 47 | } 48 | } 49 | } 50 | 51 | pub struct DropErr 52 | where 53 | A: ActorFuture, 54 | { 55 | future: A, 56 | } 57 | 58 | impl DropErr 59 | where 60 | A: ActorFuture, 61 | { 62 | pub(crate) fn new(future: A) -> DropErr { 63 | DropErr { future } 64 | } 65 | } 66 | 67 | impl ActorFuture for DropErr 68 | where 69 | A: ActorFuture, 70 | { 71 | type Item = A::Item; 72 | type Error = (); 73 | type Actor = A::Actor; 74 | 75 | fn poll( 76 | &mut self, act: &mut A::Actor, ctx: &mut ::Context, 77 | ) -> Poll { 78 | match self.future.poll(act, ctx) { 79 | Ok(Async::Ready(item)) => Ok(Async::Ready(item)), 80 | Ok(Async::NotReady) => Ok(Async::NotReady), 81 | Err(_) => Err(()), 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/test_supervisor.rs: -------------------------------------------------------------------------------- 1 | extern crate actix; 2 | extern crate futures; 3 | extern crate tokio; 4 | extern crate tokio_timer; 5 | 6 | use std::sync::atomic::{AtomicUsize, Ordering}; 7 | use std::sync::{Arc, Mutex}; 8 | use std::time::{Duration, Instant}; 9 | 10 | use actix::prelude::*; 11 | use futures::{future, Future}; 12 | use tokio_timer::Delay; 13 | 14 | struct Die; 15 | 16 | impl Message for Die { 17 | type Result = (); 18 | } 19 | 20 | struct MyActor(Arc, Arc, Arc); 21 | 22 | impl Actor for MyActor { 23 | type Context = Context; 24 | 25 | fn started(&mut self, _: &mut Context) { 26 | self.0.fetch_add(1, Ordering::Relaxed); 27 | } 28 | } 29 | impl actix::Supervised for MyActor { 30 | fn restarting(&mut self, _: &mut actix::Context) { 31 | self.1.fetch_add(1, Ordering::Relaxed); 32 | } 33 | } 34 | 35 | impl actix::Handler for MyActor { 36 | type Result = (); 37 | 38 | fn handle(&mut self, _: Die, ctx: &mut actix::Context) { 39 | self.2.fetch_add(1, Ordering::Relaxed); 40 | ctx.stop(); 41 | } 42 | } 43 | 44 | #[test] 45 | fn test_supervisor_restart() { 46 | let starts = Arc::new(AtomicUsize::new(0)); 47 | let restarts = Arc::new(AtomicUsize::new(0)); 48 | let messages = Arc::new(AtomicUsize::new(0)); 49 | let starts2 = Arc::clone(&starts); 50 | let restarts2 = Arc::clone(&restarts); 51 | let messages2 = Arc::clone(&messages); 52 | 53 | let addr = Arc::new(Mutex::new(None)); 54 | let addr2 = Arc::clone(&addr); 55 | 56 | System::run(move || { 57 | let addr = 58 | actix::Supervisor::start(move |_| MyActor(starts2, restarts2, messages2)); 59 | addr.do_send(Die); 60 | addr.do_send(Die); 61 | *addr2.lock().unwrap() = Some(addr); 62 | 63 | actix::spawn(Delay::new(Instant::now() + Duration::new(0, 100_000)).then( 64 | |_| { 65 | System::current().stop(); 66 | future::result(Ok(())) 67 | }, 68 | )); 69 | }); 70 | 71 | assert_eq!(starts.load(Ordering::Relaxed), 3); 72 | assert_eq!(restarts.load(Ordering::Relaxed), 2); 73 | assert_eq!(messages.load(Ordering::Relaxed), 2); 74 | } 75 | -------------------------------------------------------------------------------- /src/fut/stream_then.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll}; 2 | 3 | use actor::Actor; 4 | use fut::{ActorFuture, ActorStream, IntoActorFuture}; 5 | 6 | /// A stream combinator which chains a computation onto each item produced by a 7 | /// stream. 8 | /// 9 | /// This structure is produced by the `ActorStream::then` method. 10 | #[derive(Debug)] 11 | #[must_use = "streams do nothing unless polled"] 12 | pub struct StreamThen 13 | where 14 | U: IntoActorFuture, 15 | { 16 | stream: S, 17 | future: Option, 18 | f: F, 19 | } 20 | 21 | pub fn new(stream: S, f: F) -> StreamThen 22 | where 23 | S: ActorStream, 24 | F: FnMut( 25 | Result, 26 | &mut S::Actor, 27 | &mut ::Context, 28 | ) -> U, 29 | U: IntoActorFuture, 30 | { 31 | StreamThen { 32 | stream, 33 | f, 34 | future: None, 35 | } 36 | } 37 | 38 | impl ActorStream for StreamThen 39 | where 40 | S: ActorStream, 41 | F: FnMut( 42 | Result, 43 | &mut S::Actor, 44 | &mut ::Context, 45 | ) -> U, 46 | U: IntoActorFuture, 47 | { 48 | type Item = U::Item; 49 | type Error = U::Error; 50 | type Actor = S::Actor; 51 | 52 | fn poll( 53 | &mut self, act: &mut S::Actor, ctx: &mut ::Context, 54 | ) -> Poll, U::Error> { 55 | if self.future.is_none() { 56 | let item = match self.stream.poll(act, ctx) { 57 | Ok(Async::NotReady) => return Ok(Async::NotReady), 58 | Ok(Async::Ready(None)) => return Ok(Async::Ready(None)), 59 | Ok(Async::Ready(Some(e))) => Ok(e), 60 | Err(e) => Err(e), 61 | }; 62 | self.future = Some((self.f)(item, act, ctx).into_future()); 63 | } 64 | assert!(self.future.is_some()); 65 | match self.future.as_mut().unwrap().poll(act, ctx) { 66 | Ok(Async::Ready(e)) => { 67 | self.future = None; 68 | Ok(Async::Ready(Some(e))) 69 | } 70 | Err(e) => { 71 | self.future = None; 72 | Err(e) 73 | } 74 | Ok(Async::NotReady) => Ok(Async::NotReady), 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/test_actors.rs: -------------------------------------------------------------------------------- 1 | extern crate actix; 2 | extern crate futures; 3 | extern crate tokio; 4 | 5 | use std::time::Duration; 6 | 7 | use actix::actors::{resolver, signal}; 8 | use actix::prelude::*; 9 | use futures::Future; 10 | 11 | #[test] 12 | fn test_resolver() { 13 | System::run(|| { 14 | Arbiter::spawn({ 15 | let resolver = System::current().registry().get::(); 16 | resolver 17 | .send(resolver::Resolve::host("localhost")) 18 | .then(|_| { 19 | System::current().stop(); 20 | Ok::<_, ()>(()) 21 | }) 22 | }); 23 | 24 | Arbiter::spawn({ 25 | let resolver = System::current().registry().get::(); 26 | resolver 27 | .send(resolver::Connect::host("localhost:5000")) 28 | .then(|_| Ok::<_, ()>(())) 29 | }); 30 | }); 31 | } 32 | 33 | #[test] 34 | #[cfg(unix)] 35 | fn test_signal() { 36 | System::run(|| { 37 | let _addr = signal::DefaultSignalsHandler::start_default(); 38 | let sig = System::current().registry().get::(); 39 | 40 | // send SIGTERM 41 | std::thread::spawn(move || { 42 | // we need this because DefaultSignalsHandler starts a bit later 43 | std::thread::sleep(Duration::from_millis(100)); 44 | 45 | // emulate SIGNTERM 46 | sig.do_send(signal::SignalType::Quit); 47 | }); 48 | }); 49 | } 50 | 51 | #[test] 52 | #[cfg(unix)] 53 | fn test_signal_term() { 54 | System::run(|| { 55 | let _addr = signal::DefaultSignalsHandler::start_default(); 56 | Arbiter::spawn(futures::lazy(move || { 57 | let sig = System::current().registry().get::(); 58 | sig.do_send(signal::SignalType::Term); 59 | Ok(()) 60 | })); 61 | }); 62 | } 63 | 64 | #[test] 65 | #[cfg(unix)] 66 | fn test_signal_int() { 67 | System::run(|| { 68 | let _addr = signal::DefaultSignalsHandler::start_default(); 69 | Arbiter::spawn(futures::lazy(move || { 70 | let sig = System::current().registry().get::(); 71 | sig.do_send(signal::SignalType::Hup); 72 | sig.do_send(signal::SignalType::Int); 73 | Ok(()) 74 | })); 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /tests/test_fut.rs: -------------------------------------------------------------------------------- 1 | extern crate actix; 2 | extern crate futures; 3 | extern crate tokio_timer; 4 | 5 | use std::sync::atomic::{AtomicBool, Ordering}; 6 | use std::sync::Arc; 7 | use std::time::{Duration, Instant}; 8 | 9 | use actix::prelude::*; 10 | use futures::stream::futures_ordered; 11 | use futures::{Future, Stream}; 12 | use tokio_timer::Delay; 13 | 14 | struct MyActor { 15 | timeout: Arc, 16 | } 17 | 18 | #[derive(PartialEq, Copy, Clone)] 19 | enum Error { 20 | Timeout, 21 | Generic, 22 | } 23 | 24 | impl Actor for MyActor { 25 | type Context = actix::Context; 26 | 27 | fn started(&mut self, ctx: &mut Self::Context) { 28 | Delay::new(Instant::now() + Duration::new(0, 5_000_000)) 29 | .then(|_| { 30 | System::current().stop(); 31 | Ok::<_, Error>(()) 32 | }) 33 | .into_actor(self) 34 | .timeout(Duration::new(0, 100), Error::Timeout) 35 | .map_err(|e, act, _| { 36 | if e == Error::Timeout { 37 | act.timeout.store(true, Ordering::Relaxed); 38 | System::current().stop(); 39 | () 40 | } 41 | }) 42 | .wait(ctx) 43 | } 44 | } 45 | 46 | #[test] 47 | fn test_fut_timeout() { 48 | let timeout = Arc::new(AtomicBool::new(false)); 49 | let timeout2 = Arc::clone(&timeout); 50 | 51 | System::run(move || { 52 | let _addr = MyActor { timeout: timeout2 }.start(); 53 | }); 54 | 55 | assert!(timeout.load(Ordering::Relaxed), "Not timeout"); 56 | } 57 | 58 | struct MyStreamActor { 59 | timeout: Arc, 60 | } 61 | 62 | impl Actor for MyStreamActor { 63 | type Context = actix::Context; 64 | 65 | fn started(&mut self, ctx: &mut Self::Context) { 66 | let s = futures_ordered(vec![ 67 | Delay::new(Instant::now() + Duration::new(0, 5_000_000)), 68 | Delay::new(Instant::now() + Duration::new(0, 5_000_000)), 69 | ]); 70 | 71 | s.map_err(|_| Error::Generic) 72 | .into_actor(self) 73 | .timeout(Duration::new(0, 1000), Error::Timeout) 74 | .map_err(|e, act, _| { 75 | if e == Error::Timeout { 76 | act.timeout.store(true, Ordering::Relaxed); 77 | System::current().stop(); 78 | } 79 | }) 80 | .finish() 81 | .wait(ctx) 82 | } 83 | } 84 | 85 | #[test] 86 | fn test_stream_timeout() { 87 | let timeout = Arc::new(AtomicBool::new(false)); 88 | let timeout2 = Arc::clone(&timeout); 89 | 90 | System::run(|| { 91 | let _addr = MyStreamActor { timeout: timeout2 }.start(); 92 | }); 93 | 94 | assert!(timeout.load(Ordering::Relaxed), "Not timeout"); 95 | } 96 | -------------------------------------------------------------------------------- /tests/test_arbiter.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate actix; 3 | extern crate tokio; 4 | 5 | use actix::prelude::*; 6 | use futures::Future; 7 | use std::sync::atomic::{AtomicUsize, Ordering}; 8 | use std::sync::Arc; 9 | 10 | #[derive(Debug, Message)] 11 | struct Panic(); 12 | 13 | #[derive(Debug, Message)] 14 | struct Ping(usize); 15 | 16 | struct MyActor(Arc); 17 | 18 | impl Actor for MyActor { 19 | type Context = Context; 20 | } 21 | 22 | impl Handler for MyActor { 23 | type Result = (); 24 | 25 | fn handle(&mut self, _: Ping, _: &mut actix::Context) { 26 | self.0 27 | .store(self.0.load(Ordering::Relaxed) + 1, Ordering::Relaxed); 28 | System::current().stop(); 29 | } 30 | } 31 | 32 | impl Handler for MyActor { 33 | type Result = (); 34 | 35 | fn handle(&mut self, _: Panic, _: &mut actix::Context) { 36 | panic!("Whoops!"); 37 | } 38 | } 39 | 40 | #[test] 41 | fn test_start_actor() { 42 | let count = Arc::new(AtomicUsize::new(0)); 43 | let act_count = Arc::clone(&count); 44 | 45 | System::run(move || { 46 | let addr = Arbiter::start(move |_| MyActor(act_count)); 47 | 48 | addr.do_send(Ping(1)); 49 | }); 50 | 51 | assert_eq!(count.load(Ordering::Relaxed), 1); 52 | } 53 | 54 | #[test] 55 | fn test_start_actor_builder() { 56 | let count = Arc::new(AtomicUsize::new(0)); 57 | let act_count = Arc::clone(&count); 58 | 59 | System::run(move || { 60 | let addr = Arbiter::builder().start(move |_| MyActor(act_count)); 61 | 62 | addr.do_send(Ping(1)); 63 | }); 64 | 65 | assert_eq!(count.load(Ordering::Relaxed), 1); 66 | } 67 | 68 | #[test] 69 | fn test_panic_stops_system() { 70 | let count = Arc::new(AtomicUsize::new(0)); 71 | let act_count = Arc::clone(&count); 72 | 73 | let return_code = System::run(move || { 74 | let addr = Arbiter::builder() 75 | .stop_system_on_panic(true) 76 | .start(move |_| MyActor(act_count)); 77 | 78 | addr.do_send(Panic()); 79 | }); 80 | 81 | assert_eq!(return_code, 1); 82 | } 83 | 84 | #[test] 85 | fn test_start_actor_message() { 86 | let count = Arc::new(AtomicUsize::new(0)); 87 | let act_count = Arc::clone(&count); 88 | 89 | System::run(move || { 90 | let arbiter = Arbiter::new("test2"); 91 | 92 | tokio::spawn( 93 | arbiter 94 | .send(actix::msgs::StartActor::new(move |_| MyActor(act_count))) 95 | .then(|res| { 96 | res.unwrap().do_send(Ping(1)); 97 | Ok(()) 98 | }), 99 | ); 100 | }); 101 | 102 | assert_eq!(count.load(Ordering::Relaxed), 1); 103 | } 104 | -------------------------------------------------------------------------------- /examples/ring.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Ring benchmark inspired by Programming Erlang: Software for a 3 | * Concurrent World, by Joe Armstrong, Chapter 8.11.2 4 | * 5 | * "Write a ring benchmark. Create N processes in a ring. Send a 6 | * message round the ring M times so that a total of N * M messages 7 | * get sent. Time how long this takes for different values of N and M." 8 | */ 9 | extern crate actix; 10 | extern crate futures; 11 | 12 | use actix::*; 13 | 14 | use std::time::SystemTime; 15 | use std::env; 16 | 17 | /// A payload with a counter 18 | struct Payload(usize); 19 | 20 | impl Message for Payload { 21 | type Result = (); 22 | } 23 | 24 | struct Node { 25 | limit: usize, 26 | next: Recipient, 27 | } 28 | 29 | impl Actor for Node { 30 | type Context = Context; 31 | } 32 | 33 | impl Handler for Node { 34 | type Result = (); 35 | 36 | fn handle(&mut self, msg: Payload, _: &mut Context) { 37 | if msg.0 >= self.limit { 38 | println!("Reached limit of {} (payload was {})", self.limit, msg.0); 39 | System::current().stop(); 40 | return; 41 | } 42 | self.next.do_send(Payload(msg.0 + 1)).expect("Unable to send payload"); 43 | } 44 | } 45 | 46 | fn print_usage_and_exit() -> ! { 47 | eprintln!("Usage; actix-test "); 48 | ::std::process::exit(1); 49 | } 50 | 51 | fn main() { 52 | let args = env::args().collect::>(); 53 | if args.len() < 3 { 54 | print_usage_and_exit(); 55 | } 56 | let n_nodes = if let Ok(arg_num_nodes) = args[1].parse::() { 57 | if arg_num_nodes <= 1 { 58 | eprintln!("Number of nodes must be > 1"); 59 | ::std::process::exit(1); 60 | } 61 | arg_num_nodes 62 | } else { 63 | print_usage_and_exit(); 64 | }; 65 | 66 | let n_times = if let Ok(arg_ntimes) = args[2].parse::() { 67 | arg_ntimes 68 | } else { 69 | print_usage_and_exit() 70 | }; 71 | 72 | let system = System::new("test"); 73 | 74 | println!("Setting up nodes"); 75 | let _ = Node::create(move|ctx| { 76 | let first_addr = ctx.address(); 77 | let mut prev_addr = Node{limit: n_nodes * n_times, next: first_addr.recipient()}.start(); 78 | prev_addr.do_send(Payload(0)); 79 | 80 | for _ in 2..n_nodes { 81 | prev_addr = Node{limit: n_nodes * n_times, next: prev_addr.recipient()}.start(); 82 | } 83 | 84 | Node{limit: n_nodes * n_times, next: prev_addr.recipient()} 85 | }); 86 | 87 | let now = SystemTime::now(); 88 | system.run(); 89 | match now.elapsed() { 90 | Ok(elapsed) => println!("Time taken: {}.{:06} seconds", elapsed.as_secs(), elapsed.subsec_micros()), 91 | Err(e) => println!("An error occured: {:?}", e) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/actors/mocker.rs: -------------------------------------------------------------------------------- 1 | //! Mocking utility actor. 2 | //! 3 | //! This actor wraps any actor, and replaces instances of that actor with 4 | //! mocker actor, which is able to accept all messages which the actor can 5 | //! receive. 6 | //! 7 | //! Mocking is intended to be achieved by using a pattern similar to 8 | //! ```rust,ignore 9 | //! #[cfg(not(test))] 10 | //! type DBClientAct = DBClientActor; 11 | //! #[cfg(test)] 12 | //! type DBClientAct = Mocker; 13 | //! ``` 14 | //! Then, the actor should be used as a system service (or arbiter service, but 15 | //! take care that all the places which will use the mocked actor are on the 16 | //! same arbiter). Thus, in a test, it will retrieve the mocker from the 17 | //! registry instead of the actual actor. 18 | //! 19 | //! To set the mock function in the actor, the `init_actor` function 20 | //! is used, which allows the state of an actor to be set when it is 21 | //! started as an arbiter or system service. A closure which takes 22 | //! `Box` is evaluated for every message, and must return 23 | //! `Box`, specifically the return type for the message type 24 | //! send. 25 | //! 26 | //! See the mock example to see how it can be used. 27 | 28 | use std::any::Any; 29 | use std::marker::PhantomData; 30 | use std::mem; 31 | 32 | use handler::MessageResponse; 33 | use prelude::*; 34 | 35 | /// This actor is able to wrap another actor and accept all the messages the 36 | /// wrapped actor can, passing it to a closure which can mock the response of 37 | /// the actor. 38 | pub struct Mocker { 39 | phantom: PhantomData, 40 | mock: Box, &mut Context>) -> Box>, 41 | } 42 | 43 | impl Mocker { 44 | pub fn mock( 45 | mock: Box, &mut Context>) -> Box>, 46 | ) -> Mocker { 47 | Mocker:: { 48 | phantom: PhantomData, 49 | mock, 50 | } 51 | } 52 | } 53 | 54 | impl SystemService for Mocker {} 55 | impl ArbiterService for Mocker {} 56 | impl Supervised for Mocker {} 57 | 58 | impl Default for Mocker { 59 | fn default() -> Self { 60 | panic!("Mocker actor used before set") 61 | } 62 | } 63 | 64 | impl Actor for Mocker { 65 | type Context = Context; 66 | } 67 | 68 | impl Handler for Mocker 69 | where 70 | M: Message, 71 | ::Result: MessageResponse, M>, 72 | { 73 | type Result = M::Result; 74 | fn handle(&mut self, msg: M, ctx: &mut Self::Context) -> M::Result { 75 | let mut ret = (self.mock)(Box::new(msg), ctx); 76 | let out = mem::replace( 77 | ret.downcast_mut::>() 78 | .expect("wrong return type for message"), 79 | None, 80 | ); 81 | match out { 82 | Some(a) => a, 83 | _ => panic!(), 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/test_sync.rs: -------------------------------------------------------------------------------- 1 | extern crate actix; 2 | extern crate futures; 3 | extern crate tokio_timer; 4 | 5 | use std::sync::atomic::{AtomicUsize, Ordering}; 6 | use std::sync::{Arc, Condvar, Mutex}; 7 | 8 | use actix::prelude::*; 9 | 10 | struct Fibonacci(pub u32); 11 | 12 | impl Message for Fibonacci { 13 | type Result = Result; 14 | } 15 | 16 | struct SyncActor { 17 | cond: Arc, 18 | cond_l: Arc>, 19 | counter: Arc, 20 | messages: Arc, 21 | } 22 | 23 | impl Actor for SyncActor { 24 | type Context = SyncContext; 25 | 26 | fn started(&mut self, _: &mut Self::Context) { 27 | let old = self.counter.fetch_add(1, Ordering::Relaxed); 28 | if old == 1 { 29 | *self.cond_l.lock().unwrap() = true; 30 | self.cond.notify_one(); 31 | } 32 | } 33 | } 34 | 35 | impl Handler for SyncActor { 36 | type Result = Result; 37 | 38 | fn handle(&mut self, msg: Fibonacci, _: &mut Self::Context) -> Self::Result { 39 | let old = self.messages.fetch_add(1, Ordering::Relaxed); 40 | if old == 4 { 41 | System::current().stop(); 42 | } 43 | 44 | if msg.0 == 0 { 45 | Err(()) 46 | } else if msg.0 == 1 { 47 | Ok(1) 48 | } else { 49 | let mut i = 0; 50 | let mut sum = 0; 51 | let mut last = 0; 52 | let mut curr = 1; 53 | while i < msg.0 - 1 { 54 | sum = last + curr; 55 | last = curr; 56 | curr = sum; 57 | i += 1; 58 | } 59 | Ok(sum) 60 | } 61 | } 62 | } 63 | 64 | #[test] 65 | #[cfg_attr(feature = "cargo-clippy", allow(mutex_atomic))] 66 | fn test_sync() { 67 | let l = Arc::new(Mutex::new(false)); 68 | let cond = Arc::new(Condvar::new()); 69 | let counter = Arc::new(AtomicUsize::new(0)); 70 | let messages = Arc::new(AtomicUsize::new(0)); 71 | 72 | let cond_c = Arc::clone(&cond); 73 | let cond_l_c = Arc::clone(&l); 74 | let counter_c = Arc::clone(&counter); 75 | let messages_c = Arc::clone(&messages); 76 | 77 | System::run(move || { 78 | let addr = SyncArbiter::start(2, move || SyncActor { 79 | cond: Arc::clone(&cond_c), 80 | cond_l: Arc::clone(&cond_l_c), 81 | counter: Arc::clone(&counter_c), 82 | messages: Arc::clone(&messages_c), 83 | }); 84 | 85 | let mut started = l.lock().unwrap(); 86 | while !*started { 87 | started = cond.wait(started).unwrap(); 88 | } 89 | 90 | for n in 5..10 { 91 | addr.do_send(Fibonacci(n)); 92 | } 93 | }); 94 | 95 | assert_eq!(counter.load(Ordering::Relaxed), 2, "Not started"); 96 | assert_eq!( 97 | messages.load(Ordering::Relaxed), 98 | 5, 99 | "Wrong number of messages" 100 | ); 101 | } 102 | -------------------------------------------------------------------------------- /src/fut/stream_fold.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll}; 2 | use std::mem; 3 | 4 | use actor::Actor; //{Future, Poll, IntoFuture, Async}; 5 | use fut::{ActorFuture, ActorStream, IntoActorFuture}; 6 | 7 | /// A future used to collect all the results of a stream into one generic type. 8 | /// 9 | /// This future is returned by the `ActorStream::fold` method. 10 | #[derive(Debug)] 11 | #[must_use = "streams do nothing unless polled"] 12 | pub struct StreamFold 13 | where 14 | Fut: IntoActorFuture, 15 | { 16 | stream: S, 17 | f: F, 18 | state: State, 19 | } 20 | 21 | #[derive(Debug)] 22 | enum State 23 | where 24 | F: ActorFuture, 25 | { 26 | /// Placeholder state when doing work 27 | Empty, 28 | 29 | /// Ready to process the next stream item; current accumulator is the `T` 30 | Ready(T), 31 | 32 | /// Working on a future the process the previous stream item 33 | Processing(F), 34 | } 35 | 36 | pub fn new(stream: S, f: F, t: T) -> StreamFold 37 | where 38 | S: ActorStream, 39 | F: FnMut(T, S::Item, &mut S::Actor, &mut ::Context) -> Fut, 40 | Fut: IntoActorFuture, 41 | S::Error: From, 42 | { 43 | StreamFold { 44 | stream, 45 | f, 46 | state: State::Ready(t), 47 | } 48 | } 49 | 50 | impl ActorFuture for StreamFold 51 | where 52 | S: ActorStream, 53 | F: FnMut(T, S::Item, &mut S::Actor, &mut ::Context) -> Fut, 54 | Fut: IntoActorFuture, 55 | S::Error: From, 56 | { 57 | type Item = T; 58 | type Error = S::Error; 59 | type Actor = S::Actor; 60 | 61 | fn poll( 62 | &mut self, act: &mut S::Actor, ctx: &mut ::Context, 63 | ) -> Poll { 64 | loop { 65 | match mem::replace(&mut self.state, State::Empty) { 66 | State::Empty => panic!("cannot poll Fold twice"), 67 | State::Ready(state) => match self.stream.poll(act, ctx)? { 68 | Async::Ready(Some(e)) => { 69 | let future = (self.f)(state, e, act, ctx); 70 | let future = future.into_future(); 71 | self.state = State::Processing(future); 72 | } 73 | Async::Ready(None) => return Ok(Async::Ready(state)), 74 | Async::NotReady => { 75 | self.state = State::Ready(state); 76 | return Ok(Async::NotReady); 77 | } 78 | }, 79 | State::Processing(mut fut) => match fut.poll(act, ctx)? { 80 | Async::Ready(state) => self.state = State::Ready(state), 81 | Async::NotReady => { 82 | self.state = State::Processing(fut); 83 | return Ok(Async::NotReady); 84 | } 85 | }, 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /examples/chat/src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "cargo-clippy", allow(let_unit_value))] 2 | extern crate byteorder; 3 | extern crate bytes; 4 | extern crate futures; 5 | extern crate rand; 6 | extern crate serde; 7 | extern crate serde_json; 8 | extern crate tokio; 9 | extern crate tokio_io; 10 | extern crate tokio_tcp; 11 | #[macro_use] 12 | extern crate serde_derive; 13 | 14 | #[macro_use] 15 | extern crate actix; 16 | 17 | use std::net; 18 | use std::str::FromStr; 19 | 20 | use actix::prelude::*; 21 | use futures::Stream; 22 | use tokio_io::codec::FramedRead; 23 | use tokio_io::AsyncRead; 24 | use tokio_tcp::{TcpListener, TcpStream}; 25 | 26 | mod codec; 27 | mod server; 28 | mod session; 29 | 30 | use codec::ChatCodec; 31 | use server::ChatServer; 32 | use session::ChatSession; 33 | 34 | /// Define tcp server that will accept incoming tcp connection and create 35 | /// chat actors. 36 | struct Server { 37 | chat: Addr, 38 | } 39 | 40 | /// Make actor from `Server` 41 | impl Actor for Server { 42 | /// Every actor has to provide execution `Context` in which it can run. 43 | type Context = Context; 44 | } 45 | 46 | #[derive(Message)] 47 | struct TcpConnect(pub TcpStream, pub net::SocketAddr); 48 | 49 | /// Handle stream of TcpStream's 50 | impl Handler for Server { 51 | /// this is response for message, which is defined by `ResponseType` trait 52 | /// in this case we just return unit. 53 | type Result = (); 54 | 55 | fn handle(&mut self, msg: TcpConnect, _: &mut Context) { 56 | // For each incoming connection we create `ChatSession` actor 57 | // with out chat server address. 58 | let server = self.chat.clone(); 59 | ChatSession::create(move |ctx| { 60 | let (r, w) = msg.0.split(); 61 | ChatSession::add_stream(FramedRead::new(r, ChatCodec), ctx); 62 | ChatSession::new(server, actix::io::FramedWrite::new(w, ChatCodec, ctx)) 63 | }); 64 | } 65 | } 66 | 67 | fn main() { 68 | actix::System::run(|| { 69 | // Start chat server actor 70 | let server = ChatServer::default().start(); 71 | 72 | // Create server listener 73 | let addr = net::SocketAddr::from_str("127.0.0.1:12345").unwrap(); 74 | let listener = TcpListener::bind(&addr).unwrap(); 75 | 76 | // Our chat server `Server` is an actor, first we need to start it 77 | // and then add stream on incoming tcp connections to it. 78 | // TcpListener::incoming() returns stream of the (TcpStream, net::SocketAddr) 79 | // items So to be able to handle this events `Server` actor has to implement 80 | // stream handler `StreamHandler<(TcpStream, net::SocketAddr), io::Error>` 81 | Server::create(|ctx| { 82 | ctx.add_message_stream(listener.incoming().map_err(|_| ()).map(|st| { 83 | let addr = st.peer_addr().unwrap(); 84 | TcpConnect(st, addr) 85 | })); 86 | Server { chat: server } 87 | }); 88 | 89 | println!("Running chat server on 127.0.0.1:12345"); 90 | }); 91 | } 92 | -------------------------------------------------------------------------------- /tests/derive.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | #[macro_use] 3 | extern crate actix; 4 | extern crate tokio; 5 | 6 | use actix::{Actor, Context, Handler, System}; 7 | use futures::{future, Future}; 8 | 9 | #[derive(Message)] 10 | struct Empty; 11 | 12 | struct EmptyActor; 13 | 14 | impl Actor for EmptyActor { 15 | type Context = Context; 16 | } 17 | 18 | impl Handler for EmptyActor { 19 | type Result = (); 20 | 21 | fn handle(&mut self, _message: Empty, _context: &mut Context) {} 22 | } 23 | 24 | #[test] 25 | #[cfg_attr(feature = "cargo-clippy", allow(unit_cmp))] 26 | fn response_derive_empty() { 27 | System::run(|| { 28 | let addr = EmptyActor.start(); 29 | let res = addr.send(Empty); 30 | 31 | tokio::spawn(res.then(|res| { 32 | match res { 33 | Ok(result) => assert!(result == ()), 34 | _ => panic!("Something went wrong"), 35 | } 36 | 37 | System::current().stop(); 38 | future::result(Ok(())) 39 | })); 40 | }); 41 | } 42 | 43 | #[derive(Message)] 44 | #[rtype(result = "Result")] 45 | struct SumResult(usize, usize); 46 | 47 | struct SumResultActor; 48 | 49 | impl Actor for SumResultActor { 50 | type Context = Context; 51 | } 52 | 53 | impl Handler for SumResultActor { 54 | type Result = Result; 55 | 56 | fn handle( 57 | &mut self, message: SumResult, _context: &mut Context, 58 | ) -> Self::Result { 59 | Ok(message.0 + message.1) 60 | } 61 | } 62 | 63 | #[test] 64 | pub fn derive_result() { 65 | System::run(|| { 66 | let addr = SumResultActor.start(); 67 | let res = addr.send(SumResult(10, 5)); 68 | 69 | tokio::spawn(res.then(|res| { 70 | match res { 71 | Ok(result) => assert!(result == Ok(10 + 5)), 72 | _ => panic!("Something went wrong"), 73 | } 74 | 75 | System::current().stop(); 76 | future::result(Ok(())) 77 | })); 78 | }); 79 | } 80 | 81 | #[derive(Message)] 82 | #[rtype(usize)] 83 | struct SumOne(usize, usize); 84 | 85 | struct SumOneActor; 86 | 87 | impl Actor for SumOneActor { 88 | type Context = Context; 89 | } 90 | 91 | impl Handler for SumOneActor { 92 | type Result = usize; 93 | 94 | fn handle(&mut self, message: SumOne, _context: &mut Context) -> Self::Result { 95 | message.0 + message.1 96 | } 97 | } 98 | 99 | #[test] 100 | pub fn response_derive_one() { 101 | System::run(|| { 102 | let addr = SumOneActor.start(); 103 | let res = addr.send(SumOne(10, 5)); 104 | 105 | tokio::spawn(res.then(|res| { 106 | match res { 107 | Ok(result) => assert!(result == 10 + 5), 108 | _ => panic!("Something went wrong"), 109 | } 110 | 111 | System::current().stop(); 112 | future::result(Ok(())) 113 | })); 114 | }); 115 | } 116 | -------------------------------------------------------------------------------- /src/mailbox.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Stream}; 2 | use std::fmt; 3 | 4 | use actor::{Actor, AsyncContext}; 5 | use address::EnvelopeProxy; 6 | use address::{channel, Addr, AddressReceiver, AddressSenderProducer}; 7 | 8 | #[cfg(feature = "mailbox_assert")] 9 | /// Maximum number of consecutive polls in a loop 10 | const MAX_SYNC_POLLS: u16 = 256; 11 | 12 | /// Default address channel capacity 13 | pub const DEFAULT_CAPACITY: usize = 16; 14 | 15 | pub struct Mailbox 16 | where 17 | A: Actor, 18 | A::Context: AsyncContext, 19 | { 20 | msgs: AddressReceiver, 21 | } 22 | 23 | impl fmt::Debug for Mailbox where 24 | A: Actor, 25 | A::Context: AsyncContext, 26 | { 27 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 28 | fmt.debug_struct("Mailbox") 29 | .field("capacity", &self.capacity()) 30 | .finish() 31 | } 32 | } 33 | 34 | impl Default for Mailbox 35 | where 36 | A: Actor, 37 | A::Context: AsyncContext, 38 | { 39 | #[inline] 40 | fn default() -> Self { 41 | let (_, rx) = channel::channel(DEFAULT_CAPACITY); 42 | Mailbox { msgs: rx } 43 | } 44 | } 45 | 46 | impl Mailbox 47 | where 48 | A: Actor, 49 | A::Context: AsyncContext, 50 | { 51 | #[inline] 52 | pub fn new(msgs: AddressReceiver) -> Self { 53 | Mailbox { msgs } 54 | } 55 | 56 | pub fn capacity(&self) -> usize { 57 | self.msgs.capacity() 58 | } 59 | 60 | pub fn set_capacity(&mut self, cap: usize) { 61 | self.msgs.set_capacity(cap); 62 | } 63 | 64 | #[inline] 65 | pub fn connected(&self) -> bool { 66 | self.msgs.connected() 67 | } 68 | 69 | pub fn address(&self) -> Addr { 70 | Addr::new(self.msgs.sender()) 71 | } 72 | 73 | pub fn sender_producer(&self) -> AddressSenderProducer { 74 | self.msgs.sender_producer() 75 | } 76 | 77 | pub fn poll(&mut self, act: &mut A, ctx: &mut A::Context) { 78 | #[cfg(feature = "mailbox_assert")] 79 | let mut n_polls = 0u16; 80 | 81 | loop { 82 | let mut not_ready = true; 83 | 84 | // sync messages 85 | loop { 86 | if ctx.waiting() { 87 | return; 88 | } 89 | 90 | match self.msgs.poll() { 91 | Ok(Async::Ready(Some(mut msg))) => { 92 | not_ready = false; 93 | msg.handle(act, ctx); 94 | } 95 | Ok(Async::Ready(None)) | Ok(Async::NotReady) | Err(_) => break, 96 | } 97 | 98 | #[cfg(feature = "mailbox_assert")] 99 | { 100 | n_polls += 1; 101 | assert!(n_polls < MAX_SYNC_POLLS, "Too many messages are being processed. Use Self::Context::notify() instead of direct use of address"); 102 | } 103 | } 104 | 105 | if not_ready { 106 | return; 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/address/envelope.rs: -------------------------------------------------------------------------------- 1 | use futures::sync::oneshot::Sender; 2 | use std::marker::PhantomData; 3 | 4 | // use super::{MessageDestination, MessageDestinationTransport, Syn}; 5 | use actor::{Actor, AsyncContext}; 6 | use context::Context; 7 | use handler::{Handler, Message, MessageResponse}; 8 | 9 | /// Converter trait, packs message into a suitable envelope. 10 | pub trait ToEnvelope 11 | where 12 | A: Actor + Handler, 13 | A::Context: ToEnvelope, 14 | { 15 | /// Pack message into suitable envelope 16 | fn pack(msg: M, tx: Option>) -> Envelope; 17 | } 18 | 19 | pub trait EnvelopeProxy { 20 | type Actor: Actor; 21 | 22 | /// handle message within new actor and context 23 | fn handle( 24 | &mut self, act: &mut Self::Actor, ctx: &mut ::Context, 25 | ); 26 | } 27 | 28 | impl ToEnvelope for Context 29 | where 30 | A: Actor> + Handler, 31 | M: Message + Send + 'static, 32 | M::Result: Send, 33 | { 34 | fn pack(msg: M, tx: Option>) -> Envelope { 35 | Envelope::new(msg, tx) 36 | } 37 | } 38 | 39 | pub struct Envelope(Box + Send>); 40 | 41 | impl Envelope { 42 | pub fn new(msg: M, tx: Option>) -> Envelope 43 | where 44 | A: Handler, 45 | A::Context: AsyncContext, 46 | M: Message + Send + 'static, 47 | M::Result: Send, 48 | { 49 | Envelope(Box::new(SyncEnvelopeProxy { 50 | tx, 51 | msg: Some(msg), 52 | act: PhantomData, 53 | })) 54 | } 55 | 56 | pub fn with_proxy(proxy: Box + Send>) -> Envelope { 57 | Envelope(proxy) 58 | } 59 | } 60 | 61 | impl EnvelopeProxy for Envelope { 62 | type Actor = A; 63 | 64 | fn handle( 65 | &mut self, act: &mut Self::Actor, ctx: &mut ::Context, 66 | ) { 67 | self.0.handle(act, ctx) 68 | } 69 | } 70 | 71 | pub struct SyncEnvelopeProxy 72 | where 73 | M: Message + Send, 74 | M::Result: Send, 75 | { 76 | act: PhantomData, 77 | msg: Option, 78 | tx: Option>, 79 | } 80 | 81 | unsafe impl Send for SyncEnvelopeProxy 82 | where 83 | M: Message + Send, 84 | M::Result: Send, 85 | { 86 | } 87 | 88 | impl EnvelopeProxy for SyncEnvelopeProxy 89 | where 90 | M: Message + Send + 'static, 91 | M::Result: Send, 92 | A: Actor + Handler, 93 | A::Context: AsyncContext, 94 | { 95 | type Actor = A; 96 | 97 | fn handle( 98 | &mut self, act: &mut Self::Actor, ctx: &mut ::Context, 99 | ) { 100 | let tx = self.tx.take(); 101 | if tx.is_some() && tx.as_ref().unwrap().is_canceled() { 102 | return; 103 | } 104 | 105 | if let Some(msg) = self.msg.take() { 106 | let fut = >::handle(act, msg, ctx); 107 | fut.handle(ctx, tx) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/msgs.rs: -------------------------------------------------------------------------------- 1 | //! Actix system messages 2 | 3 | use actor::Actor; 4 | use address::Addr; 5 | use context::Context; 6 | use handler::Message; 7 | 8 | /// Message to stop arbiter execution 9 | pub struct StopArbiter(pub i32); 10 | 11 | impl Message for StopArbiter { 12 | type Result = (); 13 | } 14 | 15 | /// Start actor in arbiter's thread 16 | pub struct StartActor(Box>); 17 | 18 | impl Message for StartActor { 19 | type Result = Addr; 20 | } 21 | 22 | impl>> StartActor { 23 | pub fn new(f: F) -> Self 24 | where 25 | F: FnOnce(&mut Context) -> A + Send + 'static, 26 | { 27 | StartActor(Box::new(|| { 28 | let mut ctx = Context::new(); 29 | let act = f(&mut ctx); 30 | ctx.run(act) 31 | })) 32 | } 33 | 34 | pub(crate) fn call(self) -> Addr { 35 | self.0.call_box() 36 | } 37 | } 38 | 39 | trait FnBox: Send + 'static { 40 | fn call_box(self: Box) -> Addr; 41 | } 42 | 43 | impl Addr + Send + 'static> FnBox for F { 44 | #[cfg_attr(feature = "cargo-clippy", allow(boxed_local))] 45 | fn call_box(self: Box) -> Addr { 46 | (*self)() 47 | } 48 | } 49 | 50 | /// Message to execute a function in an arbiter's thread. 51 | /// 52 | /// The arbiter actor handles `Execute` messages. 53 | /// 54 | /// # Example 55 | /// 56 | /// ```rust 57 | /// # extern crate actix; 58 | /// use actix::prelude::*; 59 | /// 60 | /// struct MyActor { 61 | /// addr: Addr, 62 | /// } 63 | /// 64 | /// impl Actor for MyActor { 65 | /// type Context = Context; 66 | /// 67 | /// fn started(&mut self, ctx: &mut Context) { 68 | /// self.addr 69 | /// .do_send(actix::msgs::Execute::new(|| -> Result<(), ()> { 70 | /// // do something 71 | /// // ... 72 | /// Ok(()) 73 | /// })); 74 | /// } 75 | /// } 76 | /// fn main() {} 77 | /// ``` 78 | pub struct Execute(Box>); 79 | 80 | /// Execute message response 81 | impl Message for Execute { 82 | type Result = Result; 83 | } 84 | 85 | impl Execute 86 | where 87 | I: Send + 'static, 88 | E: Send + 'static, 89 | { 90 | pub fn new(f: F) -> Self 91 | where 92 | F: FnOnce() -> Result + Send + 'static, 93 | { 94 | Execute(Box::new(f)) 95 | } 96 | 97 | /// Execute enclosed function 98 | pub fn exec(self) -> Result { 99 | self.0.call_box() 100 | } 101 | } 102 | 103 | trait FnExec: Send + 'static { 104 | fn call_box(self: Box) -> Result; 105 | } 106 | 107 | impl FnExec for F 108 | where 109 | I: Send + 'static, 110 | E: Send + 'static, 111 | F: FnOnce() -> Result + Send + 'static, 112 | { 113 | #[cfg_attr(feature = "cargo-clippy", allow(boxed_local))] 114 | fn call_box(self: Box) -> Result { 115 | (*self)() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at fafhrd91@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /src/fut/result.rs: -------------------------------------------------------------------------------- 1 | //! Definition of the `Result` (immediately finished) combinator 2 | 3 | use futures::{Async, Poll}; 4 | use std::marker::PhantomData; 5 | 6 | use actor::Actor; 7 | use fut::ActorFuture; 8 | 9 | /// A future representing a value that is immediately ready. 10 | /// 11 | /// Created by the `result` function. 12 | #[derive(Debug)] 13 | #[must_use = "futures do nothing unless polled"] 14 | // TODO: rename this to `Result` on the next major version 15 | pub struct FutureResult { 16 | inner: Option>, 17 | act: PhantomData, 18 | } 19 | 20 | /// Creates a new "leaf future" which will resolve with the given result. 21 | /// 22 | /// The returned future represents a computation which is finished immediately. 23 | /// This can be useful with the `finished` and `failed` base future types to 24 | /// convert an immediate value to a future to interoperate elsewhere. 25 | /// 26 | /// # Examples 27 | /// 28 | /// ``` 29 | /// use actix::{fut, Actor, Context}; 30 | /// 31 | /// struct MyActor; 32 | /// impl Actor for MyActor { 33 | /// type Context = Context; 34 | /// } 35 | /// 36 | /// let future_of_1 = fut::result::(Ok(1)); 37 | /// let future_of_err_2 = fut::result::(Err(2)); 38 | /// ``` 39 | pub fn result(r: Result) -> FutureResult { 40 | FutureResult { 41 | inner: Some(r), 42 | act: PhantomData, 43 | } 44 | } 45 | 46 | /// Creates a "leaf future" from an immediate value of a finished and 47 | /// successful computation. 48 | /// 49 | /// The returned future is similar to `result` where it will immediately run a 50 | /// scheduled callback with the provided value. 51 | /// 52 | /// # Examples 53 | /// 54 | /// ``` 55 | /// use actix::fut::*; 56 | /// use actix::{Actor, Context}; 57 | /// 58 | /// struct MyActor; 59 | /// impl Actor for MyActor { 60 | /// type Context = Context; 61 | /// } 62 | /// 63 | /// let future_of_1 = ok::(1); 64 | /// ``` 65 | pub fn ok(t: T) -> FutureResult { 66 | result(Ok(t)) 67 | } 68 | 69 | /// Creates a "leaf future" from an immediate value of a failed computation. 70 | /// 71 | /// The returned future is similar to `result` where it will immediately run a 72 | /// scheduled callback with the provided value. 73 | /// 74 | /// # Examples 75 | /// 76 | /// ``` 77 | /// use actix::{fut, Actor, Context}; 78 | /// 79 | /// struct MyActor; 80 | /// impl Actor for MyActor { 81 | /// type Context = Context; 82 | /// } 83 | /// 84 | /// let future_of_err_1 = fut::err::(1); 85 | /// ``` 86 | pub fn err(e: E) -> FutureResult { 87 | result(Err(e)) 88 | } 89 | 90 | impl ActorFuture for FutureResult 91 | where 92 | A: Actor, 93 | { 94 | type Item = T; 95 | type Error = E; 96 | type Actor = A; 97 | 98 | fn poll( 99 | &mut self, _: &mut Self::Actor, _: &mut ::Context, 100 | ) -> Poll { 101 | self.inner 102 | .take() 103 | .expect("cannot poll Result twice") 104 | .map(Async::Ready) 105 | } 106 | } 107 | 108 | impl From> for FutureResult { 109 | fn from(r: Result) -> Self { 110 | result(r) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /examples/chat/src/codec.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use byteorder::{BigEndian, ByteOrder}; 3 | use bytes::{BufMut, BytesMut}; 4 | use serde_json as json; 5 | use std::io; 6 | use tokio_io::codec::{Decoder, Encoder}; 7 | 8 | /// Client request 9 | #[derive(Serialize, Deserialize, Debug, Message)] 10 | #[serde(tag = "cmd", content = "data")] 11 | pub enum ChatRequest { 12 | /// List rooms 13 | List, 14 | /// Join rooms 15 | Join(String), 16 | /// Send message 17 | Message(String), 18 | /// Ping 19 | Ping, 20 | } 21 | 22 | /// Server response 23 | #[derive(Serialize, Deserialize, Debug, Message)] 24 | #[serde(tag = "cmd", content = "data")] 25 | pub enum ChatResponse { 26 | Ping, 27 | 28 | /// List of rooms 29 | Rooms(Vec), 30 | 31 | /// Joined 32 | Joined(String), 33 | 34 | /// Message 35 | Message(String), 36 | } 37 | 38 | /// Codec for Client -> Server transport 39 | pub struct ChatCodec; 40 | 41 | impl Decoder for ChatCodec { 42 | type Item = ChatRequest; 43 | type Error = io::Error; 44 | 45 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 46 | let size = { 47 | if src.len() < 2 { 48 | return Ok(None); 49 | } 50 | BigEndian::read_u16(src.as_ref()) as usize 51 | }; 52 | 53 | if src.len() >= size + 2 { 54 | src.split_to(2); 55 | let buf = src.split_to(size); 56 | Ok(Some(json::from_slice::(&buf)?)) 57 | } else { 58 | Ok(None) 59 | } 60 | } 61 | } 62 | 63 | impl Encoder for ChatCodec { 64 | type Item = ChatResponse; 65 | type Error = io::Error; 66 | 67 | fn encode( 68 | &mut self, msg: ChatResponse, dst: &mut BytesMut, 69 | ) -> Result<(), Self::Error> { 70 | let msg = json::to_string(&msg).unwrap(); 71 | let msg_ref: &[u8] = msg.as_ref(); 72 | 73 | dst.reserve(msg_ref.len() + 2); 74 | dst.put_u16_be(msg_ref.len() as u16); 75 | dst.put(msg_ref); 76 | 77 | Ok(()) 78 | } 79 | } 80 | 81 | /// Codec for Server -> Client transport 82 | pub struct ClientChatCodec; 83 | 84 | impl Decoder for ClientChatCodec { 85 | type Item = ChatResponse; 86 | type Error = io::Error; 87 | 88 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 89 | let size = { 90 | if src.len() < 2 { 91 | return Ok(None); 92 | } 93 | BigEndian::read_u16(src.as_ref()) as usize 94 | }; 95 | 96 | if src.len() >= size + 2 { 97 | src.split_to(2); 98 | let buf = src.split_to(size); 99 | Ok(Some(json::from_slice::(&buf)?)) 100 | } else { 101 | Ok(None) 102 | } 103 | } 104 | } 105 | 106 | impl Encoder for ClientChatCodec { 107 | type Item = ChatRequest; 108 | type Error = io::Error; 109 | 110 | fn encode( 111 | &mut self, msg: ChatRequest, dst: &mut BytesMut, 112 | ) -> Result<(), Self::Error> { 113 | let msg = json::to_string(&msg).unwrap(); 114 | let msg_ref: &[u8] = msg.as_ref(); 115 | 116 | dst.reserve(msg_ref.len() + 2); 117 | dst.put_u16_be(msg_ref.len() as u16); 118 | dst.put(msg_ref); 119 | 120 | Ok(()) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | use actor::{Actor, ActorContext, ActorState, AsyncContext, SpawnHandle}; 2 | use address::{Addr, AddressReceiver}; 3 | use arbiter::Arbiter; 4 | use fut::ActorFuture; 5 | use std::fmt; 6 | 7 | use contextimpl::{AsyncContextParts, ContextFut, ContextParts}; 8 | use mailbox::Mailbox; 9 | 10 | /// An actor execution context. 11 | pub struct Context 12 | where 13 | A: Actor>, 14 | { 15 | parts: ContextParts, 16 | mb: Option>, 17 | } 18 | 19 | impl>> fmt::Debug for Context { 20 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 21 | fmt.debug_struct("Context") 22 | .field("parts", &self.parts) 23 | .field("mb", &self.mb) 24 | .finish() 25 | } 26 | } 27 | 28 | impl ActorContext for Context 29 | where 30 | A: Actor, 31 | { 32 | #[inline] 33 | fn stop(&mut self) { 34 | self.parts.stop() 35 | } 36 | #[inline] 37 | fn terminate(&mut self) { 38 | self.parts.terminate() 39 | } 40 | #[inline] 41 | fn state(&self) -> ActorState { 42 | self.parts.state() 43 | } 44 | } 45 | 46 | impl AsyncContext for Context 47 | where 48 | A: Actor, 49 | { 50 | #[inline] 51 | fn spawn(&mut self, fut: F) -> SpawnHandle 52 | where 53 | F: ActorFuture + 'static, 54 | { 55 | self.parts.spawn(fut) 56 | } 57 | 58 | #[inline] 59 | fn wait(&mut self, fut: F) 60 | where 61 | F: ActorFuture + 'static, 62 | { 63 | self.parts.wait(fut) 64 | } 65 | 66 | #[inline] 67 | fn waiting(&self) -> bool { 68 | self.parts.waiting() 69 | } 70 | 71 | #[inline] 72 | fn cancel_future(&mut self, handle: SpawnHandle) -> bool { 73 | self.parts.cancel_future(handle) 74 | } 75 | 76 | #[inline] 77 | fn address(&self) -> Addr { 78 | self.parts.address() 79 | } 80 | } 81 | 82 | impl Context 83 | where 84 | A: Actor, 85 | { 86 | #[inline] 87 | pub(crate) fn new() -> Context { 88 | let mb = Mailbox::default(); 89 | Context { 90 | parts: ContextParts::new(mb.sender_producer()), 91 | mb: Some(mb), 92 | } 93 | } 94 | 95 | #[inline] 96 | pub fn with_receiver(rx: AddressReceiver) -> Context { 97 | let mb = Mailbox::new(rx); 98 | Context { 99 | parts: ContextParts::new(mb.sender_producer()), 100 | mb: Some(mb), 101 | } 102 | } 103 | 104 | #[inline] 105 | pub fn run(self, act: A) -> Addr { 106 | let fut = self.into_future(act); 107 | let addr = fut.address(); 108 | Arbiter::spawn(fut); 109 | addr 110 | } 111 | 112 | pub fn into_future(mut self, act: A) -> ContextFut { 113 | let mb = self.mb.take().unwrap(); 114 | ContextFut::new(self, act, mb) 115 | } 116 | 117 | /// Returns a handle to the running future. 118 | /// 119 | /// This is the handle returned by the `AsyncContext::spawn()` 120 | /// method. 121 | pub fn handle(&self) -> SpawnHandle { 122 | self.parts.curr_handle() 123 | } 124 | 125 | /// Sets the mailbox capacity. 126 | /// 127 | /// The default mailbox capacity is 16 messages. 128 | pub fn set_mailbox_capacity(&mut self, cap: usize) { 129 | self.parts.set_mailbox_capacity(cap) 130 | } 131 | } 132 | 133 | impl AsyncContextParts for Context 134 | where 135 | A: Actor, 136 | { 137 | fn parts(&mut self) -> &mut ContextParts { 138 | &mut self.parts 139 | } 140 | } 141 | 142 | /// Helper trait which can spawn a future into the actor's context. 143 | pub trait ContextFutureSpawner 144 | where 145 | A: Actor, 146 | A::Context: AsyncContext, 147 | { 148 | /// Spawns the future into the given context. 149 | fn spawn(self, ctx: &mut A::Context); 150 | 151 | /// Spawns the future into the given context, waiting for it to 152 | /// resolve. 153 | /// 154 | /// This stops processing any incoming events until this future 155 | /// resolves. 156 | fn wait(self, ctx: &mut A::Context); 157 | } 158 | 159 | impl ContextFutureSpawner for T 160 | where 161 | A: Actor, 162 | A::Context: AsyncContext, 163 | T: ActorFuture + 'static, 164 | { 165 | #[inline] 166 | fn spawn(self, ctx: &mut A::Context) { 167 | let _ = ctx.spawn(self); 168 | } 169 | 170 | #[inline] 171 | fn wait(self, ctx: &mut A::Context) { 172 | ctx.wait(self); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/supervisor.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Future, Poll}; 2 | 3 | use actor::{Actor, AsyncContext, Supervised}; 4 | use address::{channel, Addr}; 5 | use arbiter::Arbiter; 6 | use context::Context; 7 | use contextimpl::ContextFut; 8 | use mailbox::DEFAULT_CAPACITY; 9 | use msgs::Execute; 10 | 11 | /// Actor supervisor 12 | /// 13 | /// Supervisor manages incoming message for actor. In case of actor failure, 14 | /// supervisor creates new execution context and restarts actor lifecycle. 15 | /// Supervisor does not re-create actor, it just calls `restarting()` 16 | /// method. 17 | /// 18 | /// Supervisor has same lifecycle as actor. In situation when all addresses to 19 | /// supervisor get dropped and actor does not execute anything, supervisor 20 | /// terminates. 21 | /// 22 | /// `Supervisor` can not guarantee that actor successfully process incoming 23 | /// message. If actor fails during message processing, this message can not be 24 | /// recovered. Sender would receive `Err(Cancelled)` error in this situation. 25 | /// 26 | /// ## Example 27 | /// 28 | /// ```rust 29 | /// # #[macro_use] extern crate actix; 30 | /// # use actix::prelude::*; 31 | /// #[derive(Message)] 32 | /// struct Die; 33 | /// 34 | /// struct MyActor; 35 | /// 36 | /// impl Actor for MyActor { 37 | /// type Context = Context; 38 | /// } 39 | /// 40 | /// // To use actor with supervisor actor has to implement `Supervised` trait 41 | /// impl actix::Supervised for MyActor { 42 | /// fn restarting(&mut self, ctx: &mut Context) { 43 | /// println!("restarting"); 44 | /// } 45 | /// } 46 | /// 47 | /// impl Handler for MyActor { 48 | /// type Result = (); 49 | /// 50 | /// fn handle(&mut self, _: Die, ctx: &mut Context) { 51 | /// ctx.stop(); 52 | /// # System::current().stop(); 53 | /// } 54 | /// } 55 | /// 56 | /// fn main() { 57 | /// System::run(|| { 58 | /// let addr = actix::Supervisor::start(|_| MyActor); 59 | /// 60 | /// addr.do_send(Die); 61 | /// }); 62 | /// } 63 | /// ``` 64 | #[derive(Debug)] 65 | pub struct Supervisor 66 | where 67 | A: Supervised + Actor>, 68 | { 69 | fut: ContextFut>, 70 | } 71 | 72 | impl Supervisor 73 | where 74 | A: Supervised + Actor>, 75 | { 76 | /// Start new supervised actor in current tokio runtime. 77 | /// 78 | /// Type of returned address depends on variable type. For example to get 79 | /// `Addr` of newly created actor, use explicitly `Addr` type as type of a variable. 81 | /// 82 | /// ```rust 83 | /// # #[macro_use] extern crate actix; 84 | /// # use actix::prelude::*; 85 | /// struct MyActor; 86 | /// 87 | /// impl Actor for MyActor { 88 | /// type Context = Context; 89 | /// } 90 | /// 91 | /// # impl actix::Supervised for MyActor {} 92 | /// # fn main() { 93 | /// # System::run(|| { 94 | /// // Get `Addr` of a MyActor actor 95 | /// let addr = actix::Supervisor::start(|_| MyActor); 96 | /// # System::current().stop(); 97 | /// # });} 98 | /// ``` 99 | pub fn start(f: F) -> Addr 100 | where 101 | F: FnOnce(&mut A::Context) -> A + 'static, 102 | A: Actor>, 103 | { 104 | // create actor 105 | let mut ctx = Context::new(); 106 | let act = f(&mut ctx); 107 | let addr = ctx.address(); 108 | let fut = ctx.into_future(act); 109 | 110 | // create supervisor 111 | Arbiter::spawn(Supervisor:: { fut }); 112 | 113 | addr 114 | } 115 | 116 | /// Start new supervised actor in arbiter's thread. 117 | pub fn start_in_arbiter(sys: &Addr, f: F) -> Addr 118 | where 119 | A: Actor>, 120 | F: FnOnce(&mut Context) -> A + Send + 'static, 121 | { 122 | let (tx, rx) = channel::channel(DEFAULT_CAPACITY); 123 | 124 | sys.do_send(Execute::new(move || -> Result<(), ()> { 125 | let mut ctx = Context::with_receiver(rx); 126 | let act = f(&mut ctx); 127 | let fut = ctx.into_future(act); 128 | 129 | Arbiter::spawn(Supervisor:: { fut }); 130 | Ok(()) 131 | })); 132 | 133 | Addr::new(tx) 134 | } 135 | } 136 | 137 | #[doc(hidden)] 138 | impl Future for Supervisor 139 | where 140 | A: Supervised + Actor>, 141 | { 142 | type Item = (); 143 | type Error = (); 144 | 145 | fn poll(&mut self) -> Poll { 146 | loop { 147 | match self.fut.poll() { 148 | Ok(Async::NotReady) => return Ok(Async::NotReady), 149 | Ok(Async::Ready(_)) | Err(_) => { 150 | // stop if context's address is not connected 151 | if !self.fut.restart() { 152 | return Ok(Async::Ready(())); 153 | } 154 | } 155 | } 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /examples/chat/src/client.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate actix; 3 | extern crate byteorder; 4 | extern crate bytes; 5 | extern crate futures; 6 | extern crate serde; 7 | extern crate serde_json; 8 | extern crate tokio; 9 | extern crate tokio_codec; 10 | extern crate tokio_io; 11 | extern crate tokio_tcp; 12 | #[macro_use] 13 | extern crate serde_derive; 14 | 15 | use std::str::FromStr; 16 | use std::time::Duration; 17 | use std::{io, net, process, thread}; 18 | 19 | use actix::prelude::*; 20 | use futures::Future; 21 | use tokio_codec::FramedRead; 22 | use tokio_io::io::WriteHalf; 23 | use tokio_io::AsyncRead; 24 | use tokio_tcp::TcpStream; 25 | 26 | mod codec; 27 | 28 | fn main() { 29 | println!("Running chat client"); 30 | 31 | actix::System::run(|| { 32 | // Connect to server 33 | let addr = net::SocketAddr::from_str("127.0.0.1:12345").unwrap(); 34 | Arbiter::spawn( 35 | TcpStream::connect(&addr) 36 | .and_then(|stream| { 37 | let addr = ChatClient::create(|ctx| { 38 | let (r, w) = stream.split(); 39 | ctx.add_stream(FramedRead::new(r, codec::ClientChatCodec)); 40 | ChatClient { 41 | framed: actix::io::FramedWrite::new( 42 | w, 43 | codec::ClientChatCodec, 44 | ctx, 45 | ), 46 | } 47 | }); 48 | 49 | // start console loop 50 | thread::spawn(move || loop { 51 | let mut cmd = String::new(); 52 | if io::stdin().read_line(&mut cmd).is_err() { 53 | println!("error"); 54 | return; 55 | } 56 | 57 | addr.do_send(ClientCommand(cmd)); 58 | }); 59 | 60 | futures::future::ok(()) 61 | }) 62 | .map_err(|e| { 63 | println!("Can not connect to server: {}", e); 64 | process::exit(1) 65 | }), 66 | ); 67 | }); 68 | } 69 | 70 | struct ChatClient { 71 | framed: actix::io::FramedWrite, codec::ClientChatCodec>, 72 | } 73 | 74 | #[derive(Message)] 75 | struct ClientCommand(String); 76 | 77 | impl Actor for ChatClient { 78 | type Context = Context; 79 | 80 | fn started(&mut self, ctx: &mut Context) { 81 | // start heartbeats otherwise server will disconnect after 10 seconds 82 | self.hb(ctx) 83 | } 84 | 85 | fn stopping(&mut self, _: &mut Context) -> Running { 86 | println!("Disconnected"); 87 | 88 | // Stop application on disconnect 89 | System::current().stop(); 90 | 91 | Running::Stop 92 | } 93 | } 94 | 95 | impl ChatClient { 96 | fn hb(&self, ctx: &mut Context) { 97 | ctx.run_later(Duration::new(1, 0), |act, ctx| { 98 | act.framed.write(codec::ChatRequest::Ping); 99 | act.hb(ctx); 100 | }); 101 | } 102 | } 103 | 104 | impl actix::io::WriteHandler for ChatClient {} 105 | 106 | /// Handle stdin commands 107 | impl Handler for ChatClient { 108 | type Result = (); 109 | 110 | fn handle(&mut self, msg: ClientCommand, _: &mut Context) { 111 | let m = msg.0.trim(); 112 | 113 | // we check for /sss type of messages 114 | if m.starts_with('/') { 115 | let v: Vec<&str> = m.splitn(2, ' ').collect(); 116 | match v[0] { 117 | "/list" => { 118 | self.framed.write(codec::ChatRequest::List); 119 | } 120 | "/join" => { 121 | if v.len() == 2 { 122 | self.framed.write(codec::ChatRequest::Join(v[1].to_owned())); 123 | } else { 124 | println!("!!! room name is required"); 125 | } 126 | } 127 | _ => println!("!!! unknown command"), 128 | } 129 | } else { 130 | self.framed.write(codec::ChatRequest::Message(m.to_owned())); 131 | } 132 | } 133 | } 134 | 135 | /// Server communication 136 | impl StreamHandler for ChatClient { 137 | fn handle(&mut self, msg: codec::ChatResponse, _: &mut Context) { 138 | match msg { 139 | codec::ChatResponse::Message(ref msg) => { 140 | println!("message: {}", msg); 141 | } 142 | codec::ChatResponse::Joined(ref msg) => { 143 | println!("!!! joined: {}", msg); 144 | } 145 | codec::ChatResponse::Rooms(rooms) => { 146 | println!("\n!!! Available rooms:"); 147 | for room in rooms { 148 | println!("{}", room); 149 | } 150 | println!(); 151 | } 152 | _ => (), 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/contextitems.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Future, Poll, Stream}; 2 | use std::marker::PhantomData; 3 | use std::time::Duration; 4 | use tokio_timer::Delay; 5 | 6 | use actor::{Actor, ActorContext, AsyncContext}; 7 | use clock; 8 | use fut::ActorFuture; 9 | use handler::{Handler, Message, MessageResponse}; 10 | 11 | pub(crate) struct ActorWaitItem( 12 | Box>, 13 | ); 14 | 15 | impl ActorWaitItem 16 | where 17 | A: Actor, 18 | A::Context: ActorContext + AsyncContext, 19 | { 20 | #[inline] 21 | pub fn new(fut: F) -> Self 22 | where 23 | F: ActorFuture + 'static, 24 | { 25 | ActorWaitItem(Box::new(fut)) 26 | } 27 | 28 | pub fn poll(&mut self, act: &mut A, ctx: &mut A::Context) -> Async<()> { 29 | match self.0.poll(act, ctx) { 30 | Ok(Async::NotReady) => { 31 | if ctx.state().alive() { 32 | Async::NotReady 33 | } else { 34 | Async::Ready(()) 35 | } 36 | } 37 | Ok(Async::Ready(_)) | Err(_) => Async::Ready(()), 38 | } 39 | } 40 | } 41 | 42 | pub(crate) struct ActorDelayedMessageItem 43 | where 44 | A: Actor, 45 | M: Message, 46 | { 47 | msg: Option, 48 | timeout: Delay, 49 | act: PhantomData, 50 | m: PhantomData, 51 | } 52 | 53 | impl ActorDelayedMessageItem 54 | where 55 | A: Actor, 56 | M: Message, 57 | { 58 | pub fn new(msg: M, timeout: Duration) -> Self { 59 | ActorDelayedMessageItem { 60 | msg: Some(msg), 61 | timeout: Delay::new(clock::now() + timeout), 62 | act: PhantomData, 63 | m: PhantomData, 64 | } 65 | } 66 | } 67 | 68 | impl ActorFuture for ActorDelayedMessageItem 69 | where 70 | A: Actor + Handler, 71 | A::Context: AsyncContext, 72 | M: Message + 'static, 73 | { 74 | type Item = (); 75 | type Error = (); 76 | type Actor = A; 77 | 78 | fn poll( 79 | &mut self, act: &mut A, ctx: &mut A::Context, 80 | ) -> Poll { 81 | match self.timeout.poll() { 82 | Ok(Async::NotReady) => Ok(Async::NotReady), 83 | Ok(Async::Ready(_)) => { 84 | let fut = A::handle(act, self.msg.take().unwrap(), ctx); 85 | fut.handle::<()>(ctx, None); 86 | Ok(Async::Ready(())) 87 | } 88 | Err(_) => unreachable!(), 89 | } 90 | } 91 | } 92 | 93 | pub(crate) struct ActorMessageItem 94 | where 95 | A: Actor, 96 | M: Message, 97 | { 98 | msg: Option, 99 | act: PhantomData, 100 | } 101 | 102 | impl ActorMessageItem 103 | where 104 | A: Actor, 105 | M: Message, 106 | { 107 | pub fn new(msg: M) -> Self { 108 | ActorMessageItem { 109 | msg: Some(msg), 110 | act: PhantomData, 111 | } 112 | } 113 | } 114 | 115 | impl ActorFuture for ActorMessageItem 116 | where 117 | A: Actor + Handler, 118 | A::Context: AsyncContext, 119 | M: Message, 120 | { 121 | type Item = (); 122 | type Error = (); 123 | type Actor = A; 124 | 125 | fn poll( 126 | &mut self, act: &mut A, ctx: &mut A::Context, 127 | ) -> Poll { 128 | let fut = Handler::handle(act, self.msg.take().unwrap(), ctx); 129 | fut.handle::<()>(ctx, None); 130 | Ok(Async::Ready(())) 131 | } 132 | } 133 | 134 | pub(crate) struct ActorMessageStreamItem 135 | where 136 | A: Actor, 137 | M: Message, 138 | { 139 | stream: S, 140 | act: PhantomData, 141 | msg: PhantomData, 142 | } 143 | 144 | impl ActorMessageStreamItem 145 | where 146 | A: Actor, 147 | M: Message, 148 | { 149 | pub fn new(st: S) -> Self { 150 | ActorMessageStreamItem { 151 | stream: st, 152 | act: PhantomData, 153 | msg: PhantomData, 154 | } 155 | } 156 | } 157 | 158 | impl ActorFuture for ActorMessageStreamItem 159 | where 160 | S: Stream, 161 | A: Actor + Handler, 162 | A::Context: AsyncContext, 163 | M: Message, 164 | { 165 | type Item = (); 166 | type Error = (); 167 | type Actor = A; 168 | 169 | fn poll( 170 | &mut self, act: &mut A, ctx: &mut A::Context, 171 | ) -> Poll { 172 | loop { 173 | match self.stream.poll() { 174 | Ok(Async::Ready(Some(msg))) => { 175 | let fut = Handler::handle(act, msg, ctx); 176 | fut.handle::<()>(ctx, None); 177 | if ctx.waiting() { 178 | return Ok(Async::NotReady); 179 | } 180 | } 181 | Ok(Async::Ready(None)) => return Ok(Async::Ready(())), 182 | Ok(Async::NotReady) => return Ok(Async::NotReady), 183 | Err(_) => (), 184 | } 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/stream.rs: -------------------------------------------------------------------------------- 1 | use futures::{Async, Poll, Stream}; 2 | use std::marker::PhantomData; 3 | 4 | use actor::{Actor, ActorContext, ActorState, AsyncContext, Running, SpawnHandle}; 5 | use fut::ActorFuture; 6 | 7 | /// Stream handler 8 | /// 9 | /// This is helper trait that allows to handle `Stream` in 10 | /// a similar way as normal actor messages. 11 | /// When stream resolves to a next item, `handle()` method of this trait 12 | /// get called. If stream produces error, `error()` method get called. 13 | /// Depends on result of the `error()` method, actor could continue to 14 | /// process stream items or stop stream processing. 15 | /// When stream completes, `finished()` method get called. By default 16 | /// `finished()` method stops actor execution. 17 | #[allow(unused_variables)] 18 | pub trait StreamHandler 19 | where 20 | Self: Actor, 21 | { 22 | /// Method is called for every message received by this Actor 23 | fn handle(&mut self, item: I, ctx: &mut Self::Context); 24 | 25 | /// Method is called when stream get polled first time. 26 | fn started(&mut self, ctx: &mut Self::Context) {} 27 | 28 | /// Method is called when stream emits error. 29 | /// 30 | /// If this method returns `ErrorAction::Continue` stream processing 31 | /// continues otherwise stream processing stops. Default method 32 | /// implementation returns `ErrorAction::Stop` 33 | fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running { 34 | Running::Stop 35 | } 36 | 37 | /// Method is called when stream finishes. 38 | /// 39 | /// By default this method stops actor execution. 40 | fn finished(&mut self, ctx: &mut Self::Context) { 41 | ctx.stop() 42 | } 43 | 44 | /// This method register stream to an actor context and 45 | /// allows to handle `Stream` in similar way as normal actor messages. 46 | /// 47 | /// ```rust 48 | /// # #[macro_use] extern crate actix; 49 | /// # extern crate futures; 50 | /// # use std::io; 51 | /// use actix::prelude::*; 52 | /// use futures::stream::once; 53 | /// 54 | /// #[derive(Message)] 55 | /// struct Ping; 56 | /// 57 | /// struct MyActor; 58 | /// 59 | /// impl StreamHandler for MyActor { 60 | /// 61 | /// fn handle(&mut self, item: Ping, ctx: &mut Context) { 62 | /// println!("PING"); 63 | /// # System::current().stop() 64 | /// } 65 | /// 66 | /// fn finished(&mut self, ctx: &mut Self::Context) { 67 | /// println!("finished"); 68 | /// } 69 | /// } 70 | /// 71 | /// impl Actor for MyActor { 72 | /// type Context = Context; 73 | /// 74 | /// fn started(&mut self, ctx: &mut Context) { 75 | /// // add stream 76 | /// Self::add_stream(once::(Ok(Ping)), ctx); 77 | /// } 78 | /// } 79 | /// # fn main() { 80 | /// # let sys = System::new("example"); 81 | /// # let addr = MyActor.start(); 82 | /// # sys.run(); 83 | /// # } 84 | /// ``` 85 | fn add_stream(fut: S, ctx: &mut Self::Context) -> SpawnHandle 86 | where 87 | Self::Context: AsyncContext, 88 | S: Stream + 'static, 89 | I: 'static, 90 | E: 'static, 91 | { 92 | if ctx.state() == ActorState::Stopped { 93 | error!("Context::add_stream called for stopped actor."); 94 | SpawnHandle::default() 95 | } else { 96 | ctx.spawn(ActorStream::new(fut)) 97 | } 98 | } 99 | } 100 | 101 | pub(crate) struct ActorStream { 102 | stream: S, 103 | started: bool, 104 | act: PhantomData, 105 | msg: PhantomData, 106 | error: PhantomData, 107 | } 108 | 109 | impl ActorStream { 110 | pub fn new(fut: S) -> Self { 111 | ActorStream { 112 | stream: fut, 113 | started: false, 114 | act: PhantomData, 115 | msg: PhantomData, 116 | error: PhantomData, 117 | } 118 | } 119 | } 120 | 121 | impl ActorFuture for ActorStream 122 | where 123 | S: Stream, 124 | A: Actor + StreamHandler, 125 | A::Context: AsyncContext, 126 | { 127 | type Item = (); 128 | type Error = (); 129 | type Actor = A; 130 | 131 | fn poll( 132 | &mut self, act: &mut A, ctx: &mut A::Context, 133 | ) -> Poll { 134 | if !self.started { 135 | self.started = true; 136 | >::started(act, ctx); 137 | } 138 | 139 | loop { 140 | match self.stream.poll() { 141 | Ok(Async::Ready(Some(msg))) => { 142 | A::handle(act, msg, ctx); 143 | if ctx.waiting() { 144 | return Ok(Async::NotReady); 145 | } 146 | } 147 | Err(err) => { 148 | if A::error(act, err, ctx) == Running::Stop { 149 | A::finished(act, ctx); 150 | return Ok(Async::Ready(())); 151 | } 152 | } 153 | Ok(Async::Ready(None)) => { 154 | A::finished(act, ctx); 155 | return Ok(Async::Ready(())); 156 | } 157 | Ok(Async::NotReady) => return Ok(Async::NotReady), 158 | } 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/address/message.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::time::Duration; 3 | 4 | use futures::sync::oneshot; 5 | use futures::{Async, Future, Poll}; 6 | use tokio_timer::Delay; 7 | 8 | use clock; 9 | use handler::{Handler, Message}; 10 | 11 | use super::channel::{AddressSender, Sender}; 12 | use super::{MailboxError, SendError, ToEnvelope}; 13 | 14 | /// A `Future` which represents an asynchronous message sending 15 | /// process. 16 | #[must_use = "You have to wait on request otherwise the Message wont be delivered"] 17 | pub struct Request 18 | where 19 | A: Handler, 20 | A::Context: ToEnvelope, 21 | M: Message, 22 | { 23 | rx: Option>, 24 | info: Option<(AddressSender, M)>, 25 | timeout: Option, 26 | act: PhantomData, 27 | } 28 | 29 | impl Request 30 | where 31 | A: Handler, 32 | A::Context: ToEnvelope, 33 | M: Message, 34 | { 35 | pub(crate) fn new( 36 | rx: Option>, info: Option<(AddressSender, M)>, 37 | ) -> Request { 38 | Request { 39 | rx, 40 | info, 41 | timeout: None, 42 | act: PhantomData, 43 | } 44 | } 45 | 46 | #[cfg(test)] 47 | pub(crate) fn rx_is_some(&self) -> bool { 48 | self.rx.is_some() 49 | } 50 | 51 | /// Set message delivery timeout 52 | pub fn timeout(mut self, dur: Duration) -> Self { 53 | self.timeout = Some(Delay::new(clock::now() + dur)); 54 | self 55 | } 56 | 57 | fn poll_timeout(&mut self) -> Poll { 58 | if let Some(ref mut timeout) = self.timeout { 59 | match timeout.poll() { 60 | Ok(Async::Ready(())) => Err(MailboxError::Timeout), 61 | Ok(Async::NotReady) => Ok(Async::NotReady), 62 | Err(_) => unreachable!(), 63 | } 64 | } else { 65 | Ok(Async::NotReady) 66 | } 67 | } 68 | } 69 | 70 | impl Future for Request 71 | where 72 | A: Handler, 73 | A::Context: ToEnvelope, 74 | M: Message + Send, 75 | M::Result: Send, 76 | { 77 | type Item = M::Result; 78 | type Error = MailboxError; 79 | 80 | fn poll(&mut self) -> Poll { 81 | if let Some((sender, msg)) = self.info.take() { 82 | match sender.send(msg) { 83 | Ok(rx) => self.rx = Some(rx), 84 | Err(SendError::Full(msg)) => { 85 | self.info = Some((sender, msg)); 86 | return Ok(Async::NotReady); 87 | } 88 | Err(SendError::Closed(_)) => return Err(MailboxError::Closed), 89 | } 90 | } 91 | 92 | if self.rx.is_some() { 93 | match self.rx.as_mut().unwrap().poll() { 94 | Ok(Async::Ready(item)) => Ok(Async::Ready(item)), 95 | Ok(Async::NotReady) => self.poll_timeout(), 96 | Err(_) => Err(MailboxError::Closed), 97 | } 98 | } else { 99 | Err(MailboxError::Closed) 100 | } 101 | } 102 | } 103 | 104 | /// A `Future` which represents an asynchronous message sending process. 105 | #[must_use = "future do nothing unless polled"] 106 | pub struct RecipientRequest 107 | where 108 | M: Message + Send + 'static, 109 | M::Result: Send, 110 | { 111 | rx: Option>, 112 | info: Option<(Box>, M)>, 113 | timeout: Option, 114 | } 115 | 116 | impl RecipientRequest 117 | where 118 | M: Message + Send + 'static, 119 | M::Result: Send, 120 | { 121 | pub fn new( 122 | rx: Option>, info: Option<(Box>, M)>, 123 | ) -> RecipientRequest { 124 | RecipientRequest { 125 | rx, 126 | info, 127 | timeout: None, 128 | } 129 | } 130 | 131 | /// Set message delivery timeout 132 | pub fn timeout(mut self, dur: Duration) -> Self { 133 | self.timeout = Some(Delay::new(clock::now() + dur)); 134 | self 135 | } 136 | 137 | fn poll_timeout(&mut self) -> Poll { 138 | if let Some(ref mut timeout) = self.timeout { 139 | match timeout.poll() { 140 | Ok(Async::Ready(())) => Err(MailboxError::Timeout), 141 | Ok(Async::NotReady) => Ok(Async::NotReady), 142 | Err(_) => unreachable!(), 143 | } 144 | } else { 145 | Ok(Async::NotReady) 146 | } 147 | } 148 | } 149 | 150 | impl Future for RecipientRequest 151 | where 152 | M: Message + Send + 'static, 153 | M::Result: Send, 154 | { 155 | type Item = M::Result; 156 | type Error = MailboxError; 157 | 158 | fn poll(&mut self) -> Poll { 159 | if let Some((sender, msg)) = self.info.take() { 160 | match sender.send(msg) { 161 | Ok(rx) => self.rx = Some(rx), 162 | Err(SendError::Full(msg)) => { 163 | self.info = Some((sender, msg)); 164 | return Ok(Async::NotReady); 165 | } 166 | Err(SendError::Closed(_)) => return Err(MailboxError::Closed), 167 | } 168 | } 169 | 170 | if let Some(mut rx) = self.rx.take() { 171 | match rx.poll() { 172 | Ok(Async::Ready(item)) => Ok(Async::Ready(item)), 173 | Ok(Async::NotReady) => { 174 | self.rx = Some(rx); 175 | self.poll_timeout() 176 | } 177 | Err(_) => Err(MailboxError::Closed), 178 | } 179 | } else { 180 | Err(MailboxError::Closed) 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /examples/chat/src/session.rs: -------------------------------------------------------------------------------- 1 | //! `ClientSession` is an actor, it manages peer tcp connection and 2 | //! proxies commands from peer to `ChatServer`. 3 | use actix::prelude::*; 4 | use std::io; 5 | use std::time::{Duration, Instant}; 6 | use tokio_io::io::WriteHalf; 7 | use tokio_tcp::TcpStream; 8 | 9 | use codec::{ChatCodec, ChatRequest, ChatResponse}; 10 | use server::{self, ChatServer}; 11 | 12 | /// Chat server sends this messages to session 13 | #[derive(Message)] 14 | pub struct Message(pub String); 15 | 16 | /// `ChatSession` actor is responsible for tcp peer communications. 17 | pub struct ChatSession { 18 | /// unique session id 19 | id: usize, 20 | /// this is address of chat server 21 | addr: Addr, 22 | /// Client must send ping at least once per 10 seconds, otherwise we drop 23 | /// connection. 24 | hb: Instant, 25 | /// joined room 26 | room: String, 27 | /// Framed wrapper 28 | framed: actix::io::FramedWrite, ChatCodec>, 29 | } 30 | 31 | impl Actor for ChatSession { 32 | type Context = actix::Context; 33 | 34 | fn started(&mut self, ctx: &mut Self::Context) { 35 | // we'll start heartbeat process on session start. 36 | self.hb(ctx); 37 | 38 | // register self in chat server. `AsyncContext::wait` register 39 | // future within context, but context waits until this future resolves 40 | // before processing any other events. 41 | self.addr 42 | .send(server::Connect { 43 | addr: ctx.address(), 44 | }) 45 | .into_actor(self) 46 | .then(|res, act, ctx| { 47 | match res { 48 | Ok(res) => act.id = res, 49 | // something is wrong with chat server 50 | _ => ctx.stop(), 51 | } 52 | actix::fut::ok(()) 53 | }) 54 | .wait(ctx); 55 | } 56 | 57 | fn stopping(&mut self, _: &mut Self::Context) -> Running { 58 | // notify chat server 59 | self.addr.do_send(server::Disconnect { id: self.id }); 60 | Running::Stop 61 | } 62 | } 63 | 64 | impl actix::io::WriteHandler for ChatSession {} 65 | 66 | /// To use `Framed` with an actor, we have to implement `StreamHandler` trait 67 | impl StreamHandler for ChatSession { 68 | /// This is main event loop for client requests 69 | fn handle(&mut self, msg: ChatRequest, ctx: &mut Self::Context) { 70 | match msg { 71 | ChatRequest::List => { 72 | // Send ListRooms message to chat server and wait for response 73 | println!("List rooms"); 74 | self.addr.send(server::ListRooms) 75 | .into_actor(self) // <- create actor compatible future 76 | .then(|res, act, _| { 77 | match res { 78 | Ok(rooms) => act.framed.write(ChatResponse::Rooms(rooms)), 79 | _ => println!("Something is wrong"), 80 | } 81 | actix::fut::ok(()) 82 | }).wait(ctx) 83 | // .wait(ctx) pauses all events in context, 84 | // so actor wont receive any new messages until it get list of rooms back 85 | } 86 | ChatRequest::Join(name) => { 87 | println!("Join to room: {}", name); 88 | self.room = name.clone(); 89 | self.addr.do_send(server::Join { 90 | id: self.id, 91 | name: name.clone(), 92 | }); 93 | self.framed.write(ChatResponse::Joined(name)); 94 | } 95 | ChatRequest::Message(message) => { 96 | // send message to chat server 97 | println!("Peer message: {}", message); 98 | self.addr.do_send(server::Message { 99 | id: self.id, 100 | msg: message, 101 | room: self.room.clone(), 102 | }) 103 | } 104 | // we update heartbeat time on ping from peer 105 | ChatRequest::Ping => self.hb = Instant::now(), 106 | } 107 | } 108 | } 109 | 110 | /// Handler for Message, chat server sends this message, we just send string to 111 | /// peer 112 | impl Handler for ChatSession { 113 | type Result = (); 114 | 115 | fn handle(&mut self, msg: Message, _: &mut Self::Context) { 116 | // send message to peer 117 | self.framed.write(ChatResponse::Message(msg.0)); 118 | } 119 | } 120 | 121 | /// Helper methods 122 | impl ChatSession { 123 | pub fn new( 124 | addr: Addr, 125 | framed: actix::io::FramedWrite, ChatCodec>, 126 | ) -> ChatSession { 127 | ChatSession { 128 | addr, 129 | framed, 130 | id: 0, 131 | hb: Instant::now(), 132 | room: "Main".to_owned(), 133 | } 134 | } 135 | 136 | /// helper method that sends ping to client every second. 137 | /// 138 | /// also this method check heartbeats from client 139 | fn hb(&self, ctx: &mut actix::Context) { 140 | ctx.run_later(Duration::new(1, 0), |act, ctx| { 141 | // check client heartbeats 142 | if Instant::now().duration_since(act.hb) > Duration::new(10, 0) { 143 | // heartbeat timed out 144 | println!("Client heartbeat failed, disconnecting!"); 145 | 146 | // notify chat server 147 | act.addr.do_send(server::Disconnect { id: act.id }); 148 | 149 | // stop actor 150 | ctx.stop(); 151 | } 152 | 153 | act.framed.write(ChatResponse::Ping); 154 | act.hb(ctx); 155 | }); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /examples/chat/src/server.rs: -------------------------------------------------------------------------------- 1 | //! `ChatServer` is an actor. It maintains list of connection client session. 2 | //! And manages available rooms. Peers send messages to other peers in same 3 | //! room through `ChatServer`. 4 | 5 | use actix::prelude::*; 6 | use rand::{self, Rng}; 7 | use std::collections::{HashMap, HashSet}; 8 | 9 | use session; 10 | 11 | /// Message for chat server communications 12 | 13 | /// New chat session is created 14 | pub struct Connect { 15 | pub addr: Addr, 16 | } 17 | 18 | /// Response type for Connect message 19 | /// 20 | /// Chat server returns unique session id 21 | impl actix::Message for Connect { 22 | type Result = usize; 23 | } 24 | 25 | /// Session is disconnected 26 | #[derive(Message)] 27 | pub struct Disconnect { 28 | pub id: usize, 29 | } 30 | 31 | /// Send message to specific room 32 | #[derive(Message)] 33 | pub struct Message { 34 | /// Id of the client session 35 | pub id: usize, 36 | /// Peer message 37 | pub msg: String, 38 | /// Room name 39 | pub room: String, 40 | } 41 | 42 | /// List of available rooms 43 | pub struct ListRooms; 44 | 45 | impl actix::Message for ListRooms { 46 | type Result = Vec; 47 | } 48 | 49 | /// Join room, if room does not exists create new one. 50 | #[derive(Message)] 51 | pub struct Join { 52 | /// Client id 53 | pub id: usize, 54 | /// Room name 55 | pub name: String, 56 | } 57 | 58 | /// `ChatServer` manages chat rooms and responsible for coordinating chat 59 | /// session. implementation is super primitive 60 | pub struct ChatServer { 61 | sessions: HashMap>, 62 | rooms: HashMap>, 63 | } 64 | 65 | impl Default for ChatServer { 66 | fn default() -> ChatServer { 67 | // default room 68 | let mut rooms = HashMap::new(); 69 | rooms.insert("Main".to_owned(), HashSet::new()); 70 | 71 | ChatServer { 72 | rooms, 73 | sessions: HashMap::new(), 74 | } 75 | } 76 | } 77 | 78 | impl ChatServer { 79 | /// Send message to all users in the room 80 | fn send_message(&self, room: &str, message: &str, skip_id: usize) { 81 | if let Some(sessions) = self.rooms.get(room) { 82 | for id in sessions { 83 | if *id != skip_id { 84 | if let Some(addr) = self.sessions.get(id) { 85 | addr.do_send(session::Message(message.to_owned())) 86 | } 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | /// Make actor from `ChatServer` 94 | impl Actor for ChatServer { 95 | /// We are going to use simple Context, we just need ability to communicate 96 | /// with other actors. 97 | type Context = Context; 98 | } 99 | 100 | /// Handler for Connect message. 101 | /// 102 | /// Register new session and assign unique id to this session 103 | impl Handler for ChatServer { 104 | type Result = usize; 105 | 106 | fn handle(&mut self, msg: Connect, _: &mut Context) -> Self::Result { 107 | println!("Someone joined"); 108 | 109 | // notify all users in same room 110 | self.send_message(&"Main".to_owned(), "Someone joined", 0); 111 | 112 | // register session with random id 113 | let id = rand::thread_rng().gen::(); 114 | self.sessions.insert(id, msg.addr); 115 | 116 | // auto join session to Main room 117 | self.rooms.get_mut(&"Main".to_owned()).unwrap().insert(id); 118 | 119 | // send id back 120 | id 121 | } 122 | } 123 | 124 | /// Handler for Disconnect message. 125 | impl Handler for ChatServer { 126 | type Result = (); 127 | 128 | fn handle(&mut self, msg: Disconnect, _: &mut Context) { 129 | println!("Someone disconnected"); 130 | 131 | let mut rooms: Vec = Vec::new(); 132 | 133 | // remove address 134 | if self.sessions.remove(&msg.id).is_some() { 135 | // remove session from all rooms 136 | for (name, sessions) in &mut self.rooms { 137 | if sessions.remove(&msg.id) { 138 | rooms.push(name.to_owned()); 139 | } 140 | } 141 | } 142 | // send message to other users 143 | for room in rooms { 144 | self.send_message(&room, "Someone disconnected", 0); 145 | } 146 | } 147 | } 148 | 149 | /// Handler for Message message. 150 | impl Handler for ChatServer { 151 | type Result = (); 152 | 153 | fn handle(&mut self, msg: Message, _: &mut Context) { 154 | self.send_message(&msg.room, msg.msg.as_str(), msg.id); 155 | } 156 | } 157 | 158 | /// Handler for `ListRooms` message. 159 | impl Handler for ChatServer { 160 | type Result = MessageResult; 161 | 162 | fn handle(&mut self, _: ListRooms, _: &mut Context) -> Self::Result { 163 | let mut rooms = Vec::new(); 164 | 165 | for key in self.rooms.keys() { 166 | rooms.push(key.to_owned()) 167 | } 168 | 169 | MessageResult(rooms) 170 | } 171 | } 172 | 173 | /// Join room, send disconnect message to old room 174 | /// send join message to new room 175 | impl Handler for ChatServer { 176 | type Result = (); 177 | 178 | fn handle(&mut self, msg: Join, _: &mut Context) { 179 | let Join { id, name } = msg; 180 | let mut rooms = Vec::new(); 181 | 182 | // remove session from all rooms 183 | for (n, sessions) in &mut self.rooms { 184 | if sessions.remove(&id) { 185 | rooms.push(n.to_owned()); 186 | } 187 | } 188 | // send message to other users 189 | for room in rooms { 190 | self.send_message(&room, "Someone disconnected", 0); 191 | } 192 | 193 | if self.rooms.get_mut(&name).is_none() { 194 | self.rooms.insert(name.clone(), HashSet::new()); 195 | } 196 | self.send_message(&name, "Someone connected", id); 197 | self.rooms.get_mut(&name).unwrap().insert(id); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/address/queue.rs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. 2 | * Redistribution and use in source and binary forms, with or without 3 | * modification, are permitted provided that the following conditions are 4 | * met: */ 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, 7 | // this list of conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 15 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 16 | // EVENT SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 18 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 19 | // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 20 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 22 | // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | // 24 | // The views and conclusions contained in the software and documentation are 25 | // those of the authors and should not be interpreted as representing official 26 | // policies, either expressed or implied, of Dmitry Vyukov. 27 | // 28 | 29 | //! A mostly lock-free multi-producer, single consumer queue. 30 | //! 31 | //! This module contains an implementation of a concurrent MPSC queue. This 32 | //! queue can be used to share data between threads, and is also used as the 33 | //! building block of channels in rust. 34 | //! 35 | //! Note that the current implementation of this queue has a caveat of the `pop` 36 | //! method, and see the method for more information about it. Due to this 37 | //! caveat, this queue may not be appropriate for all use-cases. 38 | 39 | // http://www.1024cores.net/home/lock-free-algorithms 40 | // /queues/non-intrusive-mpsc-node-based-queue 41 | 42 | // NOTE: this implementation is lifted from the standard library and only 43 | // slightly modified 44 | 45 | pub use self::PopResult::*; 46 | use std::prelude::v1::*; 47 | 48 | use std::cell::UnsafeCell; 49 | use std::ptr; 50 | use std::sync::atomic::{AtomicPtr, Ordering}; 51 | 52 | /// A result of the `pop` function. 53 | pub enum PopResult { 54 | /// Some data has been popped 55 | Data(T), 56 | /// The queue is empty 57 | Empty, 58 | /// The queue is in an inconsistent state. Popping data should succeed, but 59 | /// some pushers have yet to make enough progress in order allow a pop to 60 | /// succeed. It is recommended that a pop() occur "in the near future" in 61 | /// order to see if the sender has made progress or not 62 | Inconsistent, 63 | } 64 | 65 | #[derive(Debug)] 66 | struct Node { 67 | next: AtomicPtr>, 68 | value: Option, 69 | } 70 | 71 | /// The multi-producer single-consumer structure. This is not cloneable, but it 72 | /// may be safely shared so long as it is guaranteed that there is only one 73 | /// popper at a time (many pushers are allowed). 74 | #[derive(Debug)] 75 | pub struct Queue { 76 | head: AtomicPtr>, 77 | tail: UnsafeCell<*mut Node>, 78 | } 79 | 80 | unsafe impl Send for Queue {} 81 | unsafe impl Sync for Queue {} 82 | 83 | impl Node { 84 | unsafe fn new(v: Option) -> *mut Node { 85 | Box::into_raw(Box::new(Node { 86 | next: AtomicPtr::new(ptr::null_mut()), 87 | value: v, 88 | })) 89 | } 90 | } 91 | 92 | impl Queue { 93 | /// Creates a new queue that is safe to share among multiple producers and 94 | /// one consumer. 95 | pub fn new() -> Queue { 96 | let stub = unsafe { Node::new(None) }; 97 | Queue { 98 | head: AtomicPtr::new(stub), 99 | tail: UnsafeCell::new(stub), 100 | } 101 | } 102 | 103 | /// Pushes a new value onto this queue. 104 | pub fn push(&self, t: T) { 105 | unsafe { 106 | let n = Node::new(Some(t)); 107 | let prev = self.head.swap(n, Ordering::AcqRel); 108 | (*prev).next.store(n, Ordering::Release); 109 | } 110 | } 111 | 112 | /// Pops some data from this queue. 113 | /// 114 | /// Note that the current implementation means that this function cannot 115 | /// return `Option`. It is possible for this queue to be in an 116 | /// inconsistent state where many pushes have succeeded and completely 117 | /// finished, but pops cannot return `Some(t)`. This inconsistent state 118 | /// happens when a pusher is preempted at an inopportune moment. 119 | /// 120 | /// This inconsistent state means that this queue does indeed have data, but 121 | /// it does not currently have access to it at this time. 122 | /// 123 | /// This function is unsafe because only one thread can call it at a time. 124 | pub unsafe fn pop(&self) -> PopResult { 125 | let tail = *self.tail.get(); 126 | let next = (*tail).next.load(Ordering::Acquire); 127 | 128 | if !next.is_null() { 129 | *self.tail.get() = next; 130 | assert!((*tail).value.is_none()); 131 | assert!((*next).value.is_some()); 132 | let ret = (*next).value.take().unwrap(); 133 | drop(Box::from_raw(tail)); 134 | return Data(ret); 135 | } 136 | 137 | if self.head.load(Ordering::Acquire) == tail { 138 | Empty 139 | } else { 140 | Inconsistent 141 | } 142 | } 143 | } 144 | 145 | impl Drop for Queue { 146 | fn drop(&mut self) { 147 | unsafe { 148 | let mut cur = *self.tail.get(); 149 | while !cur.is_null() { 150 | let next = (*cur).next.load(Ordering::Relaxed); 151 | drop(Box::from_raw(cur)); 152 | cur = next; 153 | } 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use futures::unsync::oneshot; 4 | use futures::{Async, Future, Poll, Stream}; 5 | use tokio_timer::{Delay, Interval}; 6 | 7 | use actor::Actor; 8 | use clock; 9 | use fut::{ActorFuture, ActorStream}; 10 | 11 | pub struct Condition 12 | where 13 | T: Clone, 14 | { 15 | waiters: Vec>, 16 | } 17 | 18 | impl Condition 19 | where 20 | T: Clone, 21 | { 22 | pub fn wait(&mut self) -> oneshot::Receiver { 23 | let (tx, rx) = oneshot::channel(); 24 | self.waiters.push(tx); 25 | rx 26 | } 27 | 28 | pub fn set(self, result: T) { 29 | for waiter in self.waiters { 30 | let _ = waiter.send(result.clone()); 31 | } 32 | } 33 | } 34 | 35 | impl Default for Condition 36 | where 37 | T: Clone, 38 | { 39 | fn default() -> Self { 40 | Condition { 41 | waiters: Vec::new(), 42 | } 43 | } 44 | } 45 | 46 | /// An `ActorFuture` that runs a function in the actor's context after a specified amount of time. 47 | /// 48 | /// Unless you specifically need access to the future, use [`Context::run_later`] instead. 49 | /// 50 | /// [`Context::run_later`]: ../prelude/trait.AsyncContext.html#method.run_later 51 | /// 52 | /// ```rust 53 | /// # #[macro_use] extern crate actix; 54 | /// # extern crate futures; 55 | /// # use std::io; 56 | /// use std::time::Duration; 57 | /// use actix::prelude::*; 58 | /// use actix::utils::TimerFunc; 59 | /// 60 | /// struct MyActor; 61 | /// 62 | /// impl MyActor { 63 | /// fn stop(&mut self, context: &mut Context) { 64 | /// System::current().stop(); 65 | /// } 66 | /// } 67 | /// 68 | /// impl Actor for MyActor { 69 | /// type Context = Context; 70 | /// 71 | /// fn started(&mut self, context: &mut Context) { 72 | /// // spawn a delayed future into our context 73 | /// TimerFunc::new(Duration::from_millis(100), Self::stop) 74 | /// .spawn(context); 75 | /// } 76 | /// } 77 | /// # fn main() { 78 | /// # let sys = System::new("example"); 79 | /// # let addr = MyActor.start(); 80 | /// # sys.run(); 81 | /// # } 82 | /// ``` 83 | #[must_use = "future do nothing unless polled"] 84 | pub struct TimerFunc 85 | where 86 | A: Actor, 87 | { 88 | f: Option>>, 89 | timeout: Delay, 90 | } 91 | 92 | impl TimerFunc 93 | where 94 | A: Actor, 95 | { 96 | /// Creates a new `TimerFunc` with the given duration. 97 | pub fn new(timeout: Duration, f: F) -> TimerFunc 98 | where 99 | F: FnOnce(&mut A, &mut A::Context) + 'static, 100 | { 101 | TimerFunc { 102 | f: Some(Box::new(f)), 103 | timeout: Delay::new(clock::now() + timeout), 104 | } 105 | } 106 | } 107 | 108 | trait TimerFuncBox: 'static { 109 | fn call(self: Box, &mut A, &mut A::Context); 110 | } 111 | 112 | impl TimerFuncBox for F { 113 | #[cfg_attr(feature = "cargo-clippy", allow(boxed_local))] 114 | fn call(self: Box, act: &mut A, ctx: &mut A::Context) { 115 | (*self)(act, ctx) 116 | } 117 | } 118 | 119 | impl ActorFuture for TimerFunc 120 | where 121 | A: Actor, 122 | { 123 | type Item = (); 124 | type Error = (); 125 | type Actor = A; 126 | 127 | fn poll( 128 | &mut self, 129 | act: &mut Self::Actor, 130 | ctx: &mut ::Context, 131 | ) -> Poll { 132 | match self.timeout.poll() { 133 | Ok(Async::Ready(_)) => { 134 | if let Some(f) = self.f.take() { 135 | f.call(act, ctx); 136 | } 137 | Ok(Async::Ready(())) 138 | } 139 | Ok(Async::NotReady) => Ok(Async::NotReady), 140 | Err(_) => unreachable!(), 141 | } 142 | } 143 | } 144 | 145 | /// An `ActorStream` that periodically runs a function in the actor's context. 146 | /// 147 | /// Unless you specifically need access to the future, use [`Context::run_interval`] instead. 148 | /// 149 | /// [`Context::run_interval`]: ../prelude/trait.AsyncContext.html#method.run_interval 150 | /// 151 | /// ```rust 152 | /// # #[macro_use] extern crate actix; 153 | /// # extern crate futures; 154 | /// # use std::io; 155 | /// use std::time::Duration; 156 | /// use actix::prelude::*; 157 | /// use actix::utils::IntervalFunc; 158 | /// 159 | /// struct MyActor; 160 | /// 161 | /// impl MyActor { 162 | /// fn tick(&mut self, context: &mut Context) { 163 | /// println!("tick"); 164 | /// } 165 | /// } 166 | /// 167 | /// impl Actor for MyActor { 168 | /// type Context = Context; 169 | /// 170 | /// fn started(&mut self, context: &mut Context) { 171 | /// // spawn an interval stream into our context 172 | /// IntervalFunc::new(Duration::from_millis(100), Self::tick) 173 | /// .finish() 174 | /// .spawn(context); 175 | /// # context.run_later(Duration::from_millis(200), |_, _| System::current().stop()); 176 | /// } 177 | /// } 178 | /// # fn main() { 179 | /// # let sys = System::new("example"); 180 | /// # let addr = MyActor.start(); 181 | /// # sys.run(); 182 | /// # } 183 | /// ``` 184 | #[must_use = "future do nothing unless polled"] 185 | pub struct IntervalFunc { 186 | f: Box>, 187 | interval: Interval, 188 | } 189 | 190 | impl IntervalFunc { 191 | /// Creates a new `IntervalFunc` with the given interval duration. 192 | pub fn new(timeout: Duration, f: F) -> IntervalFunc 193 | where 194 | F: FnMut(&mut A, &mut A::Context) + 'static, 195 | { 196 | Self { 197 | f: Box::new(f), 198 | interval: Interval::new(clock::now() + timeout, timeout), 199 | } 200 | } 201 | } 202 | 203 | trait IntervalFuncBox: 'static { 204 | fn call(&mut self, &mut A, &mut A::Context); 205 | } 206 | 207 | impl IntervalFuncBox for F { 208 | #[cfg_attr(feature = "cargo-clippy", allow(boxed_local))] 209 | fn call(&mut self, act: &mut A, ctx: &mut A::Context) { 210 | self(act, ctx) 211 | } 212 | } 213 | 214 | impl ActorStream for IntervalFunc { 215 | type Item = (); 216 | type Error = (); 217 | type Actor = A; 218 | 219 | fn poll( 220 | &mut self, 221 | act: &mut Self::Actor, 222 | ctx: &mut ::Context, 223 | ) -> Poll, Self::Error> { 224 | loop { 225 | match self.interval.poll() { 226 | Ok(Async::Ready(_)) => { 227 | //Interval Stream cannot return None 228 | self.f.call(act, ctx); 229 | } 230 | Ok(Async::NotReady) => return Ok(Async::NotReady), 231 | Err(_) => unreachable!(), 232 | } 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /tests/test_actor.rs: -------------------------------------------------------------------------------- 1 | extern crate actix; 2 | extern crate futures; 3 | extern crate tokio; 4 | extern crate tokio_timer; 5 | 6 | use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; 7 | use std::sync; 8 | use std::sync::Arc; 9 | use std::time::{Duration, Instant}; 10 | use std::thread; 11 | 12 | use actix::prelude::*; 13 | use futures::{future, Future}; 14 | use tokio_timer::Delay; 15 | 16 | #[derive(Debug)] 17 | struct Num(usize); 18 | 19 | impl Message for Num { 20 | type Result = (); 21 | } 22 | 23 | struct MyActor(Arc, Arc, Running); 24 | 25 | impl Actor for MyActor { 26 | type Context = actix::Context; 27 | 28 | fn stopping(&mut self, _: &mut Self::Context) -> Running { 29 | System::current().stop(); 30 | Running::Stop 31 | } 32 | } 33 | 34 | impl StreamHandler for MyActor { 35 | fn handle(&mut self, msg: Num, _: &mut Context) { 36 | self.0.fetch_add(msg.0, Ordering::Relaxed); 37 | } 38 | 39 | fn error(&mut self, _: (), _: &mut Context) -> Running { 40 | self.0.fetch_add(1, Ordering::Relaxed); 41 | self.2 42 | } 43 | 44 | fn finished(&mut self, _: &mut Context) { 45 | self.1.store(true, Ordering::Relaxed); 46 | } 47 | } 48 | 49 | #[test] 50 | fn test_stream() { 51 | let count = Arc::new(AtomicUsize::new(0)); 52 | let err = Arc::new(AtomicBool::new(false)); 53 | let items = vec![Num(1), Num(1), Num(1), Num(1), Num(1), Num(1), Num(1)]; 54 | 55 | let act_count = Arc::clone(&count); 56 | let act_err = Arc::clone(&err); 57 | 58 | System::run(move || { 59 | MyActor::create(move |ctx| { 60 | MyActor::add_stream(futures::stream::iter_ok::<_, ()>(items), ctx); 61 | MyActor(act_count, act_err, Running::Stop) 62 | }); 63 | }); 64 | 65 | assert_eq!(count.load(Ordering::Relaxed), 7); 66 | assert!(err.load(Ordering::Relaxed)); 67 | } 68 | 69 | #[test] 70 | fn test_stream_with_error() { 71 | let count = Arc::new(AtomicUsize::new(0)); 72 | let error = Arc::new(AtomicBool::new(false)); 73 | let items = vec![ 74 | Ok(Num(1)), 75 | Ok(Num(1)), 76 | Err(()), 77 | Ok(Num(1)), 78 | Ok(Num(1)), 79 | Ok(Num(1)), 80 | Ok(Num(1)), 81 | Ok(Num(1)), 82 | ]; 83 | 84 | let act_count = Arc::clone(&count); 85 | let act_error = Arc::clone(&error); 86 | 87 | System::run(move || { 88 | MyActor::create(move |ctx| { 89 | MyActor::add_stream(futures::stream::iter_result(items), ctx); 90 | MyActor(act_count, act_error, Running::Stop) 91 | }); 92 | }); 93 | 94 | assert_eq!(count.load(Ordering::Relaxed), 3); 95 | assert!(error.load(Ordering::Relaxed)); 96 | } 97 | 98 | #[test] 99 | fn test_stream_with_error_no_stop() { 100 | let count = Arc::new(AtomicUsize::new(0)); 101 | let error = Arc::new(AtomicBool::new(false)); 102 | let items = vec![ 103 | Ok(Num(1)), 104 | Ok(Num(1)), 105 | Err(()), 106 | Ok(Num(1)), 107 | Ok(Num(1)), 108 | Ok(Num(1)), 109 | Ok(Num(1)), 110 | Ok(Num(1)), 111 | ]; 112 | 113 | let act_count = Arc::clone(&count); 114 | let act_error = Arc::clone(&error); 115 | 116 | System::run(move || { 117 | MyActor::create(move |ctx| { 118 | MyActor::add_stream(futures::stream::iter_result(items), ctx); 119 | MyActor(act_count, act_error, Running::Continue) 120 | }); 121 | }); 122 | assert_eq!(count.load(Ordering::Relaxed), 8); 123 | assert!(error.load(Ordering::Relaxed)); 124 | } 125 | 126 | struct MySyncActor { 127 | started: Arc, 128 | stopping: Arc, 129 | stopped: Arc, 130 | msgs: Arc, 131 | stop: bool, 132 | } 133 | 134 | impl Actor for MySyncActor { 135 | type Context = actix::SyncContext; 136 | 137 | fn started(&mut self, _: &mut Self::Context) { 138 | self.started.fetch_add(1, Ordering::Relaxed); 139 | } 140 | fn stopping(&mut self, _: &mut Self::Context) -> Running { 141 | self.stopping.fetch_add(1, Ordering::Relaxed); 142 | Running::Continue 143 | } 144 | fn stopped(&mut self, _: &mut Self::Context) { 145 | self.stopped.fetch_add(1, Ordering::Relaxed); 146 | } 147 | } 148 | 149 | impl actix::Handler for MySyncActor { 150 | type Result = (); 151 | 152 | fn handle(&mut self, msg: Num, ctx: &mut Self::Context) { 153 | self.msgs.fetch_add(msg.0, Ordering::Relaxed); 154 | if self.stop { 155 | ctx.stop(); 156 | } 157 | } 158 | } 159 | 160 | #[test] 161 | fn test_restart_sync_actor() { 162 | let started = Arc::new(AtomicUsize::new(0)); 163 | let stopping = Arc::new(AtomicUsize::new(0)); 164 | let stopped = Arc::new(AtomicUsize::new(0)); 165 | let msgs = Arc::new(AtomicUsize::new(0)); 166 | 167 | let started1 = Arc::clone(&started); 168 | let stopping1 = Arc::clone(&stopping); 169 | let stopped1 = Arc::clone(&stopped); 170 | let msgs1 = Arc::clone(&msgs); 171 | 172 | System::run(move || { 173 | let addr = SyncArbiter::start(1, move || MySyncActor { 174 | started: Arc::clone(&started1), 175 | stopping: Arc::clone(&stopping1), 176 | stopped: Arc::clone(&stopped1), 177 | msgs: Arc::clone(&msgs1), 178 | stop: started1.load(Ordering::Relaxed) == 0, 179 | }); 180 | addr.do_send(Num(2)); 181 | 182 | tokio::spawn(addr.send(Num(4)).then(move |_| { 183 | Delay::new(Instant::now() + Duration::new(0, 1_000_000)).then(move |_| { 184 | System::current().stop(); 185 | future::result(Ok(())) 186 | }) 187 | })); 188 | }); 189 | assert_eq!(started.load(Ordering::Relaxed), 2); 190 | assert_eq!(stopping.load(Ordering::Relaxed), 2); 191 | assert_eq!(stopped.load(Ordering::Relaxed), 2); 192 | assert_eq!(msgs.load(Ordering::Relaxed), 6); 193 | } 194 | 195 | struct IntervalActor { 196 | elapses_left: usize, 197 | sender: sync::mpsc::Sender, 198 | instant: Option 199 | } 200 | 201 | impl IntervalActor { 202 | pub fn new(elapses_left: usize, sender: sync::mpsc::Sender) -> Self { 203 | Self { 204 | //We stop at 0, so add 1 to make number of intervals equal to elapses_left 205 | elapses_left: elapses_left + 1, 206 | sender, 207 | instant: None 208 | } 209 | } 210 | } 211 | 212 | impl Actor for IntervalActor { 213 | type Context = actix::Context; 214 | 215 | fn started(&mut self, ctx: &mut Self::Context) { 216 | self.instant = Some(Instant::now()); 217 | 218 | ctx.run_interval(Duration::from_millis(110), move |act, ctx| { 219 | act.elapses_left -= 1; 220 | 221 | if act.elapses_left == 0 { 222 | act.sender.send(act.instant.take().expect("To have Instant")).expect("To send result"); 223 | ctx.stop(); 224 | System::current().stop(); 225 | } 226 | }); 227 | } 228 | } 229 | 230 | #[test] 231 | fn test_run_interval() { 232 | const MAX_WAIT: Duration = Duration::from_millis(10_000); 233 | 234 | let (sender, receiver) = sync::mpsc::channel(); 235 | thread::spawn(move || { 236 | System::run(move || { 237 | let _addr = IntervalActor::new(10, sender).start(); 238 | }); 239 | }); 240 | let result = receiver.recv_timeout(MAX_WAIT).expect("To receive response in time"); 241 | //We wait 10 intervals by ~100ms 242 | assert_eq!(result.elapsed().as_secs(), 1); 243 | } 244 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # CHANGES 2 | 3 | ## [0.7.10] (2019-01-16) 4 | 5 | ### Changed 6 | 7 | - Added `WeakAddr` to weak reference an actor 8 | 9 | ## [0.7.9] (2018-12-11) 10 | 11 | ### Changed 12 | 13 | - Removed `actix` module from prelude. See rationale in #161 14 | 15 | ## [0.7.8] (2018-12-05) 16 | 17 | ### Changed 18 | 19 | - Update `crossbeam-channel` to 0.3 and `parking_lot` to 0.7 20 | 21 | ## [0.7.7] (2018-11-22) 22 | 23 | ### Added 24 | 25 | - Impl `Into>` for `Addr` 26 | 27 | ## [0.7.6] (2018-11-08) 28 | 29 | ### Changed 30 | 31 | - Use `trust-dns-resolver` 0.10.0. 32 | 33 | - Make `System::stop_with_code` public. 34 | 35 | ## [0.7.5] (2018-10-10) 36 | 37 | ### Added 38 | 39 | - Introduce the `clock` module to allow overriding and mocking the system clock 40 | based on `tokio_timer`. 41 | 42 | - System now has `System::builder()` which allows overriding the system clock 43 | with a custom instance. `Arbiter::builder()` can now also override the system 44 | clock. The default is to inherit from the system. 45 | 46 | - New utility classes `TimerFunc` and `IntervalFunc` in the `utils` module. 47 | 48 | - Implement `failure::Fail` for `SendError`. 49 | 50 | - Implement `Debug` for multiple public types: `AddressSender`, `Addr`, `Arbiter`, `Context`, `ContextParts`, `ContextFut`, `Response`, `ActorResponse`, `Mailbox`, `SystemRegistry`, `Supervisor`, `System`, `SystemRunner`, `SystemArbiter`. #135 51 | 52 | 53 | ### Changed 54 | 55 | - No longer perform unnecessary clone of `Addr` in `SystemRegistry::set`. 56 | 57 | - Set min trust-dns version 58 | 59 | 60 | ### Fixed 61 | 62 | - fix infinite loop in ContextFut::poll() caused by late cancel_future() #147 63 | 64 | 65 | ## [0.7.4] (2018-08-27) 66 | 67 | ### Added 68 | 69 | * Introduce method `query` to determine whether there is running actor in registry. 70 | 71 | * Return back `mocker` module. 72 | 73 | 74 | ## [0.7.3] (2018-07-30) 75 | 76 | ### Fixed 77 | 78 | * Parked messages not getting processed #120 79 | 80 | 81 | ## [0.7.2] (2018-07-24) 82 | 83 | ### Changed 84 | 85 | * Use actix-derive 0.3 86 | 87 | 88 | ## [0.7.1] (2018-07-20) 89 | 90 | ### Added 91 | 92 | * Arbiter now has `Arbiter::builder()` which allows opt-in of behavior to stop 93 | the actor system on uncaught panic in any arbiter thread. See #111 for examples. 94 | 95 | * Allow to set custom system service actor via `SystemRegistry::set()` method. 96 | 97 | ### Fixed 98 | 99 | * `AsyncContext::run_interval` does not fire callback immediately, instead it fires after specified duration. 100 | 101 | 102 | ## [0.7.0] (2018-07-05) 103 | 104 | ### Changed 105 | 106 | * Context impl refactoring, fix potential UB 107 | 108 | ### Added 109 | 110 | * Implemented `Eq`, `PartialEq`, and `Hash` for `actix::Addr` 111 | 112 | * Implemented `Eq`, `PartialEq`, and `Hash` for `actix::Recipient` 113 | 114 | 115 | ## [0.6.2] (2018-06-xx) 116 | 117 | ### Changed 118 | 119 | * Breaking change: Restore `StreamHandler` from 0.5, new `StreamHandler` renamed to `StreamHandler2` 120 | 121 | 122 | ## [0.6.1] (2018-06-19) 123 | 124 | ### Added 125 | 126 | * Added `actix::run()` and `actix::spawn()` helper functions 127 | 128 | 129 | ### Changed 130 | 131 | * Use parking_lot 0.6 132 | 133 | 134 | ### Fixed 135 | 136 | * Fixed potential memory unsafety 137 | 138 | 139 | ## [0.6.0] (2018-06-18) 140 | 141 | ### Changed 142 | 143 | * Use tokio 144 | 145 | * `System` and `Arbiter` refactored 146 | 147 | * `Arbiter::handle()` is not available anymore. 148 | Use `Arbiter::spawn()` and `Arbiter::spawn_fn()` instead. 149 | 150 | * `StreamHandler` trait refactored. 151 | 152 | * Min rustc version - 1.26 153 | 154 | 155 | ## 0.5.7 (2018-05-17) 156 | 157 | * Stop sync actor if sender is dead. 158 | 159 | 160 | ## 0.5.6 (2018-04-17) 161 | 162 | * Fix index usage during iteration for future cancellation #67 163 | 164 | 165 | ## 0.5.5 (2018-03-19) 166 | 167 | * Fix polling of wrong wait future after completion 168 | 169 | 170 | ## 0.5.4 (2018-03-16) 171 | 172 | * Always complete actor lifecycle (i.e. Actor::started()) 173 | 174 | 175 | ## 0.5.3 (2018-03-08) 176 | 177 | * Panic after cancelling stream future #58 178 | 179 | 180 | ## 0.5.2 (2018-03-06) 181 | 182 | * Allow to set timeout for Connect message #56 183 | 184 | 185 | ## 0.5.1 (2018-03-02) 186 | 187 | * Internal state is alive during `stopping` period. 188 | 189 | * Do not send StopArbiter message to system arbiter during system shutdown #53 190 | 191 | 192 | ## 0.5.0 (2018-02-17) 193 | 194 | * Address/Recipient is generic over actor destination 195 | 196 | * Make rules of actor stopping more strict 197 | 198 | * Use bounded channels for actor communications 199 | 200 | * Add dns resolver and tcp connector utility actor 201 | 202 | * Add `StreamHandler` trait for stream handling 203 | 204 | * Add `Context::handle()` method, currently running future handle 205 | 206 | * Add `actix::io` helper types for `AsyncWrite` related types 207 | 208 | * Drop FramedContext 209 | 210 | 211 | ## 0.4.5 (2018-01-23) 212 | 213 | * Refactor context implementation 214 | 215 | * Refactor Supervisor type 216 | 217 | * Allow to use `Framed` instances with normal `Context` 218 | 219 | 220 | ## 0.4.4 (2018-01-19) 221 | 222 | * Add `Clone` implementation for `Box + Send>` 223 | 224 | * Stop stream polling if context is waiting for future completion 225 | 226 | * Upgraded address stops working after all references are dropped #38 227 | 228 | 229 | ## 0.4.3 (2018-01-09) 230 | 231 | * Cleanup `FramedActor` error and close state handling. 232 | 233 | * Do not exit early from framed polling 234 | 235 | 236 | ## 0.4.2 (2018-01-07) 237 | 238 | * Cleanup actor stopping process 239 | 240 | * Unify context implementation 241 | 242 | 243 | ## 0.4.1 (2018-01-06) 244 | 245 | * Remove StreamHandler requirements from add_message_stream() 246 | 247 | * Fix items length check 248 | 249 | 250 | ## 0.4.0 (2018-01-05) 251 | 252 | * Simplify `Handler` trait (E type removed). 253 | 254 | * Use associated type for handler response for `Handler` trait. 255 | 256 | * Added framed `drain` method. 257 | 258 | * Allow to replace framed object in framed context. 259 | 260 | * Enable signal actor by default, make it compatible with windows. 261 | 262 | * Added `SyncContext::restart()` method, which allow to restart sync actor. 263 | 264 | * Changed behaviour of `Address::call`, if request get drop message cancels. 265 | 266 | 267 | ## 0.3.5 (2017-12-23) 268 | 269 | * Re-export `actix_derive` package 270 | 271 | * Added conversion implementation `From> for Response` 272 | 273 | * Expose the Framed underneath FramedContext #29 274 | 275 | 276 | ## 0.3.4 (2017-12-20) 277 | 278 | * Fix memory leak when sending messages recursively to self #28 279 | 280 | * Add convenience impl for boxed Subscriber objects. #27 281 | 282 | * Add `ActorStream::fold()` method. 283 | 284 | * Add helper combinator `Stream::finish()` method. 285 | 286 | 287 | ## 0.3.3 (2017-11-21) 288 | 289 | * SystemRegistry does not store created actor #21 290 | 291 | 292 | ## 0.3.2 (2017-11-06) 293 | 294 | * Disable `signal` feature by default 295 | 296 | 297 | ## 0.3.1 (2017-10-30) 298 | 299 | * Simplify `ToEnvelope` trait, do not generalize over Message type. 300 | 301 | * `ActorContext` requires `ToEnvelope` trait. 302 | 303 | * Added `Subscriber::subscriber() -> Box` 304 | 305 | * Simplify `ActorContext` trait, it does not need to know about `Actor` 306 | 307 | * Cancel `notify` and `run_later` futures on context stop 308 | 309 | 310 | ## 0.3.0 (2017-10-23) 311 | 312 | * Added `Either` future 313 | 314 | * Message has to provide `ResponseType` impl instead of Actor 315 | 316 | 317 | ## 0.2.0 (2017-10-17) 318 | 319 | * Added `ActorStream` 320 | 321 | 322 | ## 0.1.0 (2017-10-11) 323 | 324 | * First release 325 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Actix is a rust actors framework 2 | //! 3 | //! [Actors](https://actix.github.io/actix/actix/trait.Actor.html) are 4 | //! objects which encapsulate state and behavior, they communicate 5 | //! exclusively by exchanging messages. Actix actors are implemented 6 | //! on top of [Tokio](https://tokio.rs). Multiple actors can run in 7 | //! same thread. Actors can run in multiple threads using the 8 | //! [`Arbiter`](struct.Arbiter.html) API. Actors exchange typed 9 | //! messages. 10 | //! 11 | //! ## Documentation 12 | //! 13 | //! * [User Guide](https://actix.rs/book/actix/) 14 | //! * [Chat on gitter](https://gitter.im/actix/actix) 15 | //! * [GitHub repository](https://github.com/actix/actix) 16 | //! * [Cargo package](https://crates.io/crates/actix) 17 | //! * Minimum supported Rust version: 1.26 or later 18 | //! 19 | //! ## Features 20 | //! 21 | //! * Async/Sync actors. 22 | //! * Actor communication in a local/thread context. 23 | //! * Using Futures for asynchronous message handling. 24 | //! * HTTP1/HTTP2 support ([actix-web](https://github.com/actix/actix-web)) 25 | //! * Actor supervision. 26 | //! * Typed messages (No `Any` type). Generic messages are allowed. 27 | //! 28 | //! ## Package feature 29 | //! 30 | //! * `resolver` - enables dns resolver actor, `actix::actors::resolver` 31 | //! * `signal` - enables signals handling actor 32 | //! 33 | //! ## Tokio runtime 34 | //! 35 | //! At the moment actix uses 36 | //! [`current_thread`](https://docs.rs/tokio/0.1.13/tokio/runtime/current_thread/index.html) runtime. 37 | //! 38 | //! While it provides minimum overhead, it has its own limits: 39 | //! 40 | //! - You cannot use tokio's async file I/O, as it relies on blocking calls that are not available 41 | //! in `current_thread` 42 | //! - `Stdin`, `Stderr` and `Stdout` from `tokio::io` are the same as file I/O in that regard and 43 | //! cannot be used in asynchronous manner in actix. 44 | #[macro_use] 45 | extern crate log; 46 | extern crate bytes; 47 | extern crate crossbeam_channel; 48 | extern crate fnv; 49 | #[cfg(unix)] 50 | extern crate libc; 51 | extern crate parking_lot; 52 | extern crate smallvec; 53 | extern crate uuid; 54 | #[macro_use] 55 | extern crate bitflags; 56 | #[macro_use] 57 | extern crate futures; 58 | extern crate tokio; 59 | extern crate tokio_codec; 60 | extern crate tokio_executor; 61 | extern crate tokio_io; 62 | extern crate tokio_reactor; 63 | extern crate tokio_tcp; 64 | extern crate tokio_timer; 65 | 66 | #[macro_use] 67 | extern crate failure; 68 | 69 | #[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))] 70 | #[allow(unused_imports)] 71 | #[macro_use] 72 | extern crate actix_derive; 73 | 74 | #[doc(hidden)] 75 | pub use actix_derive::*; 76 | 77 | mod actor; 78 | mod arbiter; 79 | mod context; 80 | mod contextimpl; 81 | mod contextitems; 82 | mod handler; 83 | mod stream; 84 | mod supervisor; 85 | mod system; 86 | 87 | mod address; 88 | mod mailbox; 89 | 90 | pub mod actors; 91 | pub mod clock; 92 | pub mod fut; 93 | pub mod io; 94 | pub mod msgs; 95 | pub mod registry; 96 | pub mod sync; 97 | pub mod utils; 98 | 99 | pub use actor::{ 100 | Actor, ActorContext, ActorState, AsyncContext, Running, SpawnHandle, Supervised, 101 | }; 102 | pub use address::{Addr, MailboxError, Recipient, WeakAddr}; 103 | pub use arbiter::{Arbiter, ArbiterBuilder}; 104 | pub use context::Context; 105 | pub use fut::{ActorFuture, ActorStream, FinishStream, WrapFuture, WrapStream}; 106 | pub use handler::{ 107 | ActorResponse, Handler, Message, MessageResult, Response, ResponseActFuture, 108 | ResponseFuture, 109 | }; 110 | pub use registry::{ArbiterService, Registry, SystemRegistry, SystemService}; 111 | pub use stream::StreamHandler; 112 | pub use supervisor::Supervisor; 113 | pub use sync::{SyncArbiter, SyncContext}; 114 | pub use system::{System, SystemRunner}; 115 | 116 | #[doc(hidden)] 117 | pub use context::ContextFutureSpawner; 118 | 119 | pub mod prelude { 120 | //! The `actix` prelude. 121 | //! 122 | //! The purpose of this module is to alleviate imports of many common actix 123 | //! traits by adding a glob import to the top of actix heavy modules: 124 | //! 125 | //! ``` 126 | //! # #![allow(unused_imports)] 127 | //! use actix::prelude::*; 128 | //! ``` 129 | 130 | #[doc(hidden)] 131 | pub use actix_derive::*; 132 | 133 | pub use actor::{ 134 | Actor, ActorContext, ActorState, AsyncContext, Running, SpawnHandle, Supervised, 135 | }; 136 | pub use address::{ 137 | Addr, MailboxError, Recipient, RecipientRequest, Request, SendError, 138 | }; 139 | pub use arbiter::Arbiter; 140 | pub use context::{Context, ContextFutureSpawner}; 141 | pub use fut::{ActorFuture, ActorStream, WrapFuture, WrapStream}; 142 | pub use handler::{ 143 | ActorResponse, Handler, Message, MessageResult, Response, ResponseActFuture, 144 | ResponseFuture, 145 | }; 146 | pub use registry::{ArbiterService, SystemService}; 147 | pub use stream::StreamHandler; 148 | pub use supervisor::Supervisor; 149 | pub use sync::{SyncArbiter, SyncContext}; 150 | pub use system::System; 151 | 152 | pub use actors; 153 | pub use dev; 154 | pub use fut; 155 | pub use io; 156 | pub use msgs; 157 | pub use utils::{Condition, IntervalFunc, TimerFunc}; 158 | } 159 | 160 | pub mod dev { 161 | //! The `actix` prelude for library developers. 162 | //! 163 | //! The purpose of this module is to alleviate imports of many common actix 164 | //! traits by adding a glob import to the top of actix heavy modules: 165 | //! 166 | //! ``` 167 | //! # #![allow(unused_imports)] 168 | //! use actix::dev::*; 169 | //! ``` 170 | 171 | pub use prelude::*; 172 | 173 | pub use address::{Envelope, EnvelopeProxy, RecipientRequest, Request, ToEnvelope}; 174 | pub mod channel { 175 | pub use address::channel::{channel, AddressReceiver, AddressSender}; 176 | } 177 | pub use contextimpl::{AsyncContextParts, ContextFut, ContextParts}; 178 | pub use handler::{MessageResponse, ResponseChannel}; 179 | pub use mailbox::Mailbox; 180 | pub use registry::{Registry, SystemRegistry}; 181 | } 182 | 183 | /// Starts the system and executes the supplied future. 184 | /// 185 | /// This function does the following: 186 | /// 187 | /// * Creates and starts the actix system with default configuration. 188 | /// * Spawns the given future onto the current arbiter. 189 | /// * Blocks the current thread until the system shuts down. 190 | /// 191 | /// The `run` function returns when the `System::current().stop()` 192 | /// method gets called. 193 | /// 194 | /// # Examples 195 | /// 196 | /// ``` 197 | /// # extern crate actix; 198 | /// # extern crate tokio_timer; 199 | /// # extern crate futures; 200 | /// # use futures::Future; 201 | /// use std::time::{Duration, Instant}; 202 | /// use tokio_timer::Delay; 203 | /// 204 | /// fn main() { 205 | /// actix::run( 206 | /// || Delay::new(Instant::now() + Duration::from_millis(100)) 207 | /// .map(|_| actix::System::current().stop()) 208 | /// .map_err(|_| ()) 209 | /// ); 210 | /// } 211 | /// ``` 212 | /// 213 | /// # Panics 214 | /// 215 | /// This function panics if the actix system is already running. 216 | pub fn run(f: F) 217 | where 218 | F: FnOnce() -> R, 219 | R: futures::Future + 'static, 220 | { 221 | if System::is_set() { 222 | panic!("System is already running"); 223 | } 224 | 225 | let sys = System::new("Default"); 226 | Arbiter::spawn(f()); 227 | sys.run(); 228 | } 229 | 230 | /// Spawns a future on the current arbiter. 231 | /// 232 | /// # Panics 233 | /// 234 | /// This function panics if the actix system is not running. 235 | pub fn spawn(f: F) 236 | where 237 | F: futures::Future + 'static, 238 | { 239 | if !System::is_set() { 240 | panic!("System is not running"); 241 | } 242 | 243 | Arbiter::spawn(f); 244 | } 245 | -------------------------------------------------------------------------------- /src/actors/signal.rs: -------------------------------------------------------------------------------- 1 | //! An actor implementation of Unix signal handling. 2 | //! 3 | //! This module implements asynchronous signal handling for Actix. For 4 | //! each signal, the `ProcessSignals` actor sends a `Signal` message 5 | //! to all subscribers. To subscribe, send a `Subscribe` message to 6 | //! the `ProcessSignals` actor. 7 | //! 8 | //! # Examples 9 | //! 10 | //! ```rust 11 | //! # extern crate actix; 12 | //! use actix::actors::signal; 13 | //! use actix::prelude::*; 14 | //! 15 | //! struct Signals; 16 | //! 17 | //! impl Actor for Signals { 18 | //! type Context = Context; 19 | //! } 20 | //! 21 | //! // Shutdown system on and of `SIGINT`, `SIGTERM`, `SIGQUIT` signals 22 | //! impl Handler for Signals { 23 | //! type Result = (); 24 | //! 25 | //! fn handle(&mut self, msg: signal::Signal, _: &mut Context) { 26 | //! match msg.0 { 27 | //! signal::SignalType::Int => { 28 | //! println!("SIGINT received, exiting"); 29 | //! System::current().stop(); 30 | //! } 31 | //! signal::SignalType::Hup => { 32 | //! println!("SIGHUP received, reloading"); 33 | //! } 34 | //! signal::SignalType::Term => { 35 | //! println!("SIGTERM received, stopping"); 36 | //! System::current().stop(); 37 | //! } 38 | //! signal::SignalType::Quit => { 39 | //! println!("SIGQUIT received, exiting"); 40 | //! System::current().stop(); 41 | //! } 42 | //! _ => (), 43 | //! } 44 | //! } 45 | //! } 46 | //! 47 | //! fn main() { 48 | //! // initialize system 49 | //! System::run(|| { 50 | //! // Start signals handler 51 | //! let addr = Signals.start(); 52 | //! 53 | //! // send SIGTERM 54 | //! std::thread::spawn(move || { 55 | //! // emulate SIGNTERM 56 | //! addr.do_send(signal::Signal(signal::SignalType::Term)); 57 | //! }); 58 | //! }); 59 | //! 60 | //! std::process::exit(0); 61 | //! } 62 | //! ``` 63 | extern crate tokio_signal; 64 | 65 | #[cfg(unix)] 66 | use self::tokio_signal::unix; 67 | use futures::{Future, Stream}; 68 | #[cfg(unix)] 69 | use libc; 70 | use std; 71 | 72 | use prelude::*; 73 | 74 | /// Represents the different types of signals a process can receive. 75 | #[derive(PartialEq, Clone, Copy, Debug)] 76 | pub enum SignalType { 77 | /// `SIGHUP` 78 | Hup, 79 | /// `SIGINT` 80 | Int, 81 | /// `SIGTERM` 82 | Term, 83 | /// `SIGQUIT` 84 | Quit, 85 | /// `SIGCHLD` 86 | Child, 87 | } 88 | 89 | impl Message for SignalType { 90 | type Result = (); 91 | } 92 | 93 | /// A message representing a received signal. 94 | #[derive(Debug)] 95 | pub struct Signal(pub SignalType); 96 | 97 | impl Message for Signal { 98 | type Result = (); 99 | } 100 | 101 | /// An actor that handles Unix signals. 102 | pub struct ProcessSignals { 103 | subscribers: Vec>, 104 | } 105 | 106 | impl Default for ProcessSignals { 107 | fn default() -> Self { 108 | ProcessSignals { 109 | subscribers: Vec::new(), 110 | } 111 | } 112 | } 113 | 114 | impl Actor for ProcessSignals { 115 | type Context = Context; 116 | } 117 | 118 | impl Supervised for ProcessSignals {} 119 | 120 | impl SystemService for ProcessSignals { 121 | fn service_started(&mut self, ctx: &mut Self::Context) { 122 | // SIGINT 123 | tokio_signal::ctrl_c() 124 | .map_err(|_| ()) 125 | .actfuture() 126 | .map(|sig, _: &mut Self, ctx: &mut Context| { 127 | ctx.add_message_stream(sig.map_err(|_| ()).map(|_| SignalType::Int)) 128 | }) 129 | .spawn(ctx); 130 | 131 | #[cfg(unix)] 132 | { 133 | // SIGHUP 134 | unix::Signal::new(libc::SIGHUP) 135 | .actfuture() 136 | .drop_err() 137 | .map(|sig, _: &mut Self, ctx: &mut Context| { 138 | ctx.add_message_stream(sig.map_err(|_| ()).map(|_| SignalType::Hup)) 139 | }) 140 | .spawn(ctx); 141 | 142 | // SIGTERM 143 | unix::Signal::new(libc::SIGTERM) 144 | .map_err(|_| ()) 145 | .actfuture() 146 | .map(|sig, _: &mut Self, ctx: &mut Context| { 147 | ctx.add_message_stream(sig.map_err(|_| ()).map(|_| SignalType::Term)) 148 | }) 149 | .spawn(ctx); 150 | 151 | // SIGQUIT 152 | unix::Signal::new(libc::SIGQUIT) 153 | .map_err(|_| ()) 154 | .actfuture() 155 | .map(|sig, _: &mut Self, ctx: &mut Context| { 156 | ctx.add_message_stream(sig.map_err(|_| ()).map(|_| SignalType::Quit)) 157 | }) 158 | .spawn(ctx); 159 | 160 | // SIGCHLD 161 | unix::Signal::new(libc::SIGCHLD) 162 | .map_err(|_| ()) 163 | .actfuture() 164 | .map(|sig, _: &mut Self, ctx: &mut Context| { 165 | ctx.add_message_stream( 166 | sig.map_err(|_| ()).map(|_| SignalType::Child), 167 | ) 168 | }) 169 | .spawn(ctx); 170 | } 171 | } 172 | } 173 | 174 | #[doc(hidden)] 175 | impl Handler for ProcessSignals { 176 | type Result = (); 177 | 178 | fn handle(&mut self, sig: SignalType, _: &mut Self::Context) { 179 | let subscribers = std::mem::replace(&mut self.subscribers, Vec::new()); 180 | for subscr in subscribers { 181 | if subscr.do_send(Signal(sig)).is_ok() { 182 | self.subscribers.push(subscr); 183 | } 184 | } 185 | } 186 | } 187 | 188 | /// Subscribe to process signals. 189 | pub struct Subscribe(pub Recipient); 190 | 191 | impl Message for Subscribe { 192 | type Result = (); 193 | } 194 | 195 | impl Handler for ProcessSignals { 196 | type Result = (); 197 | 198 | fn handle(&mut self, msg: Subscribe, _: &mut Self::Context) { 199 | self.subscribers.push(msg.0); 200 | } 201 | } 202 | 203 | /// Default signals handler. 204 | /// 205 | /// This actor sends the `SystemExit` message to the `System` actor 206 | /// for each of the following signals: `SIGINT`, `SIGTERM` and 207 | /// `SIGQUIT`. 208 | pub struct DefaultSignalsHandler; 209 | 210 | impl Default for DefaultSignalsHandler { 211 | fn default() -> Self { 212 | DefaultSignalsHandler 213 | } 214 | } 215 | 216 | impl Actor for DefaultSignalsHandler { 217 | type Context = Context; 218 | 219 | fn started(&mut self, ctx: &mut Self::Context) { 220 | let addr = System::current().registry().get::(); 221 | let slf = ctx.address(); 222 | addr.send(Subscribe(slf.recipient())) 223 | .map(|_| ()) 224 | .map_err(|_| ()) 225 | .into_actor(self) 226 | .wait(ctx) 227 | } 228 | } 229 | 230 | /// Handle `SIGINT`, `SIGTERM`, `SIGQUIT` signals and send 231 | /// a `SystemExit(0)` message to the `System` actor. 232 | impl Handler for DefaultSignalsHandler { 233 | type Result = (); 234 | 235 | fn handle(&mut self, msg: Signal, _: &mut Self::Context) { 236 | match msg.0 { 237 | SignalType::Int => { 238 | info!("SIGINT received, exiting"); 239 | System::current().stop(); 240 | } 241 | SignalType::Hup => { 242 | info!("SIGHUP received, reloading"); 243 | } 244 | SignalType::Term => { 245 | info!("SIGTERM received, stopping"); 246 | System::current().stop(); 247 | } 248 | SignalType::Quit => { 249 | info!("SIGQUIT received, exiting"); 250 | System::current().stop(); 251 | } 252 | _ => (), 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Actix [![Build Status](https://travis-ci.org/actix/actix.svg?branch=master)](https://travis-ci.org/actix/actix) [![Build status](https://ci.appveyor.com/api/projects/status/aytxo1w6a88x2cxk/branch/master?svg=true)](https://ci.appveyor.com/project/fafhrd91/actix-n9e64/branch/master) [![codecov](https://codecov.io/gh/actix/actix/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix) [![crates.io](http://meritbadge.herokuapp.com/actix)](https://crates.io/crates/actix) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | 3 | Actix is a Rust actors framework. 4 | 5 | * [User Guide](https://actix.rs/book/actix/) 6 | * [API Documentation (Development)](http://actix.github.io/actix/actix/) 7 | * [API Documentation (Releases)](https://docs.rs/actix/) 8 | * Cargo package: [actix](https://crates.io/crates/actix) 9 | * Minimum supported Rust version: 1.31 or later 10 | 11 | --- 12 | 13 | ## Features 14 | 15 | * Async/Sync actors. 16 | * Actor communication in a local/thread context. 17 | * Uses [Futures](https://crates.io/crates/futures) for asynchronous message handling. 18 | * HTTP1/HTTP2 support ([actix-web](https://github.com/actix/actix-web)) 19 | * Actor supervision. 20 | * Typed messages (No `Any` type). 21 | 22 | 23 | ## Usage 24 | 25 | To use `actix`, add this to your `Cargo.toml`: 26 | 27 | ```toml 28 | [dependencies] 29 | actix = "0.7" 30 | ``` 31 | 32 | ### Initialize Actix 33 | 34 | In order to use actix you first need to create a `System`. 35 | 36 | ```rust,ignore 37 | extern crate actix; 38 | 39 | fn main() { 40 | let system = actix::System::new("test"); 41 | 42 | system.run(); 43 | } 44 | ``` 45 | 46 | Actix uses the [tokio](https://github.com/tokio-rs/tokio) event loop. 47 | `System::new()` creates a new event loop and starts the `System` actor. 48 | `system.run()` starts the tokio event loop, and will finish once the `System` actor 49 | receives the `SystemExit` message. 50 | 51 | Let's create a simple Actor. 52 | 53 | ### Implement an Actor 54 | 55 | In order to define an actor you need to define a struct and have it implement 56 | the [`Actor`](https://actix.github.io/actix/actix/trait.Actor.html) trait. 57 | 58 | 59 | ```rust 60 | extern crate actix; 61 | use actix::{msgs, Actor, Addr, Arbiter, Context, System}; 62 | 63 | struct MyActor; 64 | 65 | impl Actor for MyActor { 66 | type Context = Context; 67 | 68 | fn started(&mut self, ctx: &mut Self::Context) { 69 | println!("I am alive!"); 70 | System::current().stop(); // <- stop system 71 | } 72 | } 73 | 74 | fn main() { 75 | let system = System::new("test"); 76 | 77 | let addr = MyActor.start(); 78 | 79 | system.run(); 80 | } 81 | ``` 82 | 83 | Spawning a new actor is achieved via the `start` and `create` methods of 84 | the [Actor](https://actix.github.io/actix/actix/trait.Actor.html) 85 | trait. It provides several different ways of creating actors, for details check docs. 86 | You can implement `started`, `stopping` and `stopped` methods of the Actor trait. 87 | `started` gets called when actor starts and `stopping` when actor finishes. 88 | Check [API documentation](https://actix.github.io/actix/actix/trait.Actor.html#actor-lifecycle) 89 | for more information on the actor lifecycle. 90 | 91 | ### Handle messages 92 | 93 | An Actor communicates with another Actor by sending messages. In actix all messages 94 | are typed. Let's define a simple `Sum` message with two `usize` parameters, 95 | and an actor which will accept this message and return the sum of those two numbers. 96 | 97 | ```rust 98 | extern crate actix; 99 | extern crate futures; 100 | use futures::{future, Future}; 101 | use actix::*; 102 | 103 | // this is our Message 104 | struct Sum(usize, usize); 105 | 106 | // we have to define the response type for `Sum` message 107 | impl Message for Sum { 108 | type Result = usize; 109 | } 110 | 111 | // Actor definition 112 | struct Summator; 113 | 114 | impl Actor for Summator { 115 | type Context = Context; 116 | } 117 | 118 | // now we need to define `MessageHandler` for the `Sum` message. 119 | impl Handler for Summator { 120 | type Result = usize; // <- Message response type 121 | 122 | fn handle(&mut self, msg: Sum, ctx: &mut Context) -> Self::Result { 123 | msg.0 + msg.1 124 | } 125 | } 126 | 127 | fn main() { 128 | let sys = System::new("test"); 129 | 130 | let addr = Summator.start(); 131 | let res = addr.send(Sum(10, 5)); // <- send message and get future for result 132 | 133 | Arbiter::spawn(res.then(|res| { 134 | match res { 135 | Ok(result) => println!("SUM: {}", result), 136 | _ => println!("Something wrong"), 137 | } 138 | 139 | System::current().stop(); 140 | future::result(Ok(())) 141 | })); 142 | 143 | sys.run(); 144 | } 145 | ``` 146 | 147 | All communications with actors go through an `Addr` object. You can `do_send` a message 148 | without waiting for a response, or `send` an actor with specific message. The `Message` 149 | trait defines the result type for a message. 150 | 151 | ### Actor state and subscription for specific messages 152 | 153 | You may have noticed that methods of `Actor` and `Handler` traits accept `&mut self`, so you are 154 | welcome to store anything in an actor and mutate it whenever necessary. 155 | 156 | Address objects require an actor type, but if we just want to send a specific message to 157 | an actor that can handle the message, we can use the `Recipient` interface. Let's create 158 | a new actor that uses `Recipient`. 159 | 160 | ```rust 161 | #[macro_use] extern crate actix; 162 | use std::time::Duration; 163 | use actix::prelude::*; 164 | 165 | #[derive(Message)] 166 | struct Ping { pub id: usize } 167 | 168 | // Actor definition 169 | struct Game { 170 | counter: usize, 171 | addr: Recipient, 172 | } 173 | 174 | impl Actor for Game { 175 | type Context = Context; 176 | } 177 | 178 | // simple message handler for Ping message 179 | impl Handler for Game { 180 | type Result = (); 181 | 182 | fn handle(&mut self, msg: Ping, ctx: &mut Context) { 183 | self.counter += 1; 184 | 185 | if self.counter > 10 { 186 | System::current().stop(); 187 | } else { 188 | println!("Ping received {:?}", msg.id); 189 | 190 | // wait 100 nanos 191 | ctx.run_later(Duration::new(0, 100), move |act, _| { 192 | act.addr.do_send(Ping{id: msg.id + 1}); 193 | }); 194 | } 195 | } 196 | } 197 | 198 | fn main() { 199 | let system = System::new("test"); 200 | 201 | // To get a Recipient object, we need to use a different builder method 202 | // which will allow postponing actor creation 203 | let addr = Game::create(|ctx| { 204 | // now we can get an address of the first actor and create the second actor 205 | let addr = ctx.address(); 206 | let addr2 = Game{counter: 0, addr: addr.recipient()}.start(); 207 | 208 | // let's start pings 209 | addr2.do_send(Ping{id: 10}); 210 | 211 | // now we can finally create first actor 212 | Game{counter: 0, addr: addr2.recipient()} 213 | }); 214 | 215 | system.run(); 216 | } 217 | ``` 218 | 219 | More information on signal handling is in the 220 | [signal](https://actix.github.io/actix/actix/actors/signal/index.html) module. 221 | 222 | 223 | ### chat example 224 | 225 | There is a 226 | [chat example](https://github.com/actix/actix/tree/master/examples/chat) 227 | which provides a basic example of networking client/server service. 228 | 229 | 230 | ### fectl 231 | 232 | You may consider checking out [fectl](https://github.com/fafhrd91/fectl) utility. It is written 233 | with `actix` and shows how to create networking application with relatively complex interactions. 234 | 235 | ## Contributing 236 | 237 | All contributions are welcome, if you have a feature request don't hesitate to open an issue! 238 | 239 | ## License 240 | 241 | This project is licensed under either of 242 | 243 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 244 | http://www.apache.org/licenses/LICENSE-2.0) 245 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 246 | http://opensource.org/licenses/MIT) 247 | 248 | at your option. 249 | 250 | ## Code of Conduct 251 | 252 | Contribution to the actix crate is organized under the terms of the 253 | Contributor Covenant, the maintainer of actix, @fafhrd91, promises to 254 | intervene to uphold that code of conduct. 255 | -------------------------------------------------------------------------------- /src/sync.rs: -------------------------------------------------------------------------------- 1 | //! Sync actors support 2 | //! 3 | //! Sync actors could be used for cpu bound load. Only one sync actor 4 | //! runs within arbiter's thread. Sync actor process one message at a time. 5 | //! Sync arbiter can start multiple threads with separate instance of actor in 6 | //! each. Note on actor `stopping` lifecycle event, sync actor can not prevent 7 | //! stopping by returning `false` from `stopping` method. 8 | //! Multi consumer queue is used as a communication channel queue. 9 | //! To be able to start sync actor via `SyncArbiter` 10 | //! Actor has to use `SyncContext` as an execution context. 11 | //! 12 | //! ## Example 13 | //! 14 | //! ```rust 15 | //! # extern crate actix; 16 | //! # extern crate futures; 17 | //! use actix::prelude::*; 18 | //! 19 | //! struct Fibonacci(pub u32); 20 | //! 21 | //! impl Message for Fibonacci { 22 | //! type Result = Result; 23 | //! } 24 | //! 25 | //! struct SyncActor; 26 | //! 27 | //! impl Actor for SyncActor { 28 | //! type Context = SyncContext; 29 | //! } 30 | //! 31 | //! impl Handler for SyncActor { 32 | //! type Result = Result; 33 | //! 34 | //! fn handle(&mut self, msg: Fibonacci, _: &mut Self::Context) -> Self::Result { 35 | //! if msg.0 == 0 { 36 | //! Err(()) 37 | //! } else if msg.0 == 1 { 38 | //! Ok(1) 39 | //! } else { 40 | //! let mut i = 0; 41 | //! let mut sum = 0; 42 | //! let mut last = 0; 43 | //! let mut curr = 1; 44 | //! while i < msg.0 - 1 { 45 | //! sum = last + curr; 46 | //! last = curr; 47 | //! curr = sum; 48 | //! i += 1; 49 | //! } 50 | //! Ok(sum) 51 | //! } 52 | //! } 53 | //! } 54 | //! 55 | //! fn main() { 56 | //! System::run(|| { 57 | //! // start sync arbiter with 2 threads 58 | //! let addr = SyncArbiter::start(2, || SyncActor); 59 | //! 60 | //! // send 5 messages 61 | //! for n in 5..10 { 62 | //! addr.do_send(Fibonacci(n)); 63 | //! } 64 | //! 65 | //! # System::current().stop(); 66 | //! }); 67 | //! } 68 | //! ``` 69 | use std::marker::PhantomData; 70 | use std::sync::Arc; 71 | use std::thread; 72 | 73 | use crossbeam_channel as cb_channel; 74 | use futures::sync::oneshot::Sender as SyncSender; 75 | use futures::{Async, Future, Poll, Stream}; 76 | 77 | use actor::{Actor, ActorContext, ActorState, Running}; 78 | use address::channel; 79 | use address::{Addr, AddressReceiver, Envelope, EnvelopeProxy, ToEnvelope}; 80 | use arbiter::Arbiter; 81 | use context::Context; 82 | use handler::{Handler, Message, MessageResponse}; 83 | use system::System; 84 | 85 | /// Sync arbiter 86 | pub struct SyncArbiter 87 | where 88 | A: Actor>, 89 | { 90 | queue: Option>>, 91 | msgs: AddressReceiver, 92 | } 93 | 94 | impl SyncArbiter 95 | where 96 | A: Actor>, 97 | { 98 | /// Start new sync arbiter with specified number of worker threads. 99 | /// Returns address of the started actor. 100 | pub fn start(threads: usize, factory: F) -> Addr 101 | where 102 | F: Fn() -> A + Send + Sync + 'static, 103 | { 104 | let factory = Arc::new(factory); 105 | let (sender, receiver) = cb_channel::unbounded(); 106 | 107 | for _ in 0..threads { 108 | let f = Arc::clone(&factory); 109 | let sys = System::current(); 110 | let actor_queue = receiver.clone(); 111 | 112 | thread::spawn(move || { 113 | System::set_current(sys); 114 | SyncContext::new(f, actor_queue).run(); 115 | }); 116 | } 117 | 118 | let (tx, rx) = channel::channel(0); 119 | Arbiter::spawn(SyncArbiter { 120 | queue: Some(sender), 121 | msgs: rx, 122 | }); 123 | 124 | Addr::new(tx) 125 | } 126 | } 127 | 128 | impl Actor for SyncArbiter 129 | where 130 | A: Actor>, 131 | { 132 | type Context = Context; 133 | } 134 | 135 | #[doc(hidden)] 136 | impl Future for SyncArbiter 137 | where 138 | A: Actor>, 139 | { 140 | type Item = (); 141 | type Error = (); 142 | 143 | fn poll(&mut self) -> Poll { 144 | loop { 145 | match self.msgs.poll() { 146 | Ok(Async::Ready(Some(msg))) => { 147 | if let Some(ref queue) = self.queue { 148 | queue.send(msg).is_ok(); 149 | } 150 | } 151 | Ok(Async::NotReady) => break, 152 | Ok(Async::Ready(None)) | Err(_) => unreachable!(), 153 | } 154 | } 155 | 156 | // stop condition 157 | if self.msgs.connected() { 158 | Ok(Async::NotReady) 159 | } else { 160 | // stop sync arbiters 161 | self.queue = None; 162 | Ok(Async::Ready(())) 163 | } 164 | } 165 | } 166 | 167 | impl ToEnvelope for SyncContext 168 | where 169 | A: Actor> + Handler, 170 | M: Message + Send + 'static, 171 | M::Result: Send, 172 | { 173 | fn pack(msg: M, tx: Option>) -> Envelope { 174 | Envelope::with_proxy(Box::new(SyncContextEnvelope::new(msg, tx))) 175 | } 176 | } 177 | 178 | /// Sync actor execution context 179 | pub struct SyncContext 180 | where 181 | A: Actor>, 182 | { 183 | act: Option, 184 | queue: cb_channel::Receiver>, 185 | stopping: bool, 186 | state: ActorState, 187 | factory: Arc A>, 188 | } 189 | 190 | impl SyncContext 191 | where 192 | A: Actor, 193 | { 194 | /// Create new SyncContext 195 | fn new(factory: Arc A>, queue: cb_channel::Receiver>) -> Self { 196 | let act = factory(); 197 | SyncContext { 198 | queue, 199 | factory, 200 | act: Some(act), 201 | stopping: false, 202 | state: ActorState::Started, 203 | } 204 | } 205 | 206 | fn run(&mut self) { 207 | let mut act = self.act.take().unwrap(); 208 | 209 | // started 210 | A::started(&mut act, self); 211 | self.state = ActorState::Running; 212 | 213 | loop { 214 | match self.queue.recv() { 215 | Ok(mut env) => { 216 | env.handle(&mut act, self); 217 | } 218 | Err(_) => { 219 | self.state = ActorState::Stopping; 220 | if A::stopping(&mut act, self) != Running::Stop { 221 | warn!("stopping method is not supported for sync actors"); 222 | } 223 | self.state = ActorState::Stopped; 224 | A::stopped(&mut act, self); 225 | return; 226 | } 227 | } 228 | 229 | if self.stopping { 230 | self.stopping = false; 231 | 232 | // stop old actor 233 | A::stopping(&mut act, self); 234 | self.state = ActorState::Stopped; 235 | A::stopped(&mut act, self); 236 | 237 | // start new actor 238 | self.state = ActorState::Started; 239 | act = (*self.factory)(); 240 | A::started(&mut act, self); 241 | self.state = ActorState::Running; 242 | } 243 | } 244 | } 245 | } 246 | 247 | impl ActorContext for SyncContext 248 | where 249 | A: Actor, 250 | { 251 | /// Stop current actor. SyncContext creates and starts new actor. 252 | fn stop(&mut self) { 253 | self.stopping = true; 254 | self.state = ActorState::Stopping; 255 | } 256 | 257 | /// Terminate actor execution. SyncContext creates and starts new actor. 258 | fn terminate(&mut self) { 259 | self.stopping = true; 260 | self.state = ActorState::Stopping; 261 | } 262 | 263 | /// Actor execution state 264 | fn state(&self) -> ActorState { 265 | self.state 266 | } 267 | } 268 | 269 | pub(crate) struct SyncContextEnvelope 270 | where 271 | A: Actor> + Handler, 272 | M: Message + Send, 273 | { 274 | msg: Option, 275 | tx: Option>, 276 | actor: PhantomData, 277 | } 278 | 279 | unsafe impl Send for SyncContextEnvelope 280 | where 281 | A: Actor> + Handler, 282 | M: Message + Send, 283 | {} 284 | 285 | impl SyncContextEnvelope 286 | where 287 | A: Actor> + Handler, 288 | M: Message + Send, 289 | M::Result: Send, 290 | { 291 | pub fn new(msg: M, tx: Option>) -> Self { 292 | SyncContextEnvelope { 293 | tx, 294 | msg: Some(msg), 295 | actor: PhantomData, 296 | } 297 | } 298 | } 299 | 300 | impl EnvelopeProxy for SyncContextEnvelope 301 | where 302 | M: Message + Send + 'static, 303 | M::Result: Send, 304 | A: Actor> + Handler, 305 | { 306 | type Actor = A; 307 | 308 | fn handle(&mut self, act: &mut A, ctx: &mut A::Context) { 309 | let tx = self.tx.take(); 310 | if tx.is_some() && tx.as_ref().unwrap().is_canceled() { 311 | return; 312 | } 313 | 314 | if let Some(msg) = self.msg.take() { 315 | >::handle(act, msg, ctx).handle(ctx, tx) 316 | } 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /tests/test_lifecycle.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "cargo-clippy", allow(let_unit_value))] 2 | 3 | extern crate actix; 4 | extern crate futures; 5 | extern crate tokio; 6 | extern crate tokio_timer; 7 | 8 | use std::sync::atomic::{AtomicBool, Ordering}; 9 | use std::sync::{Arc, Mutex}; 10 | use std::time::{Duration, Instant}; 11 | 12 | use actix::prelude::*; 13 | use futures::sync::oneshot::{channel, Sender}; 14 | use futures::{future, Future}; 15 | use tokio_timer::Delay; 16 | 17 | struct MyActor { 18 | started: Arc, 19 | stopping: Arc, 20 | stopped: Arc, 21 | temp: Option>, 22 | restore_after_stop: bool, 23 | } 24 | 25 | impl Actor for MyActor { 26 | type Context = actix::Context; 27 | 28 | fn started(&mut self, _: &mut Self::Context) { 29 | self.started.store(true, Ordering::Relaxed); 30 | } 31 | fn stopping(&mut self, ctx: &mut Self::Context) -> Running { 32 | self.stopping.store(true, Ordering::Relaxed); 33 | 34 | if self.restore_after_stop { 35 | let (tx, rx) = channel(); 36 | self.temp = Some(tx); 37 | rx.actfuture() 38 | .then(|_, _: &mut MyActor, _: &mut _| actix::fut::result(Ok(()))) 39 | .spawn(ctx); 40 | Running::Continue 41 | } else { 42 | Running::Stop 43 | } 44 | } 45 | fn stopped(&mut self, _: &mut Self::Context) { 46 | self.stopped.store(true, Ordering::Relaxed); 47 | } 48 | } 49 | 50 | struct MySyncActor { 51 | started: Arc, 52 | stopping: Arc, 53 | stopped: Arc, 54 | restore_after_stop: bool, 55 | } 56 | 57 | impl Actor for MySyncActor { 58 | type Context = actix::SyncContext; 59 | 60 | fn started(&mut self, _: &mut Self::Context) { 61 | self.started.store(true, Ordering::Relaxed); 62 | } 63 | fn stopping(&mut self, _: &mut Self::Context) -> Running { 64 | self.stopping.store(true, Ordering::Relaxed); 65 | 66 | if self.restore_after_stop { 67 | self.restore_after_stop = false; 68 | Running::Continue 69 | } else { 70 | Running::Stop 71 | } 72 | } 73 | fn stopped(&mut self, _: &mut Self::Context) { 74 | self.stopped.store(true, Ordering::Relaxed); 75 | } 76 | } 77 | 78 | #[test] 79 | fn test_active_address() { 80 | let started = Arc::new(AtomicBool::new(false)); 81 | let stopping = Arc::new(AtomicBool::new(false)); 82 | let stopped = Arc::new(AtomicBool::new(false)); 83 | let started1 = Arc::clone(&started); 84 | let stopping1 = Arc::clone(&stopping); 85 | let stopped1 = Arc::clone(&stopped); 86 | 87 | let addr = Arc::new(Mutex::new(None)); 88 | let addr2 = Arc::clone(&addr); 89 | 90 | System::run(move || { 91 | *addr2.lock().unwrap() = Some( 92 | MyActor { 93 | started: started1, 94 | stopping: stopping1, 95 | stopped: stopped1, 96 | temp: None, 97 | restore_after_stop: false, 98 | }.start(), 99 | ); 100 | 101 | tokio::spawn( 102 | Delay::new(Instant::now() + Duration::new(0, 100)).then(|_| { 103 | System::current().stop(); 104 | future::result(Ok(())) 105 | }), 106 | ); 107 | }); 108 | 109 | assert!(started.load(Ordering::Relaxed), "Not started"); 110 | assert!(!stopping.load(Ordering::Relaxed), "Stopping"); 111 | assert!(!stopped.load(Ordering::Relaxed), "Stopped"); 112 | } 113 | 114 | #[test] 115 | fn test_stop_after_drop_address() { 116 | let started = Arc::new(AtomicBool::new(false)); 117 | let stopping = Arc::new(AtomicBool::new(false)); 118 | let stopped = Arc::new(AtomicBool::new(false)); 119 | let started1 = Arc::clone(&started); 120 | let stopping1 = Arc::clone(&stopping); 121 | let stopped1 = Arc::clone(&stopped); 122 | 123 | System::run(move || { 124 | let addr = MyActor { 125 | started: started1, 126 | stopping: stopping1, 127 | stopped: stopped1, 128 | temp: None, 129 | restore_after_stop: false, 130 | }.start(); 131 | 132 | tokio::spawn(futures::lazy(move || { 133 | Delay::new(Instant::now() + Duration::new(0, 100)).then(move |_| { 134 | drop(addr); 135 | Delay::new(Instant::now() + Duration::new(0, 10_000)).then(|_| { 136 | System::current().stop(); 137 | future::result(Ok(())) 138 | }) 139 | }) 140 | })); 141 | }); 142 | 143 | assert!(started.load(Ordering::Relaxed), "Not started"); 144 | assert!(stopping.load(Ordering::Relaxed), "Not stopping"); 145 | assert!(stopped.load(Ordering::Relaxed), "Not stopped"); 146 | } 147 | 148 | #[test] 149 | fn test_stop_after_drop_sync_address() { 150 | let started = Arc::new(AtomicBool::new(false)); 151 | let stopping = Arc::new(AtomicBool::new(false)); 152 | let stopped = Arc::new(AtomicBool::new(false)); 153 | let started1 = Arc::clone(&started); 154 | let stopping1 = Arc::clone(&stopping); 155 | let stopped1 = Arc::clone(&stopped); 156 | 157 | System::run(move || { 158 | let addr = MyActor { 159 | started: started1, 160 | stopping: stopping1, 161 | stopped: stopped1, 162 | temp: None, 163 | restore_after_stop: false, 164 | }.start(); 165 | 166 | tokio::spawn(futures::lazy(move || { 167 | Delay::new(Instant::now() + Duration::new(0, 100)).then(move |_| { 168 | drop(addr); 169 | System::current().stop(); 170 | future::result(Ok(())) 171 | }) 172 | })); 173 | }); 174 | 175 | assert!(started.load(Ordering::Relaxed), "Not started"); 176 | assert!(stopping.load(Ordering::Relaxed), "Not stopping"); 177 | assert!(stopped.load(Ordering::Relaxed), "Not stopped"); 178 | } 179 | 180 | #[test] 181 | fn test_stop_after_drop_sync_actor() { 182 | let started = Arc::new(AtomicBool::new(false)); 183 | let stopping = Arc::new(AtomicBool::new(false)); 184 | let stopped = Arc::new(AtomicBool::new(false)); 185 | 186 | let started1 = Arc::clone(&started); 187 | let stopping1 = Arc::clone(&stopping); 188 | let stopped1 = Arc::clone(&stopped); 189 | 190 | let started2 = Arc::clone(&started); 191 | let stopping2 = Arc::clone(&stopping); 192 | let stopped2 = Arc::clone(&stopped); 193 | 194 | System::run(move || { 195 | let addr = SyncArbiter::start(1, move || MySyncActor { 196 | started: Arc::clone(&started1), 197 | stopping: Arc::clone(&stopping1), 198 | stopped: Arc::clone(&stopped1), 199 | restore_after_stop: false, 200 | }); 201 | 202 | tokio::spawn(futures::lazy(move || { 203 | Delay::new(Instant::now() + Duration::from_secs(2)).then(move |_| { 204 | assert!(started2.load(Ordering::Relaxed), "Not started"); 205 | assert!(!stopping2.load(Ordering::Relaxed), "Stopping"); 206 | assert!(!stopped2.load(Ordering::Relaxed), "Stopped"); 207 | drop(addr); 208 | 209 | Delay::new(Instant::now() + Duration::from_secs(2)).then(move |_| { 210 | System::current().stop(); 211 | future::result(Ok(())) 212 | }) 213 | }) 214 | })); 215 | }); 216 | 217 | assert!(started.load(Ordering::Relaxed), "Not started"); 218 | assert!(stopping.load(Ordering::Relaxed), "Not stopping"); 219 | assert!(stopped.load(Ordering::Relaxed), "Not stopped"); 220 | } 221 | 222 | #[test] 223 | fn test_stop() { 224 | let started = Arc::new(AtomicBool::new(false)); 225 | let stopping = Arc::new(AtomicBool::new(false)); 226 | let stopped = Arc::new(AtomicBool::new(false)); 227 | let started1 = Arc::clone(&started); 228 | let stopping1 = Arc::clone(&stopping); 229 | let stopped1 = Arc::clone(&stopped); 230 | 231 | System::run(move || { 232 | MyActor { 233 | started: started1, 234 | stopping: stopping1, 235 | stopped: stopped1, 236 | temp: None, 237 | restore_after_stop: false, 238 | }.start(); 239 | 240 | tokio::spawn( 241 | Delay::new(Instant::now() + Duration::new(0, 100)).then(|_| { 242 | System::current().stop(); 243 | future::result(Ok(())) 244 | }), 245 | ); 246 | }); 247 | 248 | assert!(started.load(Ordering::Relaxed), "Not started"); 249 | assert!(stopping.load(Ordering::Relaxed), "Not stopping"); 250 | assert!(stopped.load(Ordering::Relaxed), "Not stopped"); 251 | } 252 | 253 | #[test] 254 | fn test_stop_restore_after_stopping() { 255 | let started = Arc::new(AtomicBool::new(false)); 256 | let stopping = Arc::new(AtomicBool::new(false)); 257 | let stopped = Arc::new(AtomicBool::new(false)); 258 | let started1 = Arc::clone(&started); 259 | let stopping1 = Arc::clone(&stopping); 260 | let stopped1 = Arc::clone(&stopped); 261 | 262 | System::run(move || { 263 | MyActor { 264 | started: started1, 265 | stopping: stopping1, 266 | stopped: stopped1, 267 | temp: None, 268 | restore_after_stop: true, 269 | }.start(); 270 | 271 | tokio::spawn( 272 | Delay::new(Instant::now() + Duration::new(0, 100)).then(|_| { 273 | System::current().stop(); 274 | future::result(Ok(())) 275 | }), 276 | ); 277 | }); 278 | 279 | assert!(started.load(Ordering::Relaxed), "Not started"); 280 | assert!(stopping.load(Ordering::Relaxed), "Not stopping"); 281 | assert!(!stopped.load(Ordering::Relaxed), "Stopped"); 282 | } 283 | -------------------------------------------------------------------------------- /src/handler.rs: -------------------------------------------------------------------------------- 1 | use futures::sync::oneshot::Sender as SyncSender; 2 | use futures::Future; 3 | use std::fmt; 4 | 5 | use actor::{Actor, AsyncContext}; 6 | use address::Addr; 7 | use arbiter::Arbiter; 8 | use context::Context; 9 | use fut::{self, ActorFuture}; 10 | 11 | /// Describes how to handle messages of a specific type. 12 | /// 13 | /// Implementing `Handler` is a general way to handle incoming 14 | /// messages, streams, and futures. 15 | /// 16 | /// The type `M` is a message which can be handled by the actor. 17 | #[allow(unused_variables)] 18 | pub trait Handler 19 | where 20 | Self: Actor, 21 | M: Message, 22 | { 23 | /// The type of value that this handler will return. 24 | type Result: MessageResponse; 25 | 26 | /// This method is called for every message received by this actor. 27 | fn handle(&mut self, msg: M, ctx: &mut Self::Context) -> Self::Result; 28 | } 29 | 30 | /// Represent message that can be handled by an actor. 31 | pub trait Message { 32 | /// The type of value that this message will resolved with if it is 33 | /// successful. 34 | type Result: 'static; 35 | } 36 | 37 | /// A helper type that implements the `MessageResponse` trait. 38 | pub struct MessageResult(pub M::Result); 39 | 40 | /// A specialized actor future for asynchronous message handling. 41 | pub type ResponseActFuture = Box>; 42 | 43 | /// A specialized future for asynchronous message handling. 44 | pub type ResponseFuture = Box>; 45 | 46 | /// A trait that defines a message response channel. 47 | pub trait ResponseChannel: 'static { 48 | fn is_canceled(&self) -> bool; 49 | 50 | fn send(self, response: M::Result); 51 | } 52 | 53 | /// A trait which defines message responses. 54 | pub trait MessageResponse { 55 | fn handle>(self, ctx: &mut A::Context, tx: Option); 56 | } 57 | 58 | impl ResponseChannel for SyncSender 59 | where 60 | M::Result: Send, 61 | { 62 | fn is_canceled(&self) -> bool { 63 | SyncSender::is_canceled(self) 64 | } 65 | 66 | fn send(self, response: M::Result) { 67 | let _ = SyncSender::send(self, response); 68 | } 69 | } 70 | 71 | impl ResponseChannel for () { 72 | fn is_canceled(&self) -> bool { 73 | true 74 | } 75 | fn send(self, _: M::Result) {} 76 | } 77 | 78 | impl MessageResponse for MessageResult 79 | where 80 | A: Actor, 81 | M: Message, 82 | { 83 | fn handle>(self, _: &mut A::Context, tx: Option) { 84 | if let Some(tx) = tx { 85 | tx.send(self.0); 86 | } 87 | } 88 | } 89 | 90 | impl MessageResponse for Result 91 | where 92 | A: Actor, 93 | M: Message>, 94 | { 95 | fn handle>(self, _: &mut A::Context, tx: Option) { 96 | if let Some(tx) = tx { 97 | tx.send(self); 98 | } 99 | } 100 | } 101 | 102 | impl MessageResponse for Option 103 | where 104 | A: Actor, 105 | M: Message>, 106 | { 107 | fn handle>(self, _: &mut A::Context, tx: Option) { 108 | if let Some(tx) = tx { 109 | tx.send(self); 110 | } 111 | } 112 | } 113 | 114 | impl MessageResponse for Addr 115 | where 116 | A: Actor, 117 | M: Message>, 118 | B: Actor>, 119 | { 120 | fn handle>(self, _: &mut A::Context, tx: Option) { 121 | if let Some(tx) = tx { 122 | tx.send(self); 123 | } 124 | } 125 | } 126 | 127 | impl MessageResponse for ResponseActFuture 128 | where 129 | A: Actor, 130 | M: Message>, 131 | A::Context: AsyncContext, 132 | { 133 | fn handle>(self, ctx: &mut A::Context, tx: Option) { 134 | ctx.spawn(self.then(move |res, _, _| { 135 | if let Some(tx) = tx { 136 | tx.send(res); 137 | } 138 | fut::ok(()) 139 | })); 140 | } 141 | } 142 | 143 | impl MessageResponse for ResponseFuture 144 | where 145 | A: Actor, 146 | M::Result: Send, 147 | M: Message>, 148 | A::Context: AsyncContext, 149 | { 150 | fn handle>(self, _: &mut A::Context, tx: Option) { 151 | Arbiter::spawn(self.then(move |res| { 152 | if let Some(tx) = tx { 153 | tx.send(res) 154 | } 155 | Ok(()) 156 | })); 157 | } 158 | } 159 | 160 | enum ResponseTypeItem { 161 | Result(Result), 162 | Fut(Box>), 163 | } 164 | 165 | /// Helper type for representing different type of message responses 166 | pub struct Response { 167 | item: ResponseTypeItem, 168 | } 169 | 170 | impl fmt::Debug for Response { 171 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 172 | let mut fmt = fmt.debug_struct("Response"); 173 | match self.item { 174 | ResponseTypeItem::Result(_) => fmt.field("item", &"Result(_)".to_string()), 175 | ResponseTypeItem::Fut(_) => fmt.field("item", &"Fut(_)".to_string()), 176 | }.finish() 177 | } 178 | } 179 | 180 | impl Response { 181 | /// Creates an asynchronous response. 182 | pub fn async(fut: T) -> Self 183 | where 184 | T: Future + 'static, 185 | { 186 | Response { 187 | item: ResponseTypeItem::Fut(Box::new(fut)), 188 | } 189 | } 190 | 191 | /// Creates a response. 192 | pub fn reply(val: Result) -> Self { 193 | Response { 194 | item: ResponseTypeItem::Result(val), 195 | } 196 | } 197 | } 198 | 199 | impl MessageResponse for Response 200 | where 201 | A: Actor, 202 | M: Message>, 203 | A::Context: AsyncContext, 204 | { 205 | fn handle>(self, _: &mut A::Context, tx: Option) { 206 | match self.item { 207 | ResponseTypeItem::Fut(fut) => { 208 | Arbiter::spawn(fut.then(move |res| { 209 | if let Some(tx) = tx { 210 | tx.send(res); 211 | } 212 | Ok(()) 213 | })); 214 | } 215 | ResponseTypeItem::Result(res) => { 216 | if let Some(tx) = tx { 217 | tx.send(res); 218 | } 219 | } 220 | } 221 | } 222 | } 223 | 224 | enum ActorResponseTypeItem { 225 | Result(Result), 226 | Fut(Box>), 227 | } 228 | 229 | /// A helper type for representing different types of message responses. 230 | pub struct ActorResponse { 231 | item: ActorResponseTypeItem, 232 | } 233 | 234 | impl fmt::Debug for ActorResponse { 235 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 236 | let mut fmt = fmt.debug_struct("ActorResponse"); 237 | match self.item { 238 | ActorResponseTypeItem::Result(_) => fmt.field("item", &"Result(_)".to_string()), 239 | ActorResponseTypeItem::Fut(_) => fmt.field("item", &"Fut(_)".to_string()), 240 | }.finish() 241 | } 242 | } 243 | 244 | impl ActorResponse { 245 | /// Creates a response. 246 | pub fn reply(val: Result) -> Self { 247 | ActorResponse { 248 | item: ActorResponseTypeItem::Result(val), 249 | } 250 | } 251 | 252 | /// Creates an asynchronous response. 253 | pub fn async(fut: T) -> Self 254 | where 255 | T: ActorFuture + 'static, 256 | { 257 | ActorResponse { 258 | item: ActorResponseTypeItem::Fut(Box::new(fut)), 259 | } 260 | } 261 | } 262 | 263 | impl MessageResponse for ActorResponse 264 | where 265 | A: Actor, 266 | M: Message>, 267 | A::Context: AsyncContext, 268 | { 269 | fn handle>(self, ctx: &mut A::Context, tx: Option) { 270 | match self.item { 271 | ActorResponseTypeItem::Fut(fut) => { 272 | ctx.spawn(fut.then(move |res, _, _| { 273 | if let Some(tx) = tx { 274 | tx.send(res) 275 | } 276 | fut::ok(()) 277 | })); 278 | } 279 | ActorResponseTypeItem::Result(res) => { 280 | if let Some(tx) = tx { 281 | tx.send(res); 282 | } 283 | } 284 | } 285 | } 286 | } 287 | 288 | macro_rules! SIMPLE_RESULT { 289 | ($type:ty) => { 290 | impl MessageResponse for $type 291 | where 292 | A: Actor, 293 | M: Message, 294 | { 295 | fn handle>(self, _: &mut A::Context, tx: Option) { 296 | if let Some(tx) = tx { 297 | tx.send(self); 298 | } 299 | } 300 | } 301 | }; 302 | } 303 | 304 | SIMPLE_RESULT!(()); 305 | SIMPLE_RESULT!(u8); 306 | SIMPLE_RESULT!(u16); 307 | SIMPLE_RESULT!(u32); 308 | SIMPLE_RESULT!(u64); 309 | SIMPLE_RESULT!(usize); 310 | SIMPLE_RESULT!(i8); 311 | SIMPLE_RESULT!(i16); 312 | SIMPLE_RESULT!(i32); 313 | SIMPLE_RESULT!(i64); 314 | SIMPLE_RESULT!(isize); 315 | SIMPLE_RESULT!(f32); 316 | SIMPLE_RESULT!(f64); 317 | SIMPLE_RESULT!(String); 318 | SIMPLE_RESULT!(bool); 319 | -------------------------------------------------------------------------------- /src/registry.rs: -------------------------------------------------------------------------------- 1 | //! Actors registry 2 | //! 3 | //! An Actor can register itself as a service. A Service can be defined as an 4 | //! `ArbiterService`, which is unique per arbiter, or a `SystemService`, which 5 | //! is unique per system. 6 | use std::any::{Any, TypeId}; 7 | use std::cell::RefCell; 8 | use std::collections::HashMap; 9 | use std::default::Default; 10 | use std::hash::BuildHasherDefault; 11 | use std::rc::Rc; 12 | use std::sync::Arc; 13 | 14 | use fnv::FnvHasher; 15 | use parking_lot::ReentrantMutex; 16 | 17 | use actor::{Actor, Supervised}; 18 | use address::Addr; 19 | use arbiter::Arbiter; 20 | use context::Context; 21 | use supervisor::Supervisor; 22 | use system::System; 23 | 24 | type AnyMap = HashMap, BuildHasherDefault>; 25 | 26 | /// Actors registry 27 | /// 28 | /// An Actor can register itself as a service. A Service can be defined as an 29 | /// `ArbiterService`, which is unique per arbiter, or a `SystemService`, which 30 | /// is unique per system. 31 | /// 32 | /// If an arbiter service is used outside of a running arbiter, it panics. 33 | /// 34 | /// # Example 35 | /// 36 | /// ```rust 37 | /// # #[macro_use] extern crate actix; 38 | /// use actix::prelude::*; 39 | /// 40 | /// #[derive(Message)] 41 | /// struct Ping; 42 | /// 43 | /// #[derive(Default)] 44 | /// struct MyActor1; 45 | /// 46 | /// impl Actor for MyActor1 { 47 | /// type Context = Context; 48 | /// } 49 | /// impl actix::Supervised for MyActor1 {} 50 | /// 51 | /// impl ArbiterService for MyActor1 { 52 | /// fn service_started(&mut self, ctx: &mut Context) { 53 | /// println!("Service started"); 54 | /// } 55 | /// } 56 | /// 57 | /// impl Handler for MyActor1 { 58 | /// type Result = (); 59 | /// 60 | /// fn handle(&mut self, _: Ping, ctx: &mut Context) { 61 | /// println!("ping"); 62 | /// # System::current().stop(); 63 | /// } 64 | /// } 65 | /// 66 | /// struct MyActor2; 67 | /// 68 | /// impl Actor for MyActor2 { 69 | /// type Context = Context; 70 | /// 71 | /// fn started(&mut self, _: &mut Context) { 72 | /// // get MyActor1 address from the registry 73 | /// let act = Arbiter::registry().get::(); 74 | /// act.do_send(Ping); 75 | /// } 76 | /// } 77 | /// 78 | /// fn main() { 79 | /// // initialize system 80 | /// let code = System::run(|| { 81 | /// // Start MyActor2 in new Arbiter 82 | /// Arbiter::start(|_| { 83 | /// MyActor2 84 | /// }); 85 | /// }); 86 | /// } 87 | /// ``` 88 | #[derive(Clone)] 89 | pub struct Registry { 90 | registry: Rc>, 91 | } 92 | 93 | /// Trait defines arbiter's service. 94 | #[allow(unused_variables)] 95 | pub trait ArbiterService: Actor> + Supervised + Default { 96 | /// Construct and start arbiter service 97 | fn start_service() -> Addr { 98 | Supervisor::start(|ctx| { 99 | let mut act = Self::default(); 100 | act.service_started(ctx); 101 | act 102 | }) 103 | } 104 | 105 | /// Method is called during service initialization. 106 | fn service_started(&mut self, ctx: &mut Context) {} 107 | 108 | /// Get actor's address from arbiter registry 109 | fn from_registry() -> Addr { 110 | Arbiter::registry().get::() 111 | } 112 | } 113 | 114 | impl Registry { 115 | pub(crate) fn new() -> Self { 116 | Registry { 117 | registry: Rc::new(RefCell::new(HashMap::default())), 118 | } 119 | } 120 | 121 | /// Query registry for specific actor. Returns address of the actor. 122 | /// If actor is not registered, starts new actor and 123 | /// return address of newly created actor. 124 | pub fn get>>(&self) -> Addr { 125 | let id = TypeId::of::(); 126 | if let Some(addr) = self.registry.borrow().get(&id) { 127 | if let Some(addr) = addr.downcast_ref::>() { 128 | return addr.clone(); 129 | } 130 | } 131 | let addr: Addr = A::start_service(); 132 | 133 | self.registry 134 | .borrow_mut() 135 | .insert(id, Box::new(addr.clone())); 136 | addr 137 | } 138 | 139 | /// Check if actor is in registry, if so, return its address 140 | pub fn query>>(&self) -> Option> { 141 | let id = TypeId::of::(); 142 | if let Some(addr) = self.registry.borrow().get(&id) { 143 | if let Some(addr) = addr.downcast_ref::>() { 144 | return Some(addr.clone()); 145 | } 146 | } 147 | None 148 | } 149 | 150 | /// Add new actor to the registry by address, panic if actor is already running 151 | pub fn set>>(&self, addr: Addr) { 152 | let id = TypeId::of::(); 153 | if let Some(addr) = self.registry.borrow().get(&id) { 154 | if addr.downcast_ref::>().is_some() { 155 | panic!("Actor already started"); 156 | } 157 | } 158 | 159 | self.registry.borrow_mut().insert(id, Box::new(addr)); 160 | } 161 | } 162 | 163 | /// System wide actors registry 164 | /// 165 | /// System registry serves same purpose as [Registry](struct.Registry.html), 166 | /// except it is shared across all arbiters. 167 | /// 168 | /// # Example 169 | /// 170 | /// ```rust 171 | /// # #[macro_use] extern crate actix; 172 | /// use actix::prelude::*; 173 | /// 174 | /// #[derive(Message)] 175 | /// struct Ping; 176 | /// 177 | /// #[derive(Default)] 178 | /// struct MyActor1; 179 | /// 180 | /// impl Actor for MyActor1 { 181 | /// type Context = Context; 182 | /// } 183 | /// impl actix::Supervised for MyActor1 {} 184 | /// 185 | /// impl SystemService for MyActor1 { 186 | /// fn service_started(&mut self, ctx: &mut Context) { 187 | /// println!("Service started"); 188 | /// } 189 | /// } 190 | /// 191 | /// impl Handler for MyActor1 { 192 | /// type Result = (); 193 | /// 194 | /// fn handle(&mut self, _: Ping, ctx: &mut Context) { 195 | /// println!("ping"); 196 | /// # System::current().stop(); 197 | /// } 198 | /// } 199 | /// 200 | /// struct MyActor2; 201 | /// 202 | /// impl Actor for MyActor2 { 203 | /// type Context = Context; 204 | /// 205 | /// fn started(&mut self, _: &mut Context) { 206 | /// let act = System::current().registry().get::(); 207 | /// act.do_send(Ping); 208 | /// } 209 | /// } 210 | /// 211 | /// fn main() { 212 | /// // initialize system 213 | /// let code = System::run(|| { 214 | /// // Start MyActor2 215 | /// let addr = MyActor2.start(); 216 | /// }); 217 | /// } 218 | /// ``` 219 | #[derive(Debug)] 220 | pub struct SystemRegistry { 221 | system: Addr, 222 | registry: InnerRegistry, 223 | } 224 | 225 | type AnyMapSend = HashMap, BuildHasherDefault>; 226 | type InnerRegistry = Arc>>; 227 | 228 | /// Trait defines system's service. 229 | #[allow(unused_variables)] 230 | pub trait SystemService: Actor> + Supervised + Default { 231 | /// Construct and start system service 232 | fn start_service(sys: &Addr) -> Addr { 233 | Supervisor::start_in_arbiter(sys, |ctx| { 234 | let mut act = Self::default(); 235 | act.service_started(ctx); 236 | act 237 | }) 238 | } 239 | 240 | /// Method is called during service initialization. 241 | fn service_started(&mut self, ctx: &mut Context) {} 242 | 243 | /// Get actor's address from system registry 244 | fn from_registry() -> Addr { 245 | System::with_current(|sys| sys.registry().get::()) 246 | } 247 | } 248 | 249 | impl SystemRegistry { 250 | pub(crate) fn new(system: Addr) -> Self { 251 | SystemRegistry { 252 | system, 253 | registry: Arc::new(ReentrantMutex::new(RefCell::new(HashMap::default()))), 254 | } 255 | } 256 | 257 | /// Return address of the service. If service actor is not running 258 | /// it get started in the system. 259 | pub fn get>>(&self) -> Addr { 260 | let hm = self.registry.lock(); 261 | if let Some(addr) = hm.borrow().get(&TypeId::of::()) { 262 | match addr.downcast_ref::>() { 263 | Some(addr) => return addr.clone(), 264 | None => panic!("Got unknown value: {:?}", addr), 265 | } 266 | } 267 | 268 | let addr = A::start_service(&self.system); 269 | hm.borrow_mut() 270 | .insert(TypeId::of::(), Box::new(addr.clone())); 271 | addr 272 | } 273 | 274 | /// Check if actor is in registry, if so, return its address 275 | pub fn query>>(&self) -> Option> { 276 | let hm = self.registry.lock(); 277 | if let Some(addr) = hm.borrow().get(&TypeId::of::()) { 278 | match addr.downcast_ref::>() { 279 | Some(addr) => return Some(addr.clone()), 280 | None => return None, 281 | } 282 | } 283 | 284 | None 285 | } 286 | 287 | /// Add new actor to the registry by address, panic if actor is already running 288 | pub fn set>>(&self, addr: Addr) { 289 | let hm = self.registry.lock(); 290 | if let Some(addr) = hm.borrow().get(&TypeId::of::()) { 291 | if addr.downcast_ref::>().is_some() { 292 | panic!("Actor already started"); 293 | } 294 | } 295 | 296 | hm.borrow_mut() 297 | .insert(TypeId::of::(), Box::new(addr)); 298 | } 299 | } 300 | 301 | impl Clone for SystemRegistry { 302 | fn clone(&self) -> Self { 303 | SystemRegistry { 304 | system: self.system.clone(), 305 | registry: Arc::clone(&self.registry), 306 | } 307 | } 308 | } 309 | --------------------------------------------------------------------------------