From e9cbad199b3fd94d0c6be11506b0e75b387c64ee Mon Sep 17 00:00:00 2001 From: EnvOut <31275761+EnvOut@users.noreply.github.com> Date: Sat, 23 Nov 2024 16:30:56 +0200 Subject: [PATCH 1/4] removed garbage --- Cargo.lock | 223 ++------------------------- Cargo.toml | 7 +- experiments/actix-mpv/Cargo.toml | 9 -- experiments/actix-mpv/src/main.rs | 31 ---- experiments/toactor_play/Cargo.toml | 10 -- experiments/toactor_play/src/main.rs | 71 --------- experiments/tokio_play/Cargo.toml | 11 -- experiments/tokio_play/src/main.rs | 151 ------------------ 8 files changed, 12 insertions(+), 501 deletions(-) delete mode 100644 experiments/actix-mpv/Cargo.toml delete mode 100644 experiments/actix-mpv/src/main.rs delete mode 100644 experiments/toactor_play/Cargo.toml delete mode 100644 experiments/toactor_play/src/main.rs delete mode 100644 experiments/tokio_play/Cargo.toml delete mode 100644 experiments/tokio_play/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 4942d96..d5bd42d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,69 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "actix" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc9ef49f64074352f73ef9ec8c060a5f5799c96715c986a17f10933c3da2955c" -dependencies = [ - "actix-macros", - "actix-rt", - "actix_derive", - "bitflags 2.4.2", - "bytes", - "crossbeam-channel", - "futures-core", - "futures-sink", - "futures-task", - "futures-util", - "log", - "once_cell", - "parking_lot", - "pin-project-lite", - "smallvec", - "tokio", - "tokio-util", -] - -[[package]] -name = "actix-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-mpv" -version = "0.1.0" -dependencies = [ - "actix", -] - -[[package]] -name = "actix-rt" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" -dependencies = [ - "futures-core", - "tokio", -] - -[[package]] -name = "actix_derive" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c7db3d5a9718568e4cf4a537cfd7070e6e6ff7481510d0237fb529ac850f6d3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "addr2line" version = "0.21.0" @@ -107,18 +44,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" - [[package]] name = "bytes" version = "1.5.0" @@ -149,21 +74,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - [[package]] name = "deranged" version = "0.3.11" @@ -175,9 +85,9 @@ dependencies = [ [[package]] name = "derive-new" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" +checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", @@ -336,16 +246,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.20" @@ -425,29 +325,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - [[package]] name = "paste" version = "1.0.14" @@ -480,9 +357,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -526,27 +403,12 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "serde" version = "1.0.196" @@ -576,15 +438,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - [[package]] name = "slab" version = "0.4.9" @@ -612,9 +465,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -623,18 +476,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" dependencies = [ "proc-macro2", "quote", @@ -670,24 +523,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" -[[package]] -name = "toactor_play" -version = "0.1.0" -dependencies = [ - "tokactor", - "tokio", -] - -[[package]] -name = "tokactor" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9347d106cab235abbab396336c004efbf0da0edc1b9e74db63333990cfdf8b58" -dependencies = [ - "tokio", - "tracing", -] - [[package]] name = "tokio" version = "1.35.1" @@ -695,13 +530,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", - "bytes", "libc", "mio", "num_cpus", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys", @@ -718,41 +550,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-util" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "tokio_play" -version = "0.1.0" -dependencies = [ - "futures", - "tokio", - "tokio-stream", -] - [[package]] name = "tracing" version = "0.1.40" diff --git a/Cargo.toml b/Cargo.toml index c9468fe..d233124 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,9 +2,6 @@ resolver = "2" members = [ "src/uactor", - "experiments/actix-mpv", - "experiments/tokio_play", - "experiments/toactor_play", ] [workspace.dependencies] @@ -12,7 +9,7 @@ tokio = { version = "1.35.0", features = ["net", "sync", "time", "rt", "macros", futures = "0.3" # errors -thiserror = "1.0" +thiserror = "2" anyhow = { version = "1.0", features = ["backtrace"] } # tracing @@ -26,4 +23,4 @@ strum = { version = "0.26", features = ["derive"] } strum_macros = "0.26" derive_more = { version = "1", features = ["full"] } -derive-new = "0.6.0" +derive-new = "0.7" diff --git a/experiments/actix-mpv/Cargo.toml b/experiments/actix-mpv/Cargo.toml deleted file mode 100644 index 96b1f4c..0000000 --- a/experiments/actix-mpv/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "actix-mpv" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -actix = "0.13" \ No newline at end of file diff --git a/experiments/actix-mpv/src/main.rs b/experiments/actix-mpv/src/main.rs deleted file mode 100644 index a23d803..0000000 --- a/experiments/actix-mpv/src/main.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_imports)] -#![allow(unused_variables)] -#![allow(unused_mut)] -use actix::{Actor, ActorContext, AsyncContext, Context, Handler, Message}; - -fn main() { - struct MyActor; - - struct WhoAmI; - - impl Message for WhoAmI { - type Result = Result, ()>; - } - - impl Actor for MyActor { - type Context = Context; - } - - impl Handler for MyActor { - type Result = Result, ()>; - - fn handle(&mut self, msg: WhoAmI, ctx: &mut Context) -> Self::Result { - ctx.stop(); - Ok(ctx.address()) - } - } - - // let who_addr = addr.do_send(WhoAmI{}); - println!("Hello, world!"); -} diff --git a/experiments/toactor_play/Cargo.toml b/experiments/toactor_play/Cargo.toml deleted file mode 100644 index 29acd80..0000000 --- a/experiments/toactor_play/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "toactor_play" -version = "0.1.0" -edition = "2021" -readme = "../../README.md" -homepage = "https://serde.rs/" - -[dependencies] -tokio = "1.35.1" -tokactor = "2.1.0" \ No newline at end of file diff --git a/experiments/toactor_play/src/main.rs b/experiments/toactor_play/src/main.rs deleted file mode 100644 index 3e3b141..0000000 --- a/experiments/toactor_play/src/main.rs +++ /dev/null @@ -1,71 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_imports)] -#![allow(unused_variables)] -#![allow(unused_mut)] -use tokactor::{Actor, Ask, AskResult, Ctx}; - -struct Router {} - -pub struct PingPong { - counter: u8, -} - -/// This is the types of message [PingPong] supports -#[derive(Debug, Clone)] -pub enum Msg { - Ping, - Pong, -} -impl Msg { - // retrieve the next message in the sequence - fn next(&self) -> Self { - match self { - Self::Ping => Self::Pong, - Self::Pong => Self::Ping, - } - } - // print out this message - fn print(&self) { - match self { - Self::Ping => print!("ping.."), - Self::Pong => print!("pong.."), - } - } -} - -impl Actor for PingPong {} -impl Ask for PingPong { - type Result = Msg; - - // This is our main message handler - fn handle(&mut self, message: Msg, _: &mut Ctx) -> AskResult { - message.print(); - self.counter += 1; - AskResult::Reply(message.next()) - } -} - -#[tokio::main] -async fn main() { - // let world = World::new().unwrap(); - // - // let db = world.with_state(async || Db::connect().await.unwrap()); - // - // let router = Router { db }; - // - // let tcp_input = world.tcp_component("localhost:8080", router); - // - // world.on_input(tcp_input); - // - // world.block_until_completion(); - let handle = PingPong { counter: 0 }.start(); - let mut message = Msg::Ping; - for _ in 0..10 { - message = handle.ask(message).await.unwrap(); - } - let actor = handle - .await - .expect("Ping-pong actor failed to exit properly"); - assert_eq!(actor.counter, 10); - println!("\nProcessed {} messages", actor.counter); -} diff --git a/experiments/tokio_play/Cargo.toml b/experiments/tokio_play/Cargo.toml deleted file mode 100644 index 9ba3842..0000000 --- a/experiments/tokio_play/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "tokio_play" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -tokio = { workspace = true } -futures = { workspace = true } -tokio-stream = { version = "0.1", features = ["sync"] } \ No newline at end of file diff --git a/experiments/tokio_play/src/main.rs b/experiments/tokio_play/src/main.rs deleted file mode 100644 index 437ddd7..0000000 --- a/experiments/tokio_play/src/main.rs +++ /dev/null @@ -1,151 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_imports)] -#![allow(unused_variables)] -#![allow(unused_mut)] -use futures::StreamExt; -use std::rc::Rc; -use std::sync::Arc; -use std::time::Duration; -use tokio::runtime::Runtime; -use tokio::sync::mpsc::{Receiver, Sender}; -use tokio::sync::watch; -use tokio_stream::wrappers::WatchStream; - -async fn local() { - let nonsend_data = Rc::new("world"); - let local = tokio::task::LocalSet::new(); - - let nonsend_data2 = nonsend_data.clone(); - let util1 = local.run_until(async move { - // ... - tokio::time::sleep(tokio::time::Duration::from_millis(300)).await; - println!("hello {}", nonsend_data2) - }); - - let util2 = local.run_until(async move { - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - println!("goodbye {}", nonsend_data) - }); - - util1.await; - util2.await; -} - -// async fn main1() { -// #[derive(Debug)] -// enum Message1 { -// Ping, -// Pong -// } -// -// #[derive(Debug)] -// enum ReqMsg { -// GET -// } -// -// #[derive(Debug)] -// enum RespMsg { -// Ok, -// Err, -// } -// -// async fn process1(mut rx1: Receiver, mut req_rx: Receiver, resp_tx: Sender){ -// -// } -// -// let (tx1, mut rx1) = tokio::sync::mpsc::channel::(10); -// let (req_tx, mut req_rx) = tokio::sync::mpsc::channel::(10); -// let (resp_tx, mut resp_rx) = tokio::sync::mpsc::channel::(10); -// tokio::spawn(async { -// println!("started 1 process"); -// let local = tokio::task::LocalSet::new(); -// -// local.run_until(async { -// local.spawn_local(async { -// println!("started 1.1 process"); -// loop { -// println!("1.1 loop"); -// // if let Some(msg) = rx1.recv().await { -// // println!("msg1: {msg:?}") -// // } -// } -// }); -// // local.spawn_local(async { -// // println!("started 1.2 process"); -// // loop { -// // println!("1.2 loop"); -// // // if let Some(msg) = req_rx.recv().await { -// // // println!("req: {msg:?}"); -// // // resp_tx.send(RespMsg::Ok).await; -// // // } -// // } -// // }); -// // handle.await; -// }).await; -// -// Ok::<(), std::io::Error>(()) -// }); -// -// // let process2_handle = tokio::spawn(async move { -// // let local = tokio::task::LocalSet::new(); -// // local.spawn_local(async move { -// // loop { -// // if let Some(msg) = resp_rx.recv().await { -// // println!("resp: {msg:?}") -// // } -// // } -// // }); -// // }); -// -// -// tx1.send(Message1::Ping); -// req_tx.send(ReqMsg::GET); -// -// // tokio::join!(process1_handle, process2_handle); -// tokio::time::sleep(Duration::from_secs(10)).await; -// } - -trait Message {} - -#[tokio::main] -async fn main() { - // use futures::FutureExt; - // use futures::future::select_all; - // use tokio::sync::mpsc; - // - // // Предположим, что Message и Receiver уже определены - // let receivers: Vec> = vec![/* ... */]; - // - // let mut futures = Vec::new(); - // for mut receiver in receivers { - // let future = Box::pin(async move { - // let option = receiver.recv().await; - // option - // }); - // futures.push(future); - // } - // - // while !futures.is_empty() { - // let (result, index, remaining) = select_all(futures).await; - // futures = remaining; - // - // if let Some(msg) = result { - // println!("msg: {msg}"); - // } - // } - - let (tx, rx1) = watch::channel::(100500); - // WatchStream::new() - let mut stream1 = tokio_stream::wrappers::WatchStream::new(rx1.clone()); - let mut stream2 = tokio_stream::wrappers::WatchStream::new(rx1); - let value = stream1.next().await.unwrap(); - println!("value 1: {value:?}"); - let value = stream2.next().await.unwrap(); - println!("value 2: {value:?}"); - - let (_, rx2) = watch::channel::(100500); - // WatchStream::new() - let mut stream1 = tokio_stream::wrappers::WatchStream::new(rx2); - - // WatchStream::from(&) -} From f74c5ea0a5587c2eaac52e669ab74876cb97ca3b Mon Sep 17 00:00:00 2001 From: EnvOut <31275761+EnvOut@users.noreply.github.com> Date: Sat, 23 Nov 2024 16:31:07 +0200 Subject: [PATCH 2/4] restructured --- src/uactor/Cargo.toml | 5 +- src/uactor/examples/dependency_injection.rs | 20 +- src/uactor/examples/interval.rs | 10 +- .../examples/multiple_incoming_channels.rs | 10 +- src/uactor/examples/shared_state.rs | 6 +- src/uactor/examples/single_channel_actor.rs | 8 +- src/uactor/examples/supervised_actor.rs | 14 +- src/uactor/src/actor.rs | 258 ------------------ src/uactor/src/actor/abstract_actor.rs | 78 ++++++ src/uactor/src/actor/actor_ref.rs | 135 +++++++++ src/uactor/src/{ => actor}/context.rs | 32 ++- src/uactor/src/{ => actor}/message.rs | 0 src/uactor/src/actor/mod.rs | 6 + src/uactor/src/{ => actor}/select.rs | 6 +- src/uactor/src/actor/spawn/macros.rs | 49 ++++ src/uactor/src/actor/spawn/mod.rs | 2 + src/uactor/src/{ => data}/data_publisher.rs | 0 src/uactor/src/{ => data}/datasource.rs | 2 +- src/uactor/src/data/mod.rs | 2 + .../{di.rs => dependency_injection/mod.rs} | 14 +- src/uactor/src/lib.rs | 11 +- src/uactor/src/system.rs | 16 +- 22 files changed, 350 insertions(+), 334 deletions(-) delete mode 100644 src/uactor/src/actor.rs create mode 100644 src/uactor/src/actor/abstract_actor.rs create mode 100644 src/uactor/src/actor/actor_ref.rs rename src/uactor/src/{ => actor}/context.rs (93%) rename src/uactor/src/{ => actor}/message.rs (100%) create mode 100644 src/uactor/src/actor/mod.rs rename src/uactor/src/{ => actor}/select.rs (97%) create mode 100644 src/uactor/src/actor/spawn/macros.rs create mode 100644 src/uactor/src/actor/spawn/mod.rs rename src/uactor/src/{ => data}/data_publisher.rs (100%) rename src/uactor/src/{ => data}/datasource.rs (98%) create mode 100644 src/uactor/src/data/mod.rs rename src/uactor/src/{di.rs => dependency_injection/mod.rs} (87%) diff --git a/src/uactor/Cargo.toml b/src/uactor/Cargo.toml index 45b50c5..c8e550a 100644 --- a/src/uactor/Cargo.toml +++ b/src/uactor/Cargo.toml @@ -44,6 +44,7 @@ name = "single_channel_actor" name = "supervised_actor" [features] -default = ["tokio_tracing"] +default = ["tokio_tracing", "enable_spawn_macros"] async_sender = [] -tokio_tracing = [] \ No newline at end of file +tokio_tracing = [] +enable_spawn_macros = [] \ No newline at end of file diff --git a/src/uactor/examples/dependency_injection.rs b/src/uactor/examples/dependency_injection.rs index 2a97e03..b48c92c 100644 --- a/src/uactor/examples/dependency_injection.rs +++ b/src/uactor/examples/dependency_injection.rs @@ -1,5 +1,5 @@ use time::ext::NumericalStdDuration; -use uactor::actor::MessageSender; +use uactor::actor::abstract_actor::MessageSender; use uactor::system::System; @@ -13,7 +13,7 @@ use crate::messages::{MessageWithoutReply, PingMsg, PongMsg}; use crate::services::{Service1, Service2}; mod messages { - use uactor::message::{Message, Reply}; + use uactor::actor::message::{Message, Reply}; pub struct PingMsg(pub Reply); @@ -32,10 +32,10 @@ mod messages { mod actor1 { use tokio::sync::mpsc::UnboundedSender; - use uactor::actor::{Actor, HandleResult, Handler, MessageSender}; - use uactor::context::extensions::Service; - use uactor::context::Context; - use uactor::di::{Inject, InjectError}; + use uactor::actor::abstract_actor::{Actor, HandleResult, Handler, MessageSender}; + use uactor::actor::context::extensions::Service; + use uactor::actor::context::Context; + use uactor::dependency_injection::{Inject, InjectError}; use uactor::system::System; use crate::actor2::{Actor2Msg, Actor2Ref}; @@ -104,10 +104,10 @@ mod actor1 { } mod actor2 { - use uactor::actor::{Actor, HandleResult, Handler}; - use uactor::context::extensions::Service; - use uactor::context::Context; - use uactor::di::{Inject, InjectError}; + use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; + use uactor::actor::context::extensions::Service; + use uactor::actor::context::Context; + use uactor::dependency_injection::{Inject, InjectError}; use uactor::system::System; use crate::messages::{PingMsg, PongMsg, PrintMessage}; diff --git a/src/uactor/examples/interval.rs b/src/uactor/examples/interval.rs index 31fb45e..42d0862 100644 --- a/src/uactor/examples/interval.rs +++ b/src/uactor/examples/interval.rs @@ -1,5 +1,5 @@ use time::ext::NumericalStdDuration; -use uactor::actor::MessageSender; +use uactor::actor::abstract_actor::MessageSender; use uactor::system::System; @@ -9,7 +9,7 @@ use crate::actor1::Actor1Ref; use crate::messages::{PingMsg, PongMsg}; mod messages { - use uactor::message::{Message, Reply}; + use uactor::actor::message::{Message, Reply}; pub struct PingMsg(pub Reply); @@ -20,9 +20,9 @@ mod messages { } mod actor1 { - use uactor::actor::{Actor, HandleResult, Handler}; - use uactor::context::Context; - use uactor::message::IntervalMessage; + use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; + use uactor::actor::context::Context; + use uactor::actor::message::IntervalMessage; use crate::messages::{PingMsg, PongMsg}; diff --git a/src/uactor/examples/multiple_incoming_channels.rs b/src/uactor/examples/multiple_incoming_channels.rs index f7ca59b..4f1a99d 100644 --- a/src/uactor/examples/multiple_incoming_channels.rs +++ b/src/uactor/examples/multiple_incoming_channels.rs @@ -11,7 +11,7 @@ use crate::actor2::Actor2; use crate::messages::{PingPongMsg, ReqMsg, RespMsg}; pub mod messages { - use uactor::message::Message; + use uactor::actor::message::Message; use uactor::message_impl; #[derive(Debug)] @@ -35,8 +35,8 @@ pub mod messages { } pub mod actor1 { - use uactor::actor::{Actor, HandleResult, Handler}; - use uactor::context::Context; + use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; + use uactor::actor::context::Context; use crate::messages::{PingPongMsg, ReqMsg, RespMsg}; @@ -79,8 +79,8 @@ pub mod actor1 { } pub mod actor2 { - use uactor::actor::{Actor, HandleResult, Handler}; - use uactor::context::Context; + use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; + use uactor::actor::context::Context; use crate::messages::RespMsg; diff --git a/src/uactor/examples/shared_state.rs b/src/uactor/examples/shared_state.rs index 7e8a4ea..051b0d8 100644 --- a/src/uactor/examples/shared_state.rs +++ b/src/uactor/examples/shared_state.rs @@ -3,11 +3,11 @@ use std::time::Duration; use tracing::level_filters::LevelFilter; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; -use uactor::actor::{Actor, HandleResult, Handler, MessageSender}; -use uactor::context::Context; +use uactor::actor::abstract_actor::{Actor, HandleResult, Handler, MessageSender}; +use uactor::actor::context::Context; use uactor::system::System; -use uactor::message::{Message, Reply}; +use uactor::actor::message::{Message, Reply}; pub struct PingMsg; uactor::message_impl!(PingMsg); diff --git a/src/uactor/examples/single_channel_actor.rs b/src/uactor/examples/single_channel_actor.rs index b00741f..bdef80c 100644 --- a/src/uactor/examples/single_channel_actor.rs +++ b/src/uactor/examples/single_channel_actor.rs @@ -1,4 +1,4 @@ -use uactor::actor::MessageSender; +use uactor::actor::abstract_actor::MessageSender; use uactor::system::System; use crate::actor1::Actor1; @@ -7,7 +7,7 @@ use crate::actor1::Actor1Ref; use crate::messages::PingMsg; mod messages { - use uactor::message::{Message, Reply}; + use uactor::actor::message::{Message, Reply}; pub struct PingMsg(pub Reply); @@ -18,8 +18,8 @@ mod messages { } mod actor1 { - use uactor::actor::{Actor, HandleResult, Handler}; - use uactor::context::Context; + use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; + use uactor::actor::context::Context; use crate::messages::{PingMsg, PongMsg}; diff --git a/src/uactor/examples/supervised_actor.rs b/src/uactor/examples/supervised_actor.rs index 7649d16..e79139e 100644 --- a/src/uactor/examples/supervised_actor.rs +++ b/src/uactor/examples/supervised_actor.rs @@ -2,7 +2,7 @@ use std::time::Duration; use tracing::level_filters::LevelFilter; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; -use uactor::actor::MessageSender; +use uactor::actor::abstract_actor::MessageSender; use uactor::system::System; use crate::actor1::Actor1; @@ -12,7 +12,7 @@ use crate::messages::PingMsg; use crate::supervisor::{Supervisor, SupervisorMsg, SupervisorRef}; mod messages { - use uactor::message::{Message, Reply}; + use uactor::actor::message::{Message, Reply}; pub struct PingMsg(pub Reply); @@ -26,9 +26,9 @@ mod actor1 { use crate::messages::{PingMsg, PongMsg}; use crate::supervisor::{SupervisorMsg, SupervisorRef}; use tokio::sync::mpsc; - use uactor::actor::{Actor, HandleResult, Handler}; - use uactor::context::supervised::SupervisedContext; - use uactor::context::ActorContext; + use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; + use uactor::actor::context::supervised::SupervisedContext; + use uactor::actor::context::ActorContext; pub struct Actor1; @@ -59,8 +59,8 @@ mod actor1 { mod supervisor { use std::os::macos::raw::stat; - use uactor::actor::{Actor, HandleResult, Handler}; - use uactor::context::{ActorDied, Context}; + use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; + use uactor::actor::context::{ActorDied, Context}; pub struct Supervisor; diff --git a/src/uactor/src/actor.rs b/src/uactor/src/actor.rs deleted file mode 100644 index c35c5a5..0000000 --- a/src/uactor/src/actor.rs +++ /dev/null @@ -1,258 +0,0 @@ -use crate::context::ActorContext; -use crate::message::Message; -use std::future::Future; -use std::sync::Arc; - -pub trait State: std::any::Any + Send + 'static {} -impl State for T {} -pub type ActorPreStartResult = Result>; - -use crate::di::Inject; - -#[allow(unused_variables)] -pub trait Actor: Sized + Unpin + 'static { - /// Actor execution context type - type Context: ActorContext + Send; - - type Inject: Inject + Sized; - - type State: Default + Send + Sync; - - fn create_state(&mut self) -> Arc { - Arc::new(Default::default()) - } - - fn pre_start( - &mut self, - inject: &mut Self::Inject, - ctx: &mut Self::Context, - ) -> impl Future> + Send { - async { Ok(()) } - } -} -#[macro_export] -macro_rules! spawn_with_ref { - ($S: ident, $ActorName: ident, $ActorInstance: ident: $ActorType: ident, $($Timeout: ident),*) => {{ - uactor::paste! { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); - let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some($ActorName.to_owned()), ($($Timeout,)* rx)); - let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); - $S.insert_actor(actor_ref.name(), uactor::data_publisher::TryClone::try_clone(&actor_ref)?); - (actor_ref, handle) - } - }}; - - ($S: ident, $ActorInstance: ident: $ActorType: ident, $($Timeout: ident),*) => {{ - let actor_name: std::sync::Arc = stringify!($ActorInstance).into(); - - uactor::paste! { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); - let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some(actor_name), ($($Timeout,)* rx)); - let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); - $S.insert_actor(actor_ref.name(), uactor::data_publisher::TryClone::try_clone(&actor_ref)?); - (actor_ref, handle) - } - }}; - - ($S: ident, $ActorName: ident, $ActorInstance: ident: $ActorType: ident) => {{ - let actor_name: std::sync::Arc = stringify!($ActorInstance).into(); - uactor::paste! { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); - let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some(actor_name), (rx)); - let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); - $S.insert_actor(actor_ref.name(), uactor::data_publisher::TryClone::try_clone(&actor_ref)?); - (actor_ref, handle) - } - }}; - - ($S: ident, $ActorInstance: ident: $ActorType: ident) => {{ - let actor_name: std::sync::Arc = stringify!($ActorInstance).into(); - uactor::paste! { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); - let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some(actor_name), (rx)); - let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); - $S.insert_actor(actor_ref.name(), uactor::data_publisher::TryClone::try_clone(&actor_ref)?); - (actor_ref, handle) - } - }}; -} - -#[cfg(not(feature = "async_sender"))] -pub trait MessageSender -where - M: Message, -{ - fn send(&self, msg: M) -> crate::data_publisher::DataPublisherResult; - fn ask( - &self, - f: impl FnOnce(tokio::sync::oneshot::Sender) -> M, - ) -> impl Future>; -} - -#[cfg(feature = "async_sender")] -pub trait MessageSender -where - M: Message, -{ - async fn send(&self, msg: M) -> crate::data_publisher::DataPublisherResult; - async fn ask( - &self, - f: impl FnOnce(tokio::sync::oneshot::Sender) -> M, - ) -> Result; -} - -pub trait NamedActorRef { - fn static_name() -> &'static str; -} - -/// Example: -/// ``` -///# use std::future::Future; -/// use uactor::actor::{Actor, HandleResult}; -/// use uactor::context::Context; -/// use uactor::system::System; -/// let mut system = System::global().build(); -/// pub struct Actor1; -/// impl Actor for Actor1 { type Context = Context; type Inject = (); type State = (); } -/// let actor1 = Actor1; -/// use uactor::message::{Message}; -/// use uactor::message_impl; -/// pub struct Ping; -/// impl Message for Ping { fn static_name() -> &'static str { "Ping" } } -/// impl uactor::actor::Handler for Actor1 { async fn handle(&mut self, inject: &mut Self::Inject, msg: Ping, ctx: &mut Self::Context, state: &Self::State) -> HandleResult { todo!() } } -/// uactor::generate_actor_ref!(Actor1, { }); -/// ``` -/// let (mut actor1_ref, handle) = uactor::spawn_with_ref!(system, actor1: Actor1); -#[macro_export] -macro_rules! generate_actor_ref { - ($ActorType: ident, { $($Message: ident),* }) => { - uactor::paste! { - pub enum [<$ActorType Msg>] { - $($Message($Message)),* - } - - impl ::uactor::actor::NamedActorRef for [<$ActorType Msg>] { - fn static_name() -> &'static str { - stringify!([<$ActorType Msg>]) - } - } - - impl uactor::message::Message for [<$ActorType Msg>] { - fn static_name() -> &'static str { - stringify!([<$ActorType Msg>]) - } - - fn name(&self) -> String { - match self { - $( - Self::$Message(m) => m.name(), - )* - _ => Self::static_name().to_owned(), - } - } - } - - impl uactor::actor::Handler<[<$ActorType Msg>]> for $ActorType { - async fn handle( - &mut self, - inject: &mut ::Inject, - msg: [<$ActorType Msg>], - ctx: &mut ::Context, - state: &<$ActorType as uactor::actor::Actor>::State, - // state: &std::sync::Arc - // state: &Arc<{Self as uactor::actor::Actor}>::State - ) -> uactor::actor::HandleResult { - match msg { - $( - [<$ActorType Msg>]::$Message(m) => { - self.handle(inject, m, ctx, state).await?; - } - ),* - } - Ok(()) - } - } - - pub struct [<$ActorType Ref>] where T: uactor::data_publisher::DataPublisher]> + Clone { - name: std::sync::Arc, - state: std::sync::Arc<<$ActorType as uactor::actor::Actor>::State>, - sender: T, - } - - impl uactor::data_publisher::TryClone for [<$ActorType Ref>] where T: uactor::data_publisher::DataPublisher]> + Clone { - fn try_clone(&self) -> Result { - self.sender.try_clone().map(|sender| Self { name: self.name.clone(), sender, state: self.state.clone() }) - } - } - - impl Clone for [<$ActorType Ref>]]>> { - fn clone(&self) -> Self { - [<$ActorType Ref>]::new(self.name.clone(), self.sender.clone(), self.state.clone()) - } - } - - $( - #[cfg(not(feature = "async_sender"))] - impl uactor::actor::MessageSender<$Message> for [<$ActorType Ref>] where T: uactor::data_publisher::DataPublisher]> + Clone { - fn send(&self, msg: $Message) -> uactor::data_publisher::DataPublisherResult { - self.sender.publish([<$ActorType Msg>]::$Message(msg)) - } - - async fn ask(&self, f: impl FnOnce(tokio::sync::oneshot::Sender) -> $Message) -> Result { - let (tx, rx) = tokio::sync::oneshot::channel::(); - let message = f(tx); - self.sender.publish([<$ActorType Msg>]::$Message(message))?; - rx.await.map_err(uactor::data_publisher::DataPublisherErrors::from) - } - } - - #[cfg(feature = "async_sender")] - impl uactor::actor::MessageSender<$Message> for [<$ActorType Ref>] where T: uactor::data_publisher::DataPublisher]> + Clone { - async fn send(&self, msg: $Message) -> uactor::data_publisher::DataPublisherResult { - self.sender.publish([<$ActorType Msg>]::$Message(msg)).await - } - - async fn ask(&self, f: impl FnOnce(tokio::sync::oneshot::Sender) -> $Message) -> Result { - let (tx, rx) = tokio::sync::oneshot::channel::(); - let message = f(tx); - self.sender.publish([<$ActorType Msg>]::$Message(message))?; - rx.await.map_err(uactor::data_publisher::DataPublisherErrors::from) - } - } - )* - - impl [<$ActorType Ref>] where T: uactor::data_publisher::DataPublisher]> + Clone { - pub fn new(name: std::sync::Arc, sender: T, state: std::sync::Arc<<$ActorType as uactor::actor::Actor>::State>) -> Self { - let name = std::sync::Arc::from(name); - Self { name, sender, state } - } - - pub fn name(&self) -> std::sync::Arc { - self.name.clone() - } - - pub fn state(&self) -> &<$ActorType as uactor::actor::Actor>::State { - &self.state - } - } - } - - }; -} - -pub trait Handler -where - Self: Actor, - M: Message, -{ - /// This method is called for every message received by this actor. - fn handle( - &mut self, - inject: &mut Self::Inject, - msg: M, - ctx: &mut Self::Context, - state: &Self::State, - ) -> impl Future + Send; -} - -pub type HandleResult = Result<(), Box>; diff --git a/src/uactor/src/actor/abstract_actor.rs b/src/uactor/src/actor/abstract_actor.rs new file mode 100644 index 0000000..fe1942d --- /dev/null +++ b/src/uactor/src/actor/abstract_actor.rs @@ -0,0 +1,78 @@ +use crate::actor::context::ActorContext; +use crate::actor::message::Message; +use std::future::Future; +use std::sync::Arc; + +pub trait State: std::any::Any + Send + 'static {} +impl State for T {} +pub type ActorPreStartResult = Result>; + +use crate::dependency_injection::Inject; + +#[allow(unused_variables)] +pub trait Actor: Sized + Unpin + 'static { + /// Actor execution context type + type Context: ActorContext + Send; + + type Inject: Inject + Sized; + + type State: Default + Send + Sync; + + fn create_state(&mut self) -> Arc { + Arc::new(Default::default()) + } + + fn pre_start( + &mut self, + inject: &mut Self::Inject, + ctx: &mut Self::Context, + ) -> impl Future> + Send { + async { Ok(()) } + } +} + +pub trait Handler +where + Self: Actor, + M: Message, +{ + /// This method is called for every message received by this actor. + fn handle( + &mut self, + inject: &mut Self::Inject, + msg: M, + ctx: &mut Self::Context, + state: &Self::State, + ) -> impl Future + Send; +} + +pub type HandleResult = Result<(), Box>; + + +#[cfg(not(feature = "async_sender"))] +pub trait MessageSender +where + M: Message, +{ + fn send(&self, msg: M) -> crate::data::data_publisher::DataPublisherResult; + fn ask( + &self, + f: impl FnOnce(tokio::sync::oneshot::Sender) -> M, + ) -> impl Future>; +} + +#[cfg(feature = "async_sender")] +pub trait MessageSender +where + M: Message, +{ + async fn send(&self, msg: M) -> crate::data::data_publisher::DataPublisherResult; + async fn ask( + &self, + f: impl FnOnce(tokio::sync::oneshot::Sender) -> M, + ) -> Result; +} + +pub trait NamedActorRef { + fn static_name() -> &'static str; +} \ No newline at end of file diff --git a/src/uactor/src/actor/actor_ref.rs b/src/uactor/src/actor/actor_ref.rs new file mode 100644 index 0000000..782a021 --- /dev/null +++ b/src/uactor/src/actor/actor_ref.rs @@ -0,0 +1,135 @@ + +/// Example: +/// ``` +///# use std::future::Future; +/// use uactor::actor::abstract_actor::{Actor, HandleResult}; +/// use uactor::actor::context::Context; +/// use uactor::system::System; +/// let mut system = System::global().build(); +/// pub struct Actor1; +/// impl Actor for Actor1 { type Context = Context; type Inject = (); type State = (); } +/// let actor1 = Actor1; +/// use uactor::actor::message::{Message}; +/// use uactor::message_impl; +/// pub struct Ping; +/// impl Message for Ping { fn static_name() -> &'static str { "Ping" } } +/// impl uactor::actor::abstract_actor::Handler for Actor1 { async fn handle(&mut self, inject: &mut Self::Inject, msg: Ping, ctx: &mut Self::Context, state: &Self::State) -> HandleResult { todo!() } } +/// uactor::generate_actor_ref!(Actor1, { }); +/// ``` +/// let (mut actor1_ref, handle) = uactor::spawn_with_ref!(system, actor1: Actor1); +#[macro_export] +macro_rules! generate_actor_ref { + ($ActorType: ident, { $($Message: ident),* }) => { + uactor::paste! { + pub enum [<$ActorType Msg>] { + $($Message($Message)),* + } + + impl ::uactor::actor::abstract_actor::NamedActorRef for [<$ActorType Msg>] { + fn static_name() -> &'static str { + stringify!([<$ActorType Msg>]) + } + } + + impl uactor::actor::message::Message for [<$ActorType Msg>] { + fn static_name() -> &'static str { + stringify!([<$ActorType Msg>]) + } + + fn name(&self) -> String { + match self { + $( + Self::$Message(m) => m.name(), + )* + _ => Self::static_name().to_owned(), + } + } + } + + impl uactor::actor::abstract_actor::Handler<[<$ActorType Msg>]> for $ActorType { + async fn handle( + &mut self, + inject: &mut ::Inject, + msg: [<$ActorType Msg>], + ctx: &mut ::Context, + state: &<$ActorType as uactor::actor::abstract_actor::Actor>::State, + // state: &std::sync::Arc + // state: &Arc<{Self as uactor::actor::abstract_actor::Actor}>::State + ) -> uactor::actor::abstract_actor::HandleResult { + match msg { + $( + [<$ActorType Msg>]::$Message(m) => { + self.handle(inject, m, ctx, state).await?; + } + ),* + } + Ok(()) + } + } + + pub struct [<$ActorType Ref>] where T: uactor::data::data_publisher::DataPublisher]> + Clone { + name: std::sync::Arc, + state: std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>, + sender: T, + } + + impl uactor::data::data_publisher::TryClone for [<$ActorType Ref>] where T: uactor::data::data_publisher::DataPublisher]> + Clone { + fn try_clone(&self) -> Result { + self.sender.try_clone().map(|sender| Self { name: self.name.clone(), sender, state: self.state.clone() }) + } + } + + impl Clone for [<$ActorType Ref>]]>> { + fn clone(&self) -> Self { + [<$ActorType Ref>]::new(self.name.clone(), self.sender.clone(), self.state.clone()) + } + } + + $( + #[cfg(not(feature = "async_sender"))] + impl uactor::actor::abstract_actor::MessageSender<$Message> for [<$ActorType Ref>] where T: uactor::data::data_publisher::DataPublisher]> + Clone { + fn send(&self, msg: $Message) -> uactor::data::data_publisher::DataPublisherResult { + self.sender.publish([<$ActorType Msg>]::$Message(msg)) + } + + async fn ask(&self, f: impl FnOnce(tokio::sync::oneshot::Sender) -> $Message) -> Result { + let (tx, rx) = tokio::sync::oneshot::channel::(); + let message = f(tx); + self.sender.publish([<$ActorType Msg>]::$Message(message))?; + rx.await.map_err(uactor::data::data_publisher::DataPublisherErrors::from) + } + } + + #[cfg(feature = "async_sender")] + impl uactor::actor::abstract_actor::MessageSender<$Message> for [<$ActorType Ref>] where T: uactor::data::data_publisher::DataPublisher]> + Clone { + async fn send(&self, msg: $Message) -> uactor::data::data_publisher::DataPublisherResult { + self.sender.publish([<$ActorType Msg>]::$Message(msg)).await + } + + async fn ask(&self, f: impl FnOnce(tokio::sync::oneshot::Sender) -> $Message) -> Result { + let (tx, rx) = tokio::sync::oneshot::channel::(); + let message = f(tx); + self.sender.publish([<$ActorType Msg>]::$Message(message))?; + rx.await.map_err(uactor::data::data_publisher::DataPublisherErrors::from) + } + } + )* + + impl [<$ActorType Ref>] where T: uactor::data::data_publisher::DataPublisher]> + Clone { + pub fn new(name: std::sync::Arc, sender: T, state: std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>) -> Self { + let name = std::sync::Arc::from(name); + Self { name, sender, state } + } + + pub fn name(&self) -> std::sync::Arc { + self.name.clone() + } + + pub fn state(&self) -> &<$ActorType as uactor::actor::abstract_actor::Actor>::State { + &self.state + } + } + } + + }; +} \ No newline at end of file diff --git a/src/uactor/src/context.rs b/src/uactor/src/actor/context.rs similarity index 93% rename from src/uactor/src/context.rs rename to src/uactor/src/actor/context.rs index 285de6a..f11bc8b 100644 --- a/src/uactor/src/context.rs +++ b/src/uactor/src/actor/context.rs @@ -1,4 +1,4 @@ -use crate::message::Message; +use crate::actor::message::Message; use crate::system::System; use std::future::Future; use std::sync::Arc; @@ -19,6 +19,10 @@ pub trait ActorContext: Sized + Unpin + 'static { fn on_iteration(&mut self) -> ContextResult<()> { Ok(()) } + #[inline] + fn on_error(&mut self) -> ContextResult<()> { + Ok(()) + } fn kill(&mut self); fn get_name(&self) -> &str; #[allow(clippy::wrong_self_convention)] @@ -64,9 +68,9 @@ impl ActorContext for Context { } pub mod supervised { - use crate::actor::MessageSender; - use crate::context::{ActorContext, ActorDied, ContextInitializationError, ContextResult}; - use crate::data_publisher::TryClone; + use crate::actor::abstract_actor::MessageSender; + use crate::actor::context::{ActorContext, ActorDied, ContextInitializationError, ContextResult}; + use crate::data::data_publisher::TryClone; use crate::system::{utils, System}; use std::sync::Arc; @@ -186,7 +190,7 @@ pub mod extensions { /// # Example /// /// ``` - /// # use uactor::context::extensions::Extensions; + /// # use uactor::actor::context::extensions::Extensions; /// let mut ext = Extensions::new(); /// assert!(ext.insert(5i32).is_none()); /// assert!(ext.insert(4u8).is_none()); @@ -211,7 +215,7 @@ pub mod extensions { /// # Example /// /// ``` - /// # use uactor::context::extensions::Extensions; + /// # use uactor::actor::context::extensions::Extensions; /// let mut ext = Extensions::new(); /// assert!(ext.get::().is_none()); /// ext.insert(5i32); @@ -230,7 +234,7 @@ pub mod extensions { /// # Example /// /// ``` - /// # use uactor::context::extensions::Extensions; + /// # use uactor::actor::context::extensions::Extensions; /// let mut ext = Extensions::new(); /// ext.insert(String::from("Hello")); /// ext.get_mut::().unwrap().push_str(" World"); @@ -251,7 +255,7 @@ pub mod extensions { /// # Example /// /// ``` - /// # use uactor::context::extensions::Extensions; + /// # use uactor::actor::context::extensions::Extensions; /// let mut ext = Extensions::new(); /// ext.insert(5i32); /// assert_eq!(ext.remove::(), Some(5i32)); @@ -274,7 +278,7 @@ pub mod extensions { /// # Example /// /// ``` - /// # use uactor::context::extensions::Extensions; + /// # use uactor::actor::context::extensions::Extensions; /// let mut ext = Extensions::new(); /// ext.insert(5i32); /// ext.clear(); @@ -293,7 +297,7 @@ pub mod extensions { /// # Example /// /// ``` - /// # use uactor::context::extensions::Extensions; + /// # use uactor::actor::context::extensions::Extensions; /// let mut ext = Extensions::new(); /// assert!(ext.is_empty()); /// ext.insert(5i32); @@ -309,7 +313,7 @@ pub mod extensions { /// # Example /// /// ``` - /// # use uactor::context::extensions::Extensions; + /// # use uactor::actor::context::extensions::Extensions; /// let mut ext = Extensions::new(); /// assert_eq!(ext.len(), 0); /// ext.insert(5i32); @@ -328,7 +332,7 @@ pub mod extensions { /// # Example /// /// ``` - /// # use uactor::context::extensions::Extensions; + /// # use uactor::actor::context::extensions::Extensions; /// let mut ext_a = Extensions::new(); /// ext_a.insert(8u8); /// ext_a.insert(16u16); @@ -394,8 +398,8 @@ pub mod extensions { } pub mod actor_registry { - use crate::context::extensions::IdHasher; - use crate::data_publisher::{TryClone, TryCloneError}; + use crate::actor::context::extensions::IdHasher; + use crate::data::data_publisher::{TryClone, TryCloneError}; use std::any::{Any, TypeId}; use std::collections::HashMap; use std::fmt; diff --git a/src/uactor/src/message.rs b/src/uactor/src/actor/message.rs similarity index 100% rename from src/uactor/src/message.rs rename to src/uactor/src/actor/message.rs diff --git a/src/uactor/src/actor/mod.rs b/src/uactor/src/actor/mod.rs new file mode 100644 index 0000000..7edefd4 --- /dev/null +++ b/src/uactor/src/actor/mod.rs @@ -0,0 +1,6 @@ +pub mod abstract_actor; +pub mod spawn; +pub mod context; +pub mod message; +pub mod actor_ref; +pub mod select; diff --git a/src/uactor/src/select.rs b/src/uactor/src/actor/select.rs similarity index 97% rename from src/uactor/src/select.rs rename to src/uactor/src/actor/select.rs index c63b95c..3dfd1af 100644 --- a/src/uactor/src/select.rs +++ b/src/uactor/src/actor/select.rs @@ -1,6 +1,6 @@ -use crate::actor::{Actor, HandleResult, Handler}; -use crate::datasource::DataSource; -use crate::message::Message; +use crate::actor::abstract_actor::{Actor, HandleResult, Handler}; +use crate::data::datasource::DataSource; +use crate::actor::message::Message; use std::future::pending; pub trait ActorSelect { diff --git a/src/uactor/src/actor/spawn/macros.rs b/src/uactor/src/actor/spawn/macros.rs new file mode 100644 index 0000000..893a720 --- /dev/null +++ b/src/uactor/src/actor/spawn/macros.rs @@ -0,0 +1,49 @@ +use crate::actor::message::Message; +use std::future::Future; + +#[macro_export] +macro_rules! spawn_with_ref { + ($S: ident, $ActorName: ident, $ActorInstance: ident: $ActorType: ident, $($Timeout: ident),*) => {{ + uactor::paste! { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); + let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some($ActorName.to_owned()), ($($Timeout,)* rx)); + let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); + $S.insert_actor(actor_ref.name(), uactor::data::data_publisher::TryClone::try_clone(&actor_ref)?); + (actor_ref, handle) + } + }}; + + ($S: ident, $ActorInstance: ident: $ActorType: ident, $($Timeout: ident),*) => {{ + let actor_name: std::sync::Arc = stringify!($ActorInstance).into(); + + uactor::paste! { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); + let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some(actor_name), ($($Timeout,)* rx)); + let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); + $S.insert_actor(actor_ref.name(), uactor::data::data_publisher::TryClone::try_clone(&actor_ref)?); + (actor_ref, handle) + } + }}; + + ($S: ident, $ActorName: ident, $ActorInstance: ident: $ActorType: ident) => {{ + let actor_name: std::sync::Arc = stringify!($ActorInstance).into(); + uactor::paste! { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); + let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some(actor_name), (rx)); + let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); + $S.insert_actor(actor_ref.name(), uactor::data::data_publisher::TryClone::try_clone(&actor_ref)?); + (actor_ref, handle) + } + }}; + + ($S: ident, $ActorInstance: ident: $ActorType: ident) => {{ + let actor_name: std::sync::Arc = stringify!($ActorInstance).into(); + uactor::paste! { + let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); + let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some(actor_name), (rx)); + let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); + $S.insert_actor(actor_ref.name(), uactor::data::data_publisher::TryClone::try_clone(&actor_ref)?); + (actor_ref, handle) + } + }}; +} diff --git a/src/uactor/src/actor/spawn/mod.rs b/src/uactor/src/actor/spawn/mod.rs new file mode 100644 index 0000000..f3c41c9 --- /dev/null +++ b/src/uactor/src/actor/spawn/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "enable_spawn_macros")] +pub mod macros; \ No newline at end of file diff --git a/src/uactor/src/data_publisher.rs b/src/uactor/src/data/data_publisher.rs similarity index 100% rename from src/uactor/src/data_publisher.rs rename to src/uactor/src/data/data_publisher.rs diff --git a/src/uactor/src/datasource.rs b/src/uactor/src/data/datasource.rs similarity index 98% rename from src/uactor/src/datasource.rs rename to src/uactor/src/data/datasource.rs index 193cb34..4dfcb57 100644 --- a/src/uactor/src/datasource.rs +++ b/src/uactor/src/data/datasource.rs @@ -3,7 +3,7 @@ use tokio::net::{TcpListener, TcpStream}; use tokio::sync::{broadcast, mpsc, oneshot, watch}; use tokio::time::Interval; -use crate::message::IntervalMessage; +use crate::actor::message::IntervalMessage; pub type DataSourceResult = Result; diff --git a/src/uactor/src/data/mod.rs b/src/uactor/src/data/mod.rs new file mode 100644 index 0000000..15d9183 --- /dev/null +++ b/src/uactor/src/data/mod.rs @@ -0,0 +1,2 @@ +pub mod datasource; +pub mod data_publisher; \ No newline at end of file diff --git a/src/uactor/src/di.rs b/src/uactor/src/dependency_injection/mod.rs similarity index 87% rename from src/uactor/src/di.rs rename to src/uactor/src/dependency_injection/mod.rs index 10d1291..2a88b5b 100644 --- a/src/uactor/src/di.rs +++ b/src/uactor/src/dependency_injection/mod.rs @@ -1,7 +1,7 @@ use std::future::Future; -use crate::context::actor_registry::ActorRegistryErrors; -use crate::context::extensions::ExtensionErrors; +use crate::actor::context::actor_registry::ActorRegistryErrors; +use crate::actor::context::extensions::ExtensionErrors; use crate::system::System; #[derive(thiserror::Error, Debug)] @@ -14,7 +14,7 @@ pub enum InjectError { /// Sample: /// ``` -///# use uactor::context::extensions::Service; +///# use uactor::actor::context::extensions::Service; /// use uactor::di::{Inject, InjectError}; ///# use uactor::system::System; /// @@ -41,10 +41,10 @@ pub trait Inject { } pub mod inject_impls { - use crate::actor::NamedActorRef; - use crate::context::extensions::Service; - use crate::data_publisher::TryClone; - use crate::di::{Inject, InjectError}; + use crate::actor::abstract_actor::NamedActorRef; + use crate::actor::context::extensions::Service; + use crate::data::data_publisher::TryClone; + use crate::dependency_injection::{Inject, InjectError}; use crate::system::System; use std::sync::Arc; diff --git a/src/uactor/src/lib.rs b/src/uactor/src/lib.rs index 5285a46..7f99305 100644 --- a/src/uactor/src/lib.rs +++ b/src/uactor/src/lib.rs @@ -1,10 +1,7 @@ -pub mod actor; -pub mod context; -pub mod data_publisher; -pub mod datasource; -pub mod di; -pub mod message; -pub mod select; +pub mod dependency_injection; pub mod system; pub use paste::paste; + +pub mod data; +pub mod actor; diff --git a/src/uactor/src/system.rs b/src/uactor/src/system.rs index 1e1432d..26798ae 100644 --- a/src/uactor/src/system.rs +++ b/src/uactor/src/system.rs @@ -1,10 +1,10 @@ -use crate::actor::Actor; -use crate::context::actor_registry::{ActorRegistry, ActorRegistryErrors}; -use crate::context::extensions::{ExtensionErrors, Extensions, Service}; -use crate::context::ActorContext; -use crate::data_publisher::{TryClone, TryCloneError}; -use crate::di::{Inject, InjectError}; -use crate::select::ActorSelect; +use crate::actor::abstract_actor::Actor; +use crate::actor::context::actor_registry::{ActorRegistry, ActorRegistryErrors}; +use crate::actor::context::extensions::{ExtensionErrors, Extensions, Service}; +use crate::actor::context::ActorContext; +use crate::data::data_publisher::{TryClone, TryCloneError}; +use crate::dependency_injection::{Inject, InjectError}; +use crate::actor::select::ActorSelect; use crate::system::builder::SystemBuilder; use std::any::Any; use std::collections::HashMap; @@ -241,7 +241,7 @@ impl System { } pub mod builder { - use crate::context::extensions::{Extensions, Service}; + use crate::actor::context::extensions::{Extensions, Service}; use crate::system::System; use std::sync::Arc; From af32b86239cf74027f4f6f47c34f04626daf7f4c Mon Sep 17 00:00:00 2001 From: EnvOut <31275761+EnvOut@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:15:25 +0200 Subject: [PATCH 3/4] updated actors --- Cargo.lock | 12 + README.md | 3 + src/uactor/Cargo.toml | 1 + src/uactor/examples/dependency_injection.rs | 20 +- src/uactor/examples/interval.rs | 8 +- .../examples/multiple_incoming_channels.rs | 22 +- src/uactor/examples/shared_state.rs | 23 +- src/uactor/examples/single_channel_actor.rs | 8 +- src/uactor/src/actor/abstract_actor.rs | 24 +- src/uactor/src/actor/actor_ref.rs | 29 +- src/uactor/src/actor/context.rs | 42 ++- src/uactor/src/actor/mod.rs | 1 - src/uactor/src/actor/spawn/macros.rs | 49 ---- src/uactor/src/actor/spawn/mod.rs | 2 - src/uactor/src/aliases.rs | 3 + src/uactor/src/data/data_publisher.rs | 3 +- src/uactor/src/dependency_injection/mod.rs | 7 +- src/uactor/src/lib.rs | 1 + src/uactor/src/system.rs | 249 ++++++++---------- 19 files changed, 247 insertions(+), 260 deletions(-) delete mode 100644 src/uactor/src/actor/spawn/macros.rs delete mode 100644 src/uactor/src/actor/spawn/mod.rs create mode 100644 src/uactor/src/aliases.rs diff --git a/Cargo.lock b/Cargo.lock index d5bd42d..5c9dab7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -550,6 +550,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tracing" version = "0.1.40" @@ -622,6 +633,7 @@ dependencies = [ "thiserror", "time", "tokio", + "tokio-stream", "tracing", "tracing-subscriber", ] diff --git a/README.md b/README.md index 5188f8a..2ea3a07 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ Examples can be found [here](src/uactor/examples). 9. Implemented support for actors for which it is necessary to work with multiple message sources (channels) [Example: Multi channel](src/uactor/examples/multiple_incoming_channels.rs) 10. Implemented shared state for actors [Example: Shared state](src/uactor/examples/shared_state.rs) +### Actor lifecycle +![Lifecycle.png](docs/assets/Lifecycle.png) + ### Other projects: 1. Actix 2. Ractor diff --git a/src/uactor/Cargo.toml b/src/uactor/Cargo.toml index c8e550a..ad60e48 100644 --- a/src/uactor/Cargo.toml +++ b/src/uactor/Cargo.toml @@ -11,6 +11,7 @@ description = "The fastest and most modular actor system that doesn't force you [dependencies] tokio = { workspace = true } +tokio-stream = "0.1" futures = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } diff --git a/src/uactor/examples/dependency_injection.rs b/src/uactor/examples/dependency_injection.rs index b48c92c..b3e4e7b 100644 --- a/src/uactor/examples/dependency_injection.rs +++ b/src/uactor/examples/dependency_injection.rs @@ -1,4 +1,5 @@ use time::ext::NumericalStdDuration; +use tokio::sync::mpsc::UnboundedSender; use uactor::actor::abstract_actor::MessageSender; use uactor::system::System; @@ -35,10 +36,11 @@ mod actor1 { use uactor::actor::abstract_actor::{Actor, HandleResult, Handler, MessageSender}; use uactor::actor::context::extensions::Service; use uactor::actor::context::Context; + use uactor::data::data_publisher::DataPublisher; use uactor::dependency_injection::{Inject, InjectError}; use uactor::system::System; - use crate::actor2::{Actor2Msg, Actor2Ref}; + use crate::actor2::{Actor2, Actor2Msg, Actor2Ref}; use crate::messages::{MessageWithoutReply, PingMsg, PongMsg, PrintMessage}; use crate::services::Service1; @@ -56,8 +58,7 @@ mod actor1 { Self: Sized, { let service1 = system.get_service()?; - let actor2_ref = - system.get_actor::>>("actor2".into())?; + let actor2_ref = system.get_actor::>("actor2".into())?; Ok(Services::new(service1, actor2_ref)) } } @@ -207,16 +208,17 @@ async fn main() -> anyhow::Result<()> { .build(); // Init actor (instance + spawn actor) - let actor1 = Actor1; - let (actor1_ref, _) = uactor::spawn_with_ref!(system, actor1: Actor1); + let (actor1_ref, actor1_stream) = system.register_ref::>>("actor1"); // Init actor2 (instance + spawn actor) - let actor2 = Actor2; - let (actor2_ref, _) = uactor::spawn_with_ref!(system, actor2: Actor2); + let (actor2_ref, actor2_stream) = system.register_ref::>>("actor2"); // Run actors - system.run_actor::(actor1_ref.name()).await?; - system.run_actor::(actor2_ref.name()).await?; + let actor1 = Actor1; + system.spawn_actor(actor1_ref.name(), actor1, *actor1_ref.state(), (actor1_stream)).await?; + + let actor2 = Actor2; + system.spawn_actor(actor2_ref.name(), actor2, *actor2_ref.state(), (actor2_stream)).await?; // Case #1: send messages and call injected (not from &self) services inside handlers println!( diff --git a/src/uactor/examples/interval.rs b/src/uactor/examples/interval.rs index 42d0862..da94dca 100644 --- a/src/uactor/examples/interval.rs +++ b/src/uactor/examples/interval.rs @@ -1,4 +1,5 @@ use time::ext::NumericalStdDuration; +use tokio::sync::mpsc::UnboundedSender; use uactor::actor::abstract_actor::MessageSender; use uactor::system::System; @@ -77,16 +78,15 @@ mod actor1 { #[tokio::main] async fn main() -> anyhow::Result<()> { - let actor1 = Actor1::default(); - let mut system = System::global().build(); // 1 second interval let interval = tokio::time::interval(1.std_seconds()); - let (actor1_ref, _) = uactor::spawn_with_ref!(system, actor1: Actor1, interval); + let (actor1_ref, actor1_stream) = system.register_ref::>>("actor1"); - system.run_actor::(actor1_ref.name()).await?; + let actor1 = Actor1::default(); + system.spawn_actor(actor1_ref.name(), actor1, *actor1_ref.state(), (actor1_stream, interval)).await?; let pong = actor1_ref.ask::(PingMsg).await?; println!("main: received {pong:?} message"); diff --git a/src/uactor/examples/multiple_incoming_channels.rs b/src/uactor/examples/multiple_incoming_channels.rs index 4f1a99d..ae17fe6 100644 --- a/src/uactor/examples/multiple_incoming_channels.rs +++ b/src/uactor/examples/multiple_incoming_channels.rs @@ -107,12 +107,15 @@ pub mod actor2 { } #[tokio::main] -async fn main() { +async fn main() -> anyhow::Result<()> { tracing_subscriber::registry() .with(LevelFilter::TRACE) .with(tracing_subscriber::fmt::layer()) .init(); + // Initialize system + let mut system = System::global().build(); + // Initialize channels let (ping_tx, ping_rx) = tokio::sync::mpsc::channel::(10); let (req_tx, req_rx) = tokio::sync::mpsc::channel::(10); @@ -122,23 +125,18 @@ async fn main() { let actor1 = Actor1 { resp_tx }; let actor2 = Actor2; - // Initialize system - let mut system = System::global().build(); - - // Initialize actors - let (actor1_name, shared_state, handle1) = system.init_actor(actor1, None, (ping_rx, req_rx)); - let (actor2_name, shared_state, handle2) = system.init_actor(actor2, None, resp_rx); - // Run actors - system.run_actor::(actor1_name).await.unwrap(); - system.run_actor::(actor2_name).await.unwrap(); + let (_, handle1) = system.spawn_actor("actor1".into(), actor1, (), (ping_rx, req_rx)).await?; + let (_, handle2) = system.spawn_actor("actor2".into(), actor2, (), (resp_rx)).await?; // Send messages - ping_tx.send(PingPongMsg::Ping).await.unwrap(); - req_tx.send(ReqMsg::GET).await.unwrap(); + ping_tx.send(PingPongMsg::Ping).await?; + req_tx.send(ReqMsg::GET).await?; // Tokio aspects to stop spawned tasks without errors tokio::time::sleep(Duration::from_nanos(1)).await; handle1.abort_handle().abort(); handle2.abort_handle().abort(); + + Ok(()) } diff --git a/src/uactor/examples/shared_state.rs b/src/uactor/examples/shared_state.rs index 051b0d8..cfd5d94 100644 --- a/src/uactor/examples/shared_state.rs +++ b/src/uactor/examples/shared_state.rs @@ -1,5 +1,7 @@ +use std::sync::Arc; use std::sync::atomic::{AtomicU8, Ordering}; use std::time::Duration; +use tokio::sync::mpsc::UnboundedSender; use tracing::level_filters::LevelFilter; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; @@ -16,13 +18,19 @@ pub struct Actor1; #[derive(Default)] pub struct Actor1State { - pub counter: AtomicU8, + counter: AtomicU8, +} + +impl Actor1State { + pub fn get_counter(&self) -> u8 { + self.counter.load(Ordering::Relaxed) + } } impl Actor for Actor1 { type Context = Context; type Inject = (); - type State = Actor1State; + type State = Arc; } impl Handler for Actor1 { @@ -49,11 +57,10 @@ async fn main() -> anyhow::Result<()> { let mut system = System::global().build(); - let actor1 = Actor1; - - let (actor1_ref, _) = uactor::spawn_with_ref!(system, actor1: Actor1); + let (actor1_ref, actor1_stream) = system.register_ref::>>("actor1"); - system.run_actor::(actor1_ref.name()).await?; + let actor1 = Actor1; + let (_, handle) = system.spawn_actor(actor1_ref.name(), actor1, actor1_ref.state().clone(), (actor1_stream)).await?; let pong = actor1_ref.send(PingMsg); let pong = actor1_ref.send(PingMsg); @@ -61,7 +68,9 @@ async fn main() -> anyhow::Result<()> { tokio::time::sleep(Duration::from_secs(1)).await; - assert_eq!(actor1_ref.state.counter.load(Ordering::Relaxed), 3); + assert_eq!(actor1_ref.state.get_counter(), 3); + // stop the actor + handle.abort_handle().abort(); Ok(()) } diff --git a/src/uactor/examples/single_channel_actor.rs b/src/uactor/examples/single_channel_actor.rs index bdef80c..72712eb 100644 --- a/src/uactor/examples/single_channel_actor.rs +++ b/src/uactor/examples/single_channel_actor.rs @@ -1,4 +1,6 @@ +use tokio::sync::mpsc::UnboundedSender; use uactor::actor::abstract_actor::MessageSender; +use uactor::aliases::ActorName; use uactor::system::System; use crate::actor1::Actor1; @@ -18,6 +20,7 @@ mod messages { } mod actor1 { + use tokio::sync::mpsc::UnboundedReceiver; use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; use uactor::actor::context::Context; @@ -55,8 +58,9 @@ async fn main() -> anyhow::Result<()> { let mut system = System::global().build(); - let (actor1_ref, _) = uactor::spawn_with_ref!(system, actor1: Actor1); - system.run_actor::(actor1_ref.name()).await?; + let (actor1_ref, actor1_stream) = system.register_ref::>>("actor1"); + + system.spawn_actor(actor1_ref.name(), actor1, (actor1_stream)).await?; let pong = actor1_ref.ask(PingMsg).await?; println!("main: received {pong:?} message"); diff --git a/src/uactor/src/actor/abstract_actor.rs b/src/uactor/src/actor/abstract_actor.rs index fe1942d..6591064 100644 --- a/src/uactor/src/actor/abstract_actor.rs +++ b/src/uactor/src/actor/abstract_actor.rs @@ -1,11 +1,11 @@ use crate::actor::context::ActorContext; use crate::actor::message::Message; use std::future::Future; +use std::pin::Pin; use std::sync::Arc; pub trait State: std::any::Any + Send + 'static {} impl State for T {} -pub type ActorPreStartResult = Result>; use crate::dependency_injection::Inject; @@ -16,18 +16,24 @@ pub trait Actor: Sized + Unpin + 'static { type Inject: Inject + Sized; - type State: Default + Send + Sync; + type State: Default + Send + Sync + Clone; fn create_state(&mut self) -> Arc { Arc::new(Default::default()) } - fn pre_start( + fn on_start( &mut self, inject: &mut Self::Inject, ctx: &mut Self::Context, - ) -> impl Future> + Send { - async { Ok(()) } + ) -> impl Future + Send { + async { } + } + + fn on_error(&mut self, ctx: &mut Self::Context, error: HandleError) -> impl Future + Send { + async move { + tracing::error!("Actor error: {:?}", error); + } } } @@ -46,7 +52,9 @@ where ) -> impl Future + Send; } -pub type HandleResult = Result<(), Box>; +pub type HandleError = Box; + +pub type HandleResult = Result<(), HandleError>; #[cfg(not(feature = "async_sender"))] @@ -71,8 +79,4 @@ where &self, f: impl FnOnce(tokio::sync::oneshot::Sender) -> M, ) -> Result; -} - -pub trait NamedActorRef { - fn static_name() -> &'static str; } \ No newline at end of file diff --git a/src/uactor/src/actor/actor_ref.rs b/src/uactor/src/actor/actor_ref.rs index 782a021..8149fea 100644 --- a/src/uactor/src/actor/actor_ref.rs +++ b/src/uactor/src/actor/actor_ref.rs @@ -25,12 +25,6 @@ macro_rules! generate_actor_ref { $($Message($Message)),* } - impl ::uactor::actor::abstract_actor::NamedActorRef for [<$ActorType Msg>] { - fn static_name() -> &'static str { - stringify!([<$ActorType Msg>]) - } - } - impl uactor::actor::message::Message for [<$ActorType Msg>] { fn static_name() -> &'static str { stringify!([<$ActorType Msg>]) @@ -69,7 +63,7 @@ macro_rules! generate_actor_ref { pub struct [<$ActorType Ref>] where T: uactor::data::data_publisher::DataPublisher]> + Clone { name: std::sync::Arc, - state: std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>, + state: <$ActorType as uactor::actor::abstract_actor::Actor>::State, sender: T, } @@ -85,6 +79,25 @@ macro_rules! generate_actor_ref { } } + impl From<( + uactor::aliases::ActorName, + tokio::sync::mpsc::UnboundedSender<[<$ActorType Msg>]>, + <$ActorType as uactor::actor::abstract_actor::Actor>::State + )> for [<$ActorType Ref>]]>> + { + fn from((name, sender, state): ( + uactor::aliases::ActorName, + tokio::sync::mpsc::UnboundedSender<[<$ActorType Msg>]>, + <$ActorType as uactor::actor::abstract_actor::Actor>::State + )) -> Self { + Self { + name, + sender, + state, + } + } + } + $( #[cfg(not(feature = "async_sender"))] impl uactor::actor::abstract_actor::MessageSender<$Message> for [<$ActorType Ref>] where T: uactor::data::data_publisher::DataPublisher]> + Clone { @@ -116,7 +129,7 @@ macro_rules! generate_actor_ref { )* impl [<$ActorType Ref>] where T: uactor::data::data_publisher::DataPublisher]> + Clone { - pub fn new(name: std::sync::Arc, sender: T, state: std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>) -> Self { + pub fn new(name: std::sync::Arc, sender: T, state: <$ActorType as uactor::actor::abstract_actor::Actor>::State) -> Self { let name = std::sync::Arc::from(name); Self { name, sender, state } } diff --git a/src/uactor/src/actor/context.rs b/src/uactor/src/actor/context.rs index f11bc8b..713bf9b 100644 --- a/src/uactor/src/actor/context.rs +++ b/src/uactor/src/actor/context.rs @@ -399,13 +399,15 @@ pub mod extensions { pub mod actor_registry { use crate::actor::context::extensions::IdHasher; - use crate::data::data_publisher::{TryClone, TryCloneError}; + use crate::data::data_publisher::{DataPublisher, TryClone, TryCloneError}; use std::any::{Any, TypeId}; use std::collections::HashMap; use std::fmt; use std::hash::BuildHasherDefault; use std::ops::{Deref, DerefMut}; use std::sync::Arc; + use crate::actor::abstract_actor::Actor; + use crate::actor::message::Message; type AnyBoxed = Box; @@ -421,33 +423,47 @@ pub mod actor_registry { Self::default() } - // TODO: docs - pub fn insert( + pub fn register_ref( &mut self, actor_name: Arc, - val: T, - ) -> Option { - let entry = self.inner.entry(TypeId::of::()).or_default(); - entry.insert(actor_name, Box::new(val)).and_then(|boxed| { + channel: D, + state: A::State + ) -> Option + where + A: Actor, + M: Message, + D: DataPublisher + Send + Sync + 'static, + { + let entry = self.inner.entry(TypeId::of::()).or_default(); + entry.insert(actor_name, Box::new((channel, state))).and_then(|boxed| { (boxed as Box) .downcast() .ok() .map(|boxed| *boxed) }) } - // TODO: docs - pub fn get_all(&self) -> Option> { + pub fn get_all(&self) -> Option> + where + A: Actor, + M: Message, + D: DataPublisher + Send + Sync + 'static + { self.inner - .get(&TypeId::of::())? + .get(&TypeId::of::())? .values() .map(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref()) - .collect::>>() + .collect::>>() } // TODO: docs - pub fn get_actor(&self, actor_name: Arc) -> Option<&T> { - let boxed_actor_ref = self.inner.get(&TypeId::of::())?.get(&actor_name)?; + pub fn get_actor_ref(&self, actor_name: Arc) -> Option<&R> + where + T: Send + Sync + 'static, + R: 'static, + { + let group_by_type = self.inner.get(&TypeId::of::())?; + let boxed_actor_ref = group_by_type.get(&actor_name)?; (&**boxed_actor_ref as &(dyn Any + 'static)).downcast_ref() } diff --git a/src/uactor/src/actor/mod.rs b/src/uactor/src/actor/mod.rs index 7edefd4..d61f4bc 100644 --- a/src/uactor/src/actor/mod.rs +++ b/src/uactor/src/actor/mod.rs @@ -1,5 +1,4 @@ pub mod abstract_actor; -pub mod spawn; pub mod context; pub mod message; pub mod actor_ref; diff --git a/src/uactor/src/actor/spawn/macros.rs b/src/uactor/src/actor/spawn/macros.rs deleted file mode 100644 index 893a720..0000000 --- a/src/uactor/src/actor/spawn/macros.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::actor::message::Message; -use std::future::Future; - -#[macro_export] -macro_rules! spawn_with_ref { - ($S: ident, $ActorName: ident, $ActorInstance: ident: $ActorType: ident, $($Timeout: ident),*) => {{ - uactor::paste! { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); - let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some($ActorName.to_owned()), ($($Timeout,)* rx)); - let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); - $S.insert_actor(actor_ref.name(), uactor::data::data_publisher::TryClone::try_clone(&actor_ref)?); - (actor_ref, handle) - } - }}; - - ($S: ident, $ActorInstance: ident: $ActorType: ident, $($Timeout: ident),*) => {{ - let actor_name: std::sync::Arc = stringify!($ActorInstance).into(); - - uactor::paste! { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); - let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some(actor_name), ($($Timeout,)* rx)); - let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); - $S.insert_actor(actor_ref.name(), uactor::data::data_publisher::TryClone::try_clone(&actor_ref)?); - (actor_ref, handle) - } - }}; - - ($S: ident, $ActorName: ident, $ActorInstance: ident: $ActorType: ident) => {{ - let actor_name: std::sync::Arc = stringify!($ActorInstance).into(); - uactor::paste! { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); - let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some(actor_name), (rx)); - let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); - $S.insert_actor(actor_ref.name(), uactor::data::data_publisher::TryClone::try_clone(&actor_ref)?); - (actor_ref, handle) - } - }}; - - ($S: ident, $ActorInstance: ident: $ActorType: ident) => {{ - let actor_name: std::sync::Arc = stringify!($ActorInstance).into(); - uactor::paste! { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<[<$ActorType Msg>]>(); - let (name, shared_state, handle): (std::sync::Arc, std::sync::Arc<<$ActorType as uactor::actor::abstract_actor::Actor>::State>, tokio::task::JoinHandle<()>) = $S.init_actor($ActorInstance, Some(actor_name), (rx)); - let actor_ref = [<$ActorType Ref>]::new(name, tx, shared_state); - $S.insert_actor(actor_ref.name(), uactor::data::data_publisher::TryClone::try_clone(&actor_ref)?); - (actor_ref, handle) - } - }}; -} diff --git a/src/uactor/src/actor/spawn/mod.rs b/src/uactor/src/actor/spawn/mod.rs deleted file mode 100644 index f3c41c9..0000000 --- a/src/uactor/src/actor/spawn/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(feature = "enable_spawn_macros")] -pub mod macros; \ No newline at end of file diff --git a/src/uactor/src/aliases.rs b/src/uactor/src/aliases.rs new file mode 100644 index 0000000..4c772cd --- /dev/null +++ b/src/uactor/src/aliases.rs @@ -0,0 +1,3 @@ +use std::sync::Arc; + +pub type ActorName = Arc; \ No newline at end of file diff --git a/src/uactor/src/data/data_publisher.rs b/src/uactor/src/data/data_publisher.rs index f826ff5..910da95 100644 --- a/src/uactor/src/data/data_publisher.rs +++ b/src/uactor/src/data/data_publisher.rs @@ -146,7 +146,6 @@ pub use sync_sender::*; #[cfg(not(feature = "async_sender"))] mod sync_sender { - use tokio::sync::mpsc::UnboundedSender; use tokio::sync::{broadcast, mpsc, oneshot, watch}; pub trait DataPublisher: TryClone { @@ -201,7 +200,7 @@ mod sync_sender { } } - impl TryClone for UnboundedSender + impl TryClone for mpsc::UnboundedSender where T: Send, { diff --git a/src/uactor/src/dependency_injection/mod.rs b/src/uactor/src/dependency_injection/mod.rs index 2a88b5b..fe8dbe2 100644 --- a/src/uactor/src/dependency_injection/mod.rs +++ b/src/uactor/src/dependency_injection/mod.rs @@ -15,7 +15,7 @@ pub enum InjectError { /// Sample: /// ``` ///# use uactor::actor::context::extensions::Service; -/// use uactor::di::{Inject, InjectError}; +/// use uactor::dependency_injection::{Inject, InjectError}; ///# use uactor::system::System; /// /// pub struct References { @@ -41,12 +41,9 @@ pub trait Inject { } pub mod inject_impls { - use crate::actor::abstract_actor::NamedActorRef; - use crate::actor::context::extensions::Service; - use crate::data::data_publisher::TryClone; + use crate::actor::abstract_actor::Actor; use crate::dependency_injection::{Inject, InjectError}; use crate::system::System; - use std::sync::Arc; impl Inject for () { async fn inject(_: &System) -> Result diff --git a/src/uactor/src/lib.rs b/src/uactor/src/lib.rs index 7f99305..909f3d9 100644 --- a/src/uactor/src/lib.rs +++ b/src/uactor/src/lib.rs @@ -5,3 +5,4 @@ pub use paste::paste; pub mod data; pub mod actor; +pub mod aliases; diff --git a/src/uactor/src/system.rs b/src/uactor/src/system.rs index 26798ae..e9ac60c 100644 --- a/src/uactor/src/system.rs +++ b/src/uactor/src/system.rs @@ -2,15 +2,20 @@ use crate::actor::abstract_actor::Actor; use crate::actor::context::actor_registry::{ActorRegistry, ActorRegistryErrors}; use crate::actor::context::extensions::{ExtensionErrors, Extensions, Service}; use crate::actor::context::ActorContext; -use crate::data::data_publisher::{TryClone, TryCloneError}; +use crate::data::data_publisher::{DataPublisher, TryClone, TryCloneError}; use crate::dependency_injection::{Inject, InjectError}; use crate::actor::select::ActorSelect; use crate::system::builder::SystemBuilder; use std::any::Any; use std::collections::HashMap; +use std::pin::pin; use std::sync::Arc; +use tokio::sync::mpsc::UnboundedSender; use tokio::sync::oneshot; use tokio::task::JoinHandle; +use crate::actor; +use crate::actor::message::Message; +use crate::aliases::{ActorName}; #[derive(thiserror::Error, Debug)] pub enum ActorRunningError { @@ -32,6 +37,96 @@ pub struct System { actor_registry: ActorRegistry, } +impl System { + pub fn register_ref(&mut self, actor_name: &str) -> (R, tokio::sync::mpsc::UnboundedReceiver) + where + A: Actor, + M: Message + Send + 'static, + R: From<(ActorName, UnboundedSender, A::State)>, + { + let (mut tx, rx) = tokio::sync::mpsc::unbounded_channel::(); + + let actor_name: Arc = actor_name.to_owned().into(); + let state = A::State::default(); + let old_ref = self.actor_registry.register_ref::>(actor_name.clone(), tx.clone(), state.clone()); + if let Some(_old_ref) = old_ref { + tracing::warn!("The actor: {actor_name:?} has already been registered, old ref has been replaced"); + } + + (R::from((actor_name, tx, state)), rx) + } + + pub async fn spawn_actor( + &mut self, + actor_name: Arc, + mut actor: A, + state: A::State, + mut select: S, + ) -> Result<(::State, JoinHandle<()>), ActorRunningError> + where + A: Actor + Send, + S: ActorSelect + Send + 'static, + ::Inject: Inject + Sized + Send, + { + // let actor_name: Arc = actor_name.to_owned().into(); + + let system_name = self.name.clone(); + + let mut ctx = A::Context::create::(self, actor_name.clone()) + .await + .map_err(ActorRunningError::ContextError)?; + + let mut inject = A::Inject::inject(self).await?; + + let handle = { + let state = state.clone(); + tokio::spawn(async move { + tracing::debug!("The system: {:?} spawned actor: {:?}", system_name, actor_name); + + // call on_start + match ctx.on_start() { + Ok(_) => { + tracing::trace!("Starting the actor: {actor_name:?}"); + } + Err(err) => { + tracing::error!("Error during actor start: {err:?}"); + ctx.kill(); + } + } + + actor.on_start(&mut inject, &mut ctx).await; + + // main loop + while ctx.is_alive() { + tracing::trace!("iteration of the process: {actor_name:?}"); + let res = select.select(&mut inject, &mut ctx, &state, &mut actor).await; + ctx.after_iteration(); + + if let Err(err) = res { + tracing::error!("An error occurred while message handling by the \"{}\", error message: \"{}\"", ctx.get_name(), err); + + ctx.on_error(&err); + actor.on_error(&mut ctx, err).await; + } else { + tracing::trace!("{actor_name:?} successful iteration"); + } + } + // call on_die + match ctx.on_die(actor_name.clone()) { + Ok(_) => { + tracing::trace!("The actor: {actor_name:?} is dead"); + } + Err(err) => { + tracing::error!("Error during actor die: {err:?}"); + } + } + }) + }; + + Ok((state, handle)) + } +} + impl System {} impl System { @@ -52,13 +147,16 @@ impl System { Ok(service.clone()) } - pub fn get_actor(&self, actor_name: Arc) -> Result + pub fn get_actor(&self, actor_name: Arc) -> Result where - A: TryClone + Send + Sync + 'static, + A: Actor + Send + Sync + 'static, + M: Message, + D: DataPublisher + Send + Sync + 'static, + R: From<(ActorName, D, A::State)>, { - let actor_ref: &A = self + let (channel, state): &(D, A::State) = self .actor_registry - .get_actor::(actor_name.clone()) + .get_actor_ref::(actor_name.clone()) .ok_or_else(|| { let system_name = self.name.clone(); let kind = utils::type_name::(); @@ -69,41 +167,31 @@ impl System { actor_name, } })?; - let a = actor_ref.try_clone()?; - Ok(a) + + let reference = R::from((actor_name, channel.try_clone()?, state.clone())); + Ok(reference) } - pub fn get_actors(&self) -> Result, ActorRegistryErrors> + pub fn get_actors(&self) -> Result, ActorRegistryErrors> where - A: TryClone + Send + Sync + 'static, + A: Actor, + M: Message, + D: DataPublisher + Send + Sync + 'static { let actor_ref = self .actor_registry - .get_all() + .get_all::() .ok_or_else(|| { let system_name = self.name.clone(); let kind = std::any::type_name::().to_owned(); ActorRegistryErrors::NotRegisteredActorKind { system_name, kind } })? .into_iter() - .map(|i: &A| i.try_clone()) - .collect::, TryCloneError>>()?; + .map(|(c, state): &(D, A::State)| Ok((c.try_clone()?, state.clone()))) + .collect::, TryCloneError>>()?; Ok(actor_ref) } - pub fn insert_actor( - &mut self, - actor_name: Arc, - actor_ref: T, - ) { - tracing::info!( - "Insert actor: {actor_name:?}: {} into system context: {:?}", - std::any::type_name::(), - self.name - ); - self.actor_registry.insert::(actor_name, actor_ref); - } - pub fn insert_service(&mut self, data: T) { self.extensions.insert(Service(data)); } @@ -129,117 +217,6 @@ impl System { } } -impl System { - pub async fn run_actor(&mut self, actor_name: Arc) -> Result<(), ActorRunningError> - where - A: Actor + Any, - ::Inject: Inject + Sized + Send, - { - if let Some(tx) = self.initialized_actors.remove(&actor_name) { - let injected_dependencies = A::Inject::inject(self).await; - - let ctx = A::Context::create(self, actor_name.clone()) - .await - .map_err(ActorRunningError::ContextError)?; - - if let Err(err) = injected_dependencies.as_ref() { - tracing::error!( - "Can't inject dependencies for {actor_name:?}, actor not started. Err: {err:?}" - ) - } - let injected_dependencies = injected_dependencies?; - - if tx.send(Box::new((injected_dependencies, ctx))).is_err() { - return Err(ActorRunningError::Dropped(actor_name.clone())); - } - } else { - eprintln!("actor_name: {:?} already started", actor_name); - return Err(ActorRunningError::MissedInitializationOrAlreadyStarted( - actor_name.clone(), - )); - } - Ok(()) - } - - pub fn init_actor( - &mut self, - mut actor: A, - actor_name: Option>, - mut select: S, - ) -> (Arc, Arc, JoinHandle<()>) - where - A: Actor + Send, - S: ActorSelect + Send + 'static, - ::Inject: Inject + Sized + Send, - { - let system_name = self.name.clone(); - - let actor_name: Arc = actor_name.unwrap_or_else(|| { - let type_name = utils::type_name::(); - format!("{}-{}", type_name, (&type_name as *const String as i32)).into() - }); - let (actor_inject_tx, actor_inject_rx) = oneshot::channel::>(); - - self.initialized_actors.insert(actor_name.clone(), actor_inject_tx); - - let shared_state = actor.create_state(); - - let handle = { - let actor_name = actor_name.clone(); - let shared_state = shared_state.clone(); - - tokio::spawn(async move { - tracing::debug!("The system: {:?} spawned actor: {:?}", system_name, actor_name); - - if let Ok(boxed_state) = actor_inject_rx.await { - let (mut state, mut ctx) = { - let boxed_state = boxed_state - .downcast::<(::Inject, ::Context)>() - .expect("failed to downcast state"); - *boxed_state - }; - - // call on_start - match ctx.on_start() { - Ok(_) => { - tracing::trace!("Starting the actor: {actor_name:?}"); - } - Err(err) => { - tracing::error!("Error during actor start: {err:?}"); - ctx.kill(); - } - } - - // main loop - while ctx.is_alive() { - tracing::trace!("iteration of the process: {actor_name:?}"); - let res = select.select(&mut state, &mut ctx, &shared_state, &mut actor).await; - - if let Err(err) = res { - tracing::error!("An error occurred while message handling by the \"{}\", error message: \"{}\"", ctx.get_name(), err); - } else { - tracing::trace!("{actor_name:?} successful iteration"); - } - } - // call on_die - match ctx.on_die(actor_name.clone()) { - Ok(_) => { - tracing::trace!("The actor: {actor_name:?} is dead"); - } - Err(err) => { - tracing::error!("Error during actor die: {err:?}"); - } - } - } else { - tracing::error!("Can't run {actor_name:?}, system dropped"); - } - }) - }; - - (actor_name, shared_state, handle) - } -} - pub mod builder { use crate::actor::context::extensions::{Extensions, Service}; use crate::system::System; From 600d02c5b2e04b3f9fb3706614a3a9410020be29 Mon Sep 17 00:00:00 2001 From: EnvOut <31275761+EnvOut@users.noreply.github.com> Date: Sun, 24 Nov 2024 18:41:53 +0200 Subject: [PATCH 4/4] use Ref alias and code clean --- docs/assets/Lifecycle.png | Bin 0 -> 82338 bytes src/uactor/examples/dependency_injection.rs | 21 ++--- src/uactor/examples/interval.rs | 4 +- .../examples/multiple_incoming_channels.rs | 13 +-- src/uactor/examples/shared_state.rs | 17 ++-- src/uactor/examples/single_channel_actor.rs | 6 +- src/uactor/examples/supervised_actor.rs | 32 ++++--- src/uactor/src/actor/actor_ref.rs | 2 + src/uactor/src/actor/context.rs | 82 ++---------------- src/uactor/src/dependency_injection/mod.rs | 58 ------------- 10 files changed, 49 insertions(+), 186 deletions(-) create mode 100644 docs/assets/Lifecycle.png diff --git a/docs/assets/Lifecycle.png b/docs/assets/Lifecycle.png new file mode 100644 index 0000000000000000000000000000000000000000..4a5666aec2b7e73af2f5428495f54bfaa882d5ac GIT binary patch literal 82338 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 XS1q&AlVapzfPXKaE6Wx=)A#ug7Re5x literal 0 HcmV?d00001 diff --git a/src/uactor/examples/dependency_injection.rs b/src/uactor/examples/dependency_injection.rs index b3e4e7b..eb1f929 100644 --- a/src/uactor/examples/dependency_injection.rs +++ b/src/uactor/examples/dependency_injection.rs @@ -1,15 +1,10 @@ use time::ext::NumericalStdDuration; -use tokio::sync::mpsc::UnboundedSender; use uactor::actor::abstract_actor::MessageSender; use uactor::system::System; -use crate::actor1::Actor1; -use crate::actor1::Actor1Msg; -use crate::actor1::Actor1Ref; -use crate::actor2::Actor2; -use crate::actor2::Actor2Msg; -use crate::actor2::Actor2Ref; +use crate::actor1::{Actor1, Actor1MpscRef}; +use crate::actor2::{Actor2, Actor2MpscRef}; use crate::messages::{MessageWithoutReply, PingMsg, PongMsg}; use crate::services::{Service1, Service2}; @@ -31,8 +26,6 @@ mod messages { } mod actor1 { - use tokio::sync::mpsc::UnboundedSender; - use uactor::actor::abstract_actor::{Actor, HandleResult, Handler, MessageSender}; use uactor::actor::context::extensions::Service; use uactor::actor::context::Context; @@ -40,7 +33,7 @@ mod actor1 { use uactor::dependency_injection::{Inject, InjectError}; use uactor::system::System; - use crate::actor2::{Actor2, Actor2Msg, Actor2Ref}; + use crate::actor2::{Actor2, Actor2MpscRef}; use crate::messages::{MessageWithoutReply, PingMsg, PongMsg, PrintMessage}; use crate::services::Service1; @@ -49,7 +42,7 @@ mod actor1 { #[derive(derive_more::Constructor)] pub struct Services { service1: Service, - actor2_ref: Actor2Ref>, + actor2_ref: Actor2MpscRef, } impl Inject for Services { @@ -58,7 +51,7 @@ mod actor1 { Self: Sized, { let service1 = system.get_service()?; - let actor2_ref = system.get_actor::>("actor2".into())?; + let actor2_ref = system.get_actor::("actor2".into())?; Ok(Services::new(service1, actor2_ref)) } } @@ -208,10 +201,10 @@ async fn main() -> anyhow::Result<()> { .build(); // Init actor (instance + spawn actor) - let (actor1_ref, actor1_stream) = system.register_ref::>>("actor1"); + let (actor1_ref, actor1_stream) = system.register_ref::("actor1"); // Init actor2 (instance + spawn actor) - let (actor2_ref, actor2_stream) = system.register_ref::>>("actor2"); + let (actor2_ref, actor2_stream) = system.register_ref::("actor2"); // Run actors let actor1 = Actor1; diff --git a/src/uactor/examples/interval.rs b/src/uactor/examples/interval.rs index da94dca..c46c340 100644 --- a/src/uactor/examples/interval.rs +++ b/src/uactor/examples/interval.rs @@ -4,7 +4,7 @@ use uactor::actor::abstract_actor::MessageSender; use uactor::system::System; -use crate::actor1::Actor1; +use crate::actor1::{Actor1, Actor1MpscRef}; use crate::actor1::Actor1Msg; use crate::actor1::Actor1Ref; use crate::messages::{PingMsg, PongMsg}; @@ -83,7 +83,7 @@ async fn main() -> anyhow::Result<()> { // 1 second interval let interval = tokio::time::interval(1.std_seconds()); - let (actor1_ref, actor1_stream) = system.register_ref::>>("actor1"); + let (actor1_ref, actor1_stream) = system.register_ref::("actor1"); let actor1 = Actor1::default(); system.spawn_actor(actor1_ref.name(), actor1, *actor1_ref.state(), (actor1_stream, interval)).await?; diff --git a/src/uactor/examples/multiple_incoming_channels.rs b/src/uactor/examples/multiple_incoming_channels.rs index ae17fe6..c9e26b0 100644 --- a/src/uactor/examples/multiple_incoming_channels.rs +++ b/src/uactor/examples/multiple_incoming_channels.rs @@ -1,5 +1,5 @@ use std::time::Duration; - +use tokio::sync::mpsc; use tracing::level_filters::LevelFilter; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; @@ -35,13 +35,14 @@ pub mod messages { } pub mod actor1 { + use tokio::sync::mpsc; use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; use uactor::actor::context::Context; use crate::messages::{PingPongMsg, ReqMsg, RespMsg}; pub struct Actor1 { - pub resp_tx: tokio::sync::mpsc::Sender, + pub resp_tx: mpsc::Sender, } impl Actor for Actor1 { @@ -117,9 +118,9 @@ async fn main() -> anyhow::Result<()> { let mut system = System::global().build(); // Initialize channels - let (ping_tx, ping_rx) = tokio::sync::mpsc::channel::(10); - let (req_tx, req_rx) = tokio::sync::mpsc::channel::(10); - let (resp_tx, resp_rx) = tokio::sync::mpsc::channel::(10); + let (ping_tx, ping_rx) = mpsc::channel::(10); + let (req_tx, req_rx) = mpsc::channel::(10); + let (resp_tx, resp_rx) = mpsc::channel::(10); // Initialize actors let actor1 = Actor1 { resp_tx }; @@ -127,7 +128,7 @@ async fn main() -> anyhow::Result<()> { // Run actors let (_, handle1) = system.spawn_actor("actor1".into(), actor1, (), (ping_rx, req_rx)).await?; - let (_, handle2) = system.spawn_actor("actor2".into(), actor2, (), (resp_rx)).await?; + let (_, handle2) = system.spawn_actor("actor2".into(), actor2, (), resp_rx).await?; // Send messages ping_tx.send(PingPongMsg::Ping).await?; diff --git a/src/uactor/examples/shared_state.rs b/src/uactor/examples/shared_state.rs index cfd5d94..35a8a09 100644 --- a/src/uactor/examples/shared_state.rs +++ b/src/uactor/examples/shared_state.rs @@ -1,7 +1,6 @@ -use std::sync::Arc; use std::sync::atomic::{AtomicU8, Ordering}; +use std::sync::Arc; use std::time::Duration; -use tokio::sync::mpsc::UnboundedSender; use tracing::level_filters::LevelFilter; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; @@ -9,7 +8,7 @@ use uactor::actor::abstract_actor::{Actor, HandleResult, Handler, MessageSender} use uactor::actor::context::Context; use uactor::system::System; -use uactor::actor::message::{Message, Reply}; +use uactor::actor::message::Message; pub struct PingMsg; uactor::message_impl!(PingMsg); @@ -37,8 +36,8 @@ impl Handler for Actor1 { async fn handle( &mut self, _: &mut Self::Inject, - ping: PingMsg, - ctx: &mut Self::Context, + _ping: PingMsg, + _ctx: &mut Self::Context, state: &Self::State, ) -> HandleResult { state.counter.fetch_add(1, Ordering::Relaxed); @@ -57,14 +56,14 @@ async fn main() -> anyhow::Result<()> { let mut system = System::global().build(); - let (actor1_ref, actor1_stream) = system.register_ref::>>("actor1"); + let (actor1_ref, actor1_stream) = system.register_ref::("actor1"); let actor1 = Actor1; let (_, handle) = system.spawn_actor(actor1_ref.name(), actor1, actor1_ref.state().clone(), (actor1_stream)).await?; - let pong = actor1_ref.send(PingMsg); - let pong = actor1_ref.send(PingMsg); - let pong = actor1_ref.send(PingMsg); + actor1_ref.send(PingMsg)?; + actor1_ref.send(PingMsg)?; + actor1_ref.send(PingMsg)?; tokio::time::sleep(Duration::from_secs(1)).await; diff --git a/src/uactor/examples/single_channel_actor.rs b/src/uactor/examples/single_channel_actor.rs index 72712eb..4b4313c 100644 --- a/src/uactor/examples/single_channel_actor.rs +++ b/src/uactor/examples/single_channel_actor.rs @@ -3,7 +3,7 @@ use uactor::actor::abstract_actor::MessageSender; use uactor::aliases::ActorName; use uactor::system::System; -use crate::actor1::Actor1; +use crate::actor1::{Actor1, Actor1MpscRef}; use crate::actor1::Actor1Msg; use crate::actor1::Actor1Ref; use crate::messages::PingMsg; @@ -58,9 +58,9 @@ async fn main() -> anyhow::Result<()> { let mut system = System::global().build(); - let (actor1_ref, actor1_stream) = system.register_ref::>>("actor1"); + let (actor1_ref, actor1_stream) = system.register_ref::("actor1"); - system.spawn_actor(actor1_ref.name(), actor1, (actor1_stream)).await?; + system.spawn_actor(actor1_ref.name(), actor1, (), actor1_stream).await?; let pong = actor1_ref.ask(PingMsg).await?; println!("main: received {pong:?} message"); diff --git a/src/uactor/examples/supervised_actor.rs b/src/uactor/examples/supervised_actor.rs index e79139e..d0dc8e1 100644 --- a/src/uactor/examples/supervised_actor.rs +++ b/src/uactor/examples/supervised_actor.rs @@ -1,15 +1,16 @@ use std::time::Duration; +use tokio::sync::mpsc::UnboundedSender; use tracing::level_filters::LevelFilter; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; use uactor::actor::abstract_actor::MessageSender; use uactor::system::System; -use crate::actor1::Actor1; +use crate::actor1::{Actor1, Actor1MpscRef}; use crate::actor1::Actor1Msg; use crate::actor1::Actor1Ref; use crate::messages::PingMsg; -use crate::supervisor::{Supervisor, SupervisorMsg, SupervisorRef}; +use crate::supervisor::{Supervisor, SupervisorMpscRef, SupervisorMsg, SupervisorRef}; mod messages { use uactor::actor::message::{Message, Reply}; @@ -24,16 +25,13 @@ mod messages { mod actor1 { use crate::messages::{PingMsg, PongMsg}; - use crate::supervisor::{SupervisorMsg, SupervisorRef}; - use tokio::sync::mpsc; use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; - use uactor::actor::context::supervised::SupervisedContext; - use uactor::actor::context::ActorContext; + use uactor::actor::context::{ActorContext, Context}; pub struct Actor1; impl Actor for Actor1 { - type Context = SupervisedContext>>; + type Context = Context; type Inject = (); type State = (); } @@ -44,7 +42,7 @@ mod actor1 { _: &mut Self::Inject, ping: PingMsg, ctx: &mut Self::Context, - state: &Self::State, + _state: &Self::State, ) -> HandleResult { println!("actor1: Received ping message"); let PingMsg(reply) = ping; @@ -58,7 +56,6 @@ mod actor1 { } mod supervisor { - use std::os::macos::raw::stat; use uactor::actor::abstract_actor::{Actor, HandleResult, Handler}; use uactor::actor::context::{ActorDied, Context}; @@ -95,17 +92,18 @@ async fn main() -> anyhow::Result<()> { let mut system = System::global().build(); - let actor1 = Actor1; - let supervisor = Supervisor; + let (supervisor_ref, supervisor_stream) = system.register_ref::("supervisor"); + let (actor1_ref, actor1_stream) = system.register_ref::("actor1"); - let (actor1_ref, _) = uactor::spawn_with_ref!(system, actor1: Actor1); - let (supervisor_ref, _) = uactor::spawn_with_ref!(system, supervisor: Supervisor); + // Run supervisor + let supervisor = Supervisor; + system.spawn_actor(supervisor_ref.name(), supervisor, (), supervisor_stream).await?; - system - .run_actor::(supervisor_ref.name()) - .await?; - system.run_actor::(actor1_ref.name()).await?; + // Run actor1 + let actor1 = Actor1; + system.spawn_actor(actor1_ref.name(), actor1, (), actor1_stream).await?; + // ask actor1 to send a pong message let pong = actor1_ref.ask(PingMsg).await?; println!("main: received {pong:?} message"); diff --git a/src/uactor/src/actor/actor_ref.rs b/src/uactor/src/actor/actor_ref.rs index 8149fea..5c69806 100644 --- a/src/uactor/src/actor/actor_ref.rs +++ b/src/uactor/src/actor/actor_ref.rs @@ -61,6 +61,8 @@ macro_rules! generate_actor_ref { } } + pub type [<$ActorType MpscRef>] = [<$ActorType Ref>]]>>; + pub struct [<$ActorType Ref>] where T: uactor::data::data_publisher::DataPublisher]> + Clone { name: std::sync::Arc, state: <$ActorType as uactor::actor::abstract_actor::Actor>::State, diff --git a/src/uactor/src/actor/context.rs b/src/uactor/src/actor/context.rs index 713bf9b..6fb11bd 100644 --- a/src/uactor/src/actor/context.rs +++ b/src/uactor/src/actor/context.rs @@ -2,6 +2,7 @@ use crate::actor::message::Message; use crate::system::System; use std::future::Future; use std::sync::Arc; +use crate::actor::abstract_actor::{Actor, HandleError}; pub type ContextResult = Result>; pub type ContextInitializationError = Result; @@ -16,20 +17,16 @@ pub trait ActorContext: Sized + Unpin + 'static { Ok(()) } #[inline] - fn on_iteration(&mut self) -> ContextResult<()> { - Ok(()) - } + fn after_iteration(&mut self) -> () { } #[inline] - fn on_error(&mut self) -> ContextResult<()> { - Ok(()) - } + fn on_error(&mut self, _error: &HandleError) -> () { } fn kill(&mut self); fn get_name(&self) -> &str; #[allow(clippy::wrong_self_convention)] fn is_alive(&self) -> bool { true } - fn create( + fn create( system: &mut System, name: Arc, ) -> impl Future> + Send; @@ -62,80 +59,11 @@ impl ActorContext for Context { self.alive } - async fn create(_: &mut System, name: Arc) -> ContextInitializationError { + async fn create(_: &mut System, name: Arc) -> ContextInitializationError { Ok(Context { alive: true, name }) } } -pub mod supervised { - use crate::actor::abstract_actor::MessageSender; - use crate::actor::context::{ActorContext, ActorDied, ContextInitializationError, ContextResult}; - use crate::data::data_publisher::TryClone; - use crate::system::{utils, System}; - use std::sync::Arc; - - #[derive(derive_more::Constructor)] - pub struct SupervisedContext - where - T: MessageSender, - { - pub alive: bool, - _id: usize, - supervisor: T, - name: Arc, - } - - impl ActorContext for SupervisedContext - where - T: MessageSender + Unpin + 'static + TryClone + Send + Sync, - { - fn on_die(&mut self, actor_name: Arc) -> ContextResult<()> { - if let Err(e) = self.supervisor.send(ActorDied(actor_name)) { - tracing::error!("Failed to notify supervisor about actor death: {:?}", e); - } - Ok(()) - } - - fn kill(&mut self) { - self.alive = false; - } - - fn get_name(&self) -> &str { - &self.name - } - - fn is_alive(&self) -> bool { - self.alive - } - - async fn create(system: &mut System, name: Arc) -> ContextInitializationError { - let mut found_actors: Vec = system.get_actors::().map_err(|e| e.to_string())?; - let is_more_one = found_actors.len() > 1; - - if is_more_one { - let msg = format!("SupervisedContext can't be used with more than one actor: {:?} of the same kind", utils::type_name::()); - tracing::error!(msg); - return Err(msg); - } else if found_actors.is_empty() { - let msg = format!( - "SupervisedContext can't be used without selected supervisor's actor: {:?}", - utils::type_name::() - ); - tracing::error!(msg); - return Err(msg); - } - - let supervisor = found_actors.remove(0); - Ok(Self { - alive: true, - _id: rand::random(), - supervisor, - name, - }) - } - } -} - pub mod extensions { use std::any::{Any, TypeId}; use std::collections::HashMap; diff --git a/src/uactor/src/dependency_injection/mod.rs b/src/uactor/src/dependency_injection/mod.rs index fe8dbe2..99eb88f 100644 --- a/src/uactor/src/dependency_injection/mod.rs +++ b/src/uactor/src/dependency_injection/mod.rs @@ -53,62 +53,4 @@ pub mod inject_impls { Ok(()) } } - - impl Inject for T1 - where - T1: DependencyProvider, - { - async fn inject(system: &System) -> Result - where - Self: Sized, - { - let result = T1::get_dependency(system)?; - Ok(result) - } - } - - impl Inject for (T1, T2) - where - T1: DependencyProvider, - T2: DependencyProvider, - { - async fn inject(system: &System) -> Result - where - Self: Sized, - { - let t1 = T1::get_dependency(system)?; - let t2 = T2::get_dependency(system)?; - Ok((t1, t2)) - } - } - - pub trait DependencyProvider { - type Dependency; - fn get_dependency(system: &System) -> Result; - } - - impl DependencyProvider for Service - where - T: Clone + Send + Sync + 'static, - { - type Dependency = Service; - - fn get_dependency(system: &System) -> Result { - let service = system.get_service()?; - Ok(service) - } - } - - impl DependencyProvider for T - where - T: NamedActorRef + TryClone + Clone + Send + Sync + 'static, - { - type Dependency = Self; - - fn get_dependency(system: &System) -> Result { - let actor_name = Self::static_name(); - let actor = system.get_actor(Arc::from(actor_name))?; - Ok(actor) - } - } }