├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── channel └── mod.rs ├── js_pool ├── mod.rs └── tests.rs ├── lib.rs ├── tests.rs └── thread ├── mod.rs └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | matrix: 8 | allow_failures: 9 | - rust: nightly 10 | script: 11 | - cargo build --verbose 12 | - cargo test --verbose 13 | - cargo test --release --verbose 14 | - cargo doc 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "actors" 3 | version = "0.2.0-SNAPSHOT" 4 | authors = ["Peter Kolloch "] 5 | description = "Provides actor-like concurrency for Rust" 6 | documentation = "https://kolloch.github.io/actors/doc/actors/index.html" 7 | homepage = "https://github.com/kolloch/actors" 8 | repository = "https://github.com/kolloch/actors" 9 | readme = "README.md" 10 | keywords = ["actor", "concurrency", "threads", "synchronization"] 11 | license = "BSD-3-Clause" 12 | 13 | [dependencies] 14 | jobsteal = {version = "0.2.4", optional = true} 15 | log = "0.3" 16 | 17 | [dev-dependencies] 18 | env_logger = "0.3" 19 | 20 | [features] 21 | # Enable all features by default 22 | default = ["channel", "thread", "jobsteal"] 23 | # Allow spawning actors on a ForkJoinPool 24 | channel = [] 25 | thread = [] 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Peter Kolloch 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | actors 2 | ====== 3 | 4 | WARNING: Abondoned. I am not planning to work on this anymore. 5 | 6 | Look at [tokio](https://github.com/tokio-rs/tokio) and 7 | the underlying [futures](https://github.com/alexcrichton/futures-rs) 8 | for nice event loop based concurrency paradigms. 9 | 10 | -- 11 | 12 | A [rust](http://www.rust-lang.org/) library to provide 13 | [actor](http://en.wikipedia.org/wiki/Actor_model)-like message-based concurrency. 14 | 15 | [![Build Status](https://travis-ci.org/kolloch/actors.svg?branch=master)](https://travis-ci.org/kolloch/actors) 16 | 17 | [API Documentation](https://kolloch.github.io/actors/doc/actors/index.html) 18 | 19 | Warning: This library is in a very early stage, it is not recomended for production 20 | and all APIs are subject to change. 21 | 22 | Goals: 23 | 24 | * Message-based state manipulation. 25 | * Deal with failure by allowing actor supervision. 26 | * Light-weighed: Each actor should consume only few resources. 27 | * Multi-plex actor execution efficiently upon thread-pools. 28 | * Composable: Do not try to solve everything at once. 29 | * Rusty: Use features available in this beautiful language. 30 | 31 | Non-Goals: 32 | 33 | * Transparent network communication/distribution as part of this 34 | library. 35 | 36 | ## Usage 37 | 38 | Add this to your `Cargo.toml`: 39 | 40 | ```toml 41 | [dependencies] 42 | 43 | actors = "0.1" 44 | ``` 45 | -------------------------------------------------------------------------------- /src/channel/mod.rs: -------------------------------------------------------------------------------- 1 | //! Use channels as ActorRefs 2 | 3 | use std::sync::mpsc; 4 | use std::fmt::{self, Debug, Formatter}; 5 | use {ActorRef, ActorRefEnum, ActorRefImpl, SendError}; 6 | 7 | /// An ActorRef which forwards its messages to a channel 8 | pub struct SenderActorRef(mpsc::Sender); 9 | 10 | impl ActorRefImpl for SenderActorRef { 11 | fn send(&self, msg: Message) -> Result<(), SendError> { 12 | let &SenderActorRef(ref tx) = self; 13 | Ok(try!(tx.send(msg))) 14 | } 15 | } 16 | 17 | impl Debug for SenderActorRef { 18 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 19 | write!(f, "SenderActorRef") 20 | } 21 | } 22 | 23 | impl Clone for SenderActorRef { 24 | fn clone(&self) -> SenderActorRef { 25 | let &SenderActorRef(ref sender) = self; 26 | SenderActorRef(sender.clone()) 27 | } 28 | } 29 | 30 | /// Create an ActorRef and a Receiver so that messages sent to the ActorRef 31 | /// can be obtained via the Receiver. 32 | pub fn channel_ref() -> (SenderActorRef, mpsc::Receiver) { 33 | let (sender, receiver) = mpsc::channel(); 34 | let sender_ref = SenderActorRef(sender); 35 | (sender_ref, receiver) 36 | } 37 | 38 | /// Like channel_ref but wrap the actor ref in an ActorRef for convenience. 39 | pub fn channel_actor_ref() -> (ActorRef, mpsc::Receiver) { 40 | let (sender_ref, receiver) = channel_ref(); 41 | (ActorRef(ActorRefEnum::Channel(sender_ref)), receiver) 42 | } 43 | -------------------------------------------------------------------------------- /src/js_pool/mod.rs: -------------------------------------------------------------------------------- 1 | //! ActorSpawner which spawns work on jobsteal thread-pool. 2 | 3 | use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError}; 4 | 5 | use std::sync::{Arc, Mutex}; 6 | use std::fmt::{self, Debug, Formatter}; 7 | use std::sync::atomic::{AtomicBool, Ordering}; 8 | use jobsteal::{self, WorkPool}; 9 | 10 | use {Actor, ActorSpawner}; 11 | use {ActorRef, ActorRefImpl, ActorRefEnum}; 12 | use {SendError}; 13 | 14 | #[cfg(test)] 15 | mod tests; 16 | 17 | /// An actor cell pointing to an actor running on a work pool. 18 | pub struct ActorCell { 19 | tx: Sender, 20 | state: Arc>, 21 | } 22 | 23 | struct ActorState { 24 | pool: Arc, 25 | scheduled: AtomicBool, 26 | actionable: Mutex>, 27 | } 28 | 29 | struct Actionable { 30 | rx: Receiver, 31 | actor: Box>, 32 | } 33 | 34 | pub struct WorkPoolSpawner { 35 | pool: Mutex, 36 | message_batch_size: usize, 37 | } 38 | 39 | impl WorkPoolSpawner { 40 | pub fn new() -> Arc { 41 | Arc::new( 42 | WorkPoolSpawner { 43 | pool: Mutex::new(jobsteal::make_pool(8).unwrap()), 44 | message_batch_size: 10, 45 | } 46 | ) 47 | } 48 | } 49 | 50 | impl ActorSpawner for Arc { 51 | /// Create and ActorCell for the given actor. 52 | fn spawn(&self, actor: A) -> ActorRef 53 | where Message: Send + 'static, A: Actor + 'static 54 | { 55 | let (tx, rx) = channel(); 56 | 57 | let actor_box = Box::new(actor) as Box>; 58 | let actionable = Mutex::new( 59 | Actionable { 60 | rx: rx, 61 | actor: actor_box, 62 | } 63 | ); 64 | let state = ActorState { 65 | pool: self.clone(), 66 | scheduled: AtomicBool::new(false), 67 | actionable: actionable, 68 | }; 69 | 70 | ActorRef( 71 | ActorRefEnum::InJobStealPool( 72 | ActorCell { 73 | tx: tx, 74 | state: Arc::new(state), 75 | } 76 | ) 77 | ) 78 | } 79 | } 80 | 81 | impl ActorRefImpl for ActorCell { 82 | fn send(&self, msg: Message) -> Result<(), SendError> { 83 | try!(self.tx.send(msg)); 84 | 85 | if !self.state.scheduled.swap(true, Ordering::AcqRel) { 86 | fn submit(local_state: &Arc>) { 87 | let state: Arc> = (*local_state).clone(); 88 | let mut pool = local_state.pool.pool.lock().expect("get access to pool"); 89 | pool.submit( move|_| { 90 | let ref mut actionable = state.actionable.lock().unwrap(); 91 | 92 | let mut last_receive_got_message = false; 93 | for _ in 0..state.pool.message_batch_size { 94 | last_receive_got_message = false; 95 | match actionable.rx.try_recv() { 96 | Ok(msg) => { 97 | debug!("Processing"); 98 | actionable.actor.process(msg); 99 | state.scheduled.swap(false, Ordering::AcqRel); 100 | last_receive_got_message = true; 101 | }, 102 | Err(TryRecvError::Empty) => { 103 | debug!("no more messages for now"); 104 | break; 105 | }, 106 | Err(TryRecvError::Disconnected) => { 107 | debug!("Quitting because of disconnect"); 108 | break; 109 | }, 110 | } 111 | } 112 | if last_receive_got_message { 113 | submit(&state); 114 | } 115 | }); 116 | }; 117 | submit(&self.state); 118 | } 119 | 120 | Ok(()) 121 | } 122 | } 123 | 124 | impl Debug for ActorCell { 125 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 126 | write!(f, "ActorCell") 127 | } 128 | } 129 | 130 | impl Clone for ActorCell { 131 | fn clone(&self) -> ActorCell { 132 | ActorCell { 133 | tx: self.tx.clone(), 134 | state: self.state.clone(), 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/js_pool/tests.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | 3 | use {ActorSpawner}; 4 | 5 | use tests::{CountingActor, CountingMessage, ForwardingActor, ForwardMessage}; 6 | 7 | use js_pool::{WorkPoolSpawner}; 8 | use channel::channel_actor_ref; 9 | 10 | #[test] 11 | fn test_counting_actor_with_dedicated_thread() { 12 | let counting_actor = CountingActor { count: 0 }; 13 | let spawner = WorkPoolSpawner::new(); 14 | let actor_ref = spawner.spawn(counting_actor); 15 | 16 | actor_ref.send(CountingMessage::Count).unwrap(); 17 | actor_ref.send(CountingMessage::Count).unwrap(); 18 | actor_ref.send(CountingMessage::Count).unwrap(); 19 | 20 | let (channel_ref, receive) = channel_actor_ref(); 21 | actor_ref.send(CountingMessage::GetCount(channel_ref)).unwrap(); 22 | let count = receive.recv().unwrap(); 23 | assert_eq!(count, 3) 24 | } 25 | 26 | #[test] 27 | fn test_actor_ref() { 28 | let counting_actor = CountingActor { count: 0 }; 29 | let spawner = WorkPoolSpawner::new(); 30 | let count_cell = spawner.spawn(counting_actor); 31 | 32 | println!("1"); 33 | 34 | let forwarding_actor = ForwardingActor; 35 | let forwarding_cell = spawner.spawn(forwarding_actor); 36 | let forwarding_ref = forwarding_cell.clone(); 37 | 38 | println!("2"); 39 | 40 | let count_ref = count_cell.clone(); 41 | forwarding_ref 42 | .send(ForwardMessage {forward_to: count_ref.clone(), message: CountingMessage::Count }) 43 | .ok().expect("send1 failed"); 44 | forwarding_ref 45 | .send(ForwardMessage {forward_to: count_ref.clone(), message: CountingMessage::Count }) 46 | .ok().expect("send2 failed"); 47 | forwarding_ref 48 | .send(ForwardMessage {forward_to: count_ref.clone(), message: CountingMessage::Count }) 49 | .ok().expect("send3 failed"); 50 | 51 | println!("3"); 52 | 53 | let (channel_ref, receive) = channel_actor_ref(); 54 | forwarding_ref 55 | .send(ForwardMessage {forward_to: count_ref.clone(), message: CountingMessage::GetCount(channel_ref) }) 56 | .expect("send 4 failed"); 57 | 58 | let count = receive.recv().expect("waiting for count failed"); 59 | assert_eq!(count, 3) 60 | } 61 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(missing_docs)] 2 | 3 | //! Actor-like concurrency for rust. 4 | 5 | #[macro_use] 6 | extern crate log; 7 | 8 | #[cfg(feature = "channel")] 9 | pub mod channel; 10 | 11 | #[cfg(feature = "thread")] 12 | pub mod thread; 13 | 14 | #[cfg(feature = "jobsteal")] 15 | extern crate jobsteal; 16 | 17 | #[cfg(feature = "jobsteal")] 18 | pub mod js_pool; 19 | 20 | #[cfg(test)] 21 | pub mod tests; 22 | 23 | use std::fmt; 24 | use std::sync::mpsc; 25 | 26 | /// Contains all variants of ActoRefs which are then hidden 27 | /// in the pub stuct `ActorRef` as private field. 28 | /// 29 | /// With this pattern, we allow stack allocated `ActorRef`s of 30 | /// different types. 31 | #[derive(Clone, Debug)] 32 | enum ActorRefEnum { 33 | #[cfg(feature = "channel")] 34 | Channel(channel::SenderActorRef), 35 | #[cfg(feature = "thread")] 36 | DedicatedThread(thread::ActorCell), 37 | #[cfg(feature = "jobsteal")] 38 | InJobStealPool(js_pool::ActorCell), 39 | } 40 | 41 | /// Corresponds to the public interface of ActorRef. 42 | /// `ActorRefEnum` variants can implement this interface to ensure 43 | /// that the forwarding method calls in `ActorRefEnum` are easy to implement. 44 | trait ActorRefImpl: Send + Clone + fmt::Debug { 45 | fn send(&self, msg: M) -> Result<(), SendError>; 46 | } 47 | 48 | /// A handle for passing messages to an actor 49 | /// or another message processing entity. 50 | /// 51 | /// All communication between actors should 52 | /// use this interface. 53 | pub struct ActorRef(ActorRefEnum); 54 | 55 | impl ActorRef { 56 | 57 | /// Send a message to the referenced actor 58 | /// or message processing entity. 59 | /// 60 | /// Depending on the type of the actorRef that might or 61 | /// might not guarantee delivery of the message. 62 | /// Also, the actor might not be alive anymore. 63 | pub fn send(&self, msg: M) -> Result<(), SendError> { 64 | let &ActorRef(ref variant) = self; 65 | match variant { 66 | #[cfg(feature = "channel")] 67 | &ActorRefEnum::Channel(ref sender) => sender.send(msg), 68 | #[cfg(feature = "thread")] 69 | &ActorRefEnum::DedicatedThread(ref cell) => cell.send(msg), 70 | #[cfg(feature = "jobsteal")] 71 | &ActorRefEnum::InJobStealPool(ref cell) => cell.send(msg), 72 | } 73 | } 74 | } 75 | 76 | impl fmt::Debug for ActorRef { 77 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 78 | let &ActorRef(ref variant) = self; 79 | match variant { 80 | #[cfg(feature = "channel")] 81 | &ActorRefEnum::Channel(ref sender) => write!(f, "ActorRef({:?})", sender), 82 | #[cfg(feature = "thread")] 83 | &ActorRefEnum::DedicatedThread(ref cell) => write!(f, "ActorRef({:?})", cell), 84 | #[cfg(feature = "jobsteal")] 85 | &ActorRefEnum::InJobStealPool(ref cell) => write!(f, "ActorRef({:?})", cell), 86 | } 87 | } 88 | } 89 | 90 | impl Clone for ActorRef { 91 | fn clone(&self) -> ActorRef { 92 | let &ActorRef(ref variant) = self; 93 | let variant = match variant { 94 | #[cfg(feature = "channel")] 95 | &ActorRefEnum::Channel(ref sender) => 96 | ActorRefEnum::Channel(sender.clone()), 97 | #[cfg(feature = "thread")] 98 | &ActorRefEnum::DedicatedThread(ref cell) => 99 | ActorRefEnum::DedicatedThread(cell.clone()), 100 | #[cfg(feature = "jobsteal")] 101 | &ActorRefEnum::InJobStealPool(ref cell) => 102 | ActorRefEnum::InJobStealPool(cell.clone()), 103 | }; 104 | ActorRef(variant) 105 | } 106 | } 107 | 108 | 109 | /// Reason for failed send. 110 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 111 | pub enum SendErrorReason { 112 | /// Message cannot be sent because the message 113 | /// buffer is full. 114 | Full, 115 | /// Message cannot be sent because the recipient 116 | /// is not reachable anymore. 117 | Unreachable, 118 | /// Unknown Error 119 | Unknown, 120 | } 121 | 122 | #[derive(PartialEq, Eq, Clone, Copy)] 123 | /// Error for attempted send. 124 | pub struct SendError(SendErrorReason, Message); 125 | 126 | impl From> for SendError { 127 | fn from(err: mpsc::SendError) -> SendError { 128 | match err { 129 | mpsc::SendError(message) => SendError(SendErrorReason::Unreachable, message) 130 | } 131 | } 132 | } 133 | 134 | impl fmt::Debug for SendError { 135 | fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { 136 | formatter.write_fmt(format_args!("SendError({:?}, ...)", self.0)) 137 | } 138 | } 139 | 140 | /// An actor can process messages that are sent 141 | /// to it sequentially. 142 | pub trait Actor: Send { 143 | /// Process one message, update state 144 | fn process(&mut self, msg: Message); 145 | } 146 | 147 | impl Actor for Func { 148 | fn process(&mut self, msg: Arg) { 149 | // let ref mut f = self; 150 | self(msg); 151 | } 152 | } 153 | 154 | /// An ActorSpawner allows spawning actors. It returns a reference counted 155 | /// ActorRef to communicate with the created Actor. If all references to 156 | /// the actor disappear, the thread should be stopped. 157 | pub trait ActorSpawner { 158 | /// Spawns a new actor returning an actor ref for passing messages to it. 159 | fn spawn(&self, actor: A) -> ActorRef 160 | where Message: Send + 'static, A: Actor + 'static; 161 | } 162 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | use Actor; 4 | use ActorRef; 5 | 6 | #[test] 7 | fn test_fn_mut_as_actor() { 8 | let mut counter = 0; 9 | { 10 | let mut func = |_| { 11 | counter+=1 12 | }; 13 | let counter_actor: &mut Actor = &mut func; 14 | counter_actor.process(2); 15 | } 16 | assert_eq!(counter, 1) 17 | } 18 | 19 | pub enum CountingMessage { 20 | Count, 21 | GetCount(ActorRef) 22 | } 23 | 24 | #[derive(Debug)] 25 | pub struct CountingActor { 26 | pub count: i32, 27 | } 28 | 29 | impl Actor for CountingActor { 30 | fn process(&mut self, msg: CountingMessage) { 31 | match msg { 32 | CountingMessage::Count => { 33 | debug!("count"); 34 | self.count += 1; 35 | }, 36 | CountingMessage::GetCount(sender) => { 37 | debug!("get count"); 38 | sender.send(self.count).expect("while responding with count"); 39 | } 40 | } 41 | 42 | } 43 | } 44 | 45 | #[test] 46 | fn test_counting_actor() { 47 | let mut counting_actor = CountingActor { count: 0 }; 48 | { 49 | let handle = thread::spawn( move|| { 50 | { 51 | let mut actor: &mut Actor = &mut counting_actor; 52 | actor.process(CountingMessage::Count); 53 | actor.process(CountingMessage::Count); 54 | actor.process(CountingMessage::Count); 55 | } 56 | assert_eq!(counting_actor.count, 3) 57 | }); 58 | handle.join().unwrap(); 59 | } 60 | } 61 | 62 | pub struct ForwardMessage { 63 | pub forward_to: ActorRef, 64 | pub message: Message, 65 | } 66 | 67 | #[derive(Debug)] 68 | pub struct ForwardingActor; 69 | 70 | impl Actor> for ForwardingActor { 71 | fn process(&mut self, message: ForwardMessage) { 72 | debug!("forward"); 73 | message.forward_to.send(message.message).ok(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/thread/mod.rs: -------------------------------------------------------------------------------- 1 | //! Dedicated single thread actor-ref implementations 2 | 3 | use std::thread; 4 | use std::sync::mpsc::{channel, Sender}; 5 | 6 | use std::sync::{Arc, Mutex}; 7 | use std::fmt::{self, Debug, Formatter}; 8 | 9 | use {Actor, ActorSpawner}; 10 | use {ActorRef, ActorRefImpl, ActorRefEnum}; 11 | use {SendError}; 12 | 13 | #[cfg(test)] 14 | mod tests; 15 | 16 | /// A simplistic environment to run an actor in 17 | /// which can act as ActorRef. 18 | /// 19 | /// It uses one thread per actor. 20 | pub struct ActorCell { 21 | tx: Sender, 22 | actor: Arc>>>, 23 | } 24 | 25 | /// An ActorSpawner which spawns a dedicated thread for every 26 | /// actor. 27 | pub struct DedicatedThreadSpawner; 28 | 29 | impl ActorSpawner for DedicatedThreadSpawner { 30 | /// Create and ActorCell for the given actor. 31 | fn spawn(&self, actor: A) -> ActorRef 32 | where Message: Send + 'static, A: Actor + 'static 33 | { 34 | let (tx, rx) = channel(); 35 | 36 | let actor_box: Box> = Box::new(actor); 37 | let actor = Arc::new(Mutex::new(actor_box)); 38 | let actor_for_thread = actor.clone(); 39 | thread::spawn( move|| { 40 | let mut actor = actor_for_thread.lock().unwrap(); 41 | 42 | loop { 43 | match rx.recv() { 44 | Ok(msg) => { 45 | debug!("Processing"); 46 | actor.process(msg); 47 | }, 48 | Err(error) => { 49 | debug!("Quitting: {:?}", error); 50 | break; 51 | }, 52 | } 53 | } 54 | }); 55 | 56 | ActorRef( 57 | ActorRefEnum::DedicatedThread( 58 | ActorCell { 59 | tx: tx, 60 | actor: actor 61 | } 62 | ) 63 | ) 64 | } 65 | } 66 | 67 | impl ActorRefImpl for ActorCell { 68 | fn send(&self, msg: Message) -> Result<(), SendError> { 69 | Ok(try!(self.tx.send(msg))) 70 | } 71 | } 72 | 73 | impl Debug for ActorCell { 74 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 75 | write!(f, "ActorCell") 76 | } 77 | } 78 | 79 | impl Clone for ActorCell { 80 | fn clone(&self) -> ActorCell { 81 | ActorCell { 82 | tx: self.tx.clone(), 83 | actor: self.actor.clone(), 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/thread/tests.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | 3 | use {ActorSpawner}; 4 | 5 | use tests::{CountingActor, CountingMessage, ForwardingActor, ForwardMessage}; 6 | 7 | use thread::{DedicatedThreadSpawner}; 8 | use channel::channel_actor_ref; 9 | 10 | #[test] 11 | fn test_counting_actor_with_dedicated_thread() { 12 | let counting_actor = CountingActor { count: 0 }; 13 | let spawner = DedicatedThreadSpawner; 14 | let actor_ref = spawner.spawn(counting_actor); 15 | 16 | actor_ref.send(CountingMessage::Count).unwrap(); 17 | actor_ref.send(CountingMessage::Count).unwrap(); 18 | actor_ref.send(CountingMessage::Count).unwrap(); 19 | 20 | let (channel_ref, receive) = channel_actor_ref(); 21 | actor_ref.send(CountingMessage::GetCount(channel_ref)).unwrap(); 22 | let count = receive.recv().unwrap(); 23 | assert_eq!(count, 3) 24 | } 25 | 26 | #[test] 27 | fn test_actor_ref() { 28 | let counting_actor = CountingActor { count: 0 }; 29 | let spawner = DedicatedThreadSpawner; 30 | let count_cell = spawner.spawn(counting_actor); 31 | 32 | println!("1"); 33 | 34 | let forwarding_actor = ForwardingActor; 35 | let forwarding_cell = spawner.spawn(forwarding_actor); 36 | let forwarding_ref = forwarding_cell.clone(); 37 | 38 | println!("2"); 39 | 40 | let count_ref = count_cell.clone(); 41 | forwarding_ref 42 | .send(ForwardMessage {forward_to: count_ref.clone(), message: CountingMessage::Count }) 43 | .ok().expect("send1 failed"); 44 | forwarding_ref 45 | .send(ForwardMessage {forward_to: count_ref.clone(), message: CountingMessage::Count }) 46 | .ok().expect("send2 failed"); 47 | forwarding_ref 48 | .send(ForwardMessage {forward_to: count_ref.clone(), message: CountingMessage::Count }) 49 | .ok().expect("send3 failed"); 50 | 51 | println!("3"); 52 | 53 | let (channel_ref, receive) = channel_actor_ref(); 54 | forwarding_ref 55 | .send(ForwardMessage {forward_to: count_ref.clone(), message: CountingMessage::GetCount(channel_ref) }) 56 | .expect("send 4 failed"); 57 | 58 | let count = receive.recv().expect("waiting for count failed"); 59 | assert_eq!(count, 3) 60 | } 61 | --------------------------------------------------------------------------------