stash: rewrite wip

We have a basic ring buffer for dealing with concurrent message exchange. Currently
in the middle of reifying the traits, types and structs needed for the `hello.rs`
example of the simple `Hello, World!` greeter type actor.
This commit is contained in:
Zachery Aaron Shores-Chmielewski 2026-01-22 14:04:07 +07:00
parent 87fb908ba7
commit 08b241481f
6 changed files with 150 additions and 139 deletions

64
Cargo.lock generated
View file

@ -23,28 +23,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "bytes" name = "crossbeam-queue"
version = "1.11.0" version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "futures-core" name = "crossbeam-utils"
version = "0.3.31" version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
@ -69,8 +60,7 @@ name = "swactor"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"tokio", "crossbeam-queue",
"tokio-util",
] ]
[[package]] [[package]]
@ -84,40 +74,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "tokio"
version = "1.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
dependencies = [
"pin-project-lite",
"tokio-macros",
]
[[package]]
name = "tokio-macros"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-util"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.22" version = "1.0.22"

View file

@ -8,5 +8,4 @@ crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
bytemuck = { version = "1.24.0", features = ["derive"] } bytemuck = { version = "1.24.0", features = ["derive"] }
tokio = { version = "1.48.0", features = ["rt", "macros"] } crossbeam-queue = "0.3.12"
tokio-util = "0.7.17"

View file

@ -48,3 +48,31 @@ The router is the engine for message delivery. It runs on its own thread and pos
- Its own inbox: - Its own inbox:
The router possesses its own mpsc queue where references to messages are stored. The router will process this queue by dereferencing and writing The router possesses its own mpsc queue where references to messages are stored. The router will process this queue by dereferencing and writing
directly into the recipient's inbox buffer. directly into the recipient's inbox buffer.
### Misc
A means of providing an emergency overflow without adding much more code complexity. The mutex means
this will not be `no_std` however.
```rust
struct HybridChannel<T> {
// Start with lock-free ring buffer
ring: AtomicRingBuffer<T>,
// When full, spill into a Mutex<VecDeque<T>>
overflow: parking_lot::Mutex<VecDeque<T>>,
// Track overflow frequency to resize ring proactively
overflow_count: AtomicUsize,
}
impl<T> HybridChannel<T> {
fn push(&self, value: T) {
if self.ring.push(value).is_err() {
self.overflow.lock().push_back(value);
self.overflow_count.fetch_add(1, Relaxed);
// Optionally: if overflow_count > threshold, grow ring
}
}
}
```

View file

@ -1,63 +1,34 @@
use bytemuck::{Pod, Zeroable};
use swactor::{ActorAddress, ActorInterface, Message, Runtime};
// use std::sync::{Arc, atomic::AtomicBool}; #[repr(C)]
#[derive(Pod, Zeroable)]
struct Greeter {
pub num_greeted: usize,
}
// use swactor::error::*; #[derive(Clone)]
// use tokio::task::JoinHandle; struct GreetMessage {
name: String,
// struct ActorInbox { addr: ActorAddress,
// _guard: AtomicBool }
// } impl Message for GreetMessage {}
// struct GenericGreeter {
// _guard: Arc<AtomicBool>,
// inbox: Vec<GreeterMessage>,
// outbox: Vec<GreeterResponse>,
// }
// impl GenericGreeter {
// pub fn new() -> Self {
// Self {
// _guard: Arc::new(AtomicBool::new(false)),
// inbox: Vec::new(),
// outbox: Vec::new(),
// }
// }
// }
// pub enum GreeterMessage { impl ActorInterface<GreetMessage> for Greeter {
// Name(String), fn handle(&mut self, ctx: &Runtime, msg: GreetMessage) {
// } let res = GreetResponse(format!("Hello, {}!", msg.name));
if let Err(_) = ctx.send(res, msg.addr) {
// no error handling
}
}
}
// pub enum GreeterResponse { #[derive(Debug, Default, Clone)]
// Hello(String), struct GreetResponse(String);
// } impl Message for GreetResponse {}
// pub struct Greeter; fn main() {
let rt = Runtime::new();
}
// fn main() {
// let rt = tokio::runtime::Builder::new_current_thread()
// .build()
// .expect("failed to build runtime");
// let greet = GenericGreeter::spawn(&rt);
// let res = rt.block_on(async {greet.await}).expect("runtime error").expect("greeter error");
// println!("Success!");
// // let greeter = Greeter.spawn(&rt);
// // let response = rt
// // .block_on(async move {
// // greeter
// // .send(GreeterMessage::Name("world".to_string()))
// // .await
// // })
// // .expect("failed to get respose");
// // match response {
// // GreeterResponse::Hello(hello) => println!("{hello}"),
// // }
// }

View file

@ -1,39 +1,47 @@
// mod kimi; mod ring_buffer;
use bytemuck::{Pod, Zeroable};
use ring_buffer::{Receiver, Sender};
pub mod error; pub mod error;
use std::{ pub trait Message: 'static + Sized + Clone {}
marker::PhantomData, mem::MaybeUninit, sync::{Mutex, atomic::AtomicUsize, mpsc::TryRecvError}
};
use crate::error::{Error, Result, convert_err}; pub trait ActorInterface<M: Message>: Pod + Zeroable {
fn handle(&mut self, ctx: &Runtime, msg: M);
use std::sync::mpsc;
pub trait Message: 'static + Sized + Copy + Default {}
pub trait Handler<M: Message> {
fn handle(&mut self, msg: M);
} }
pub type ActorAddress = u64;
pub struct Actor<S, M, N>
where
M: Message,
use bytemuck::{Pod, Zeroable}; N: Message,
S: ActorInterface<M>,
#[repr(C)] {
#[derive(Copy, Clone, Pod, Zeroable)] inbox: Receiver<M>,
struct GreeterState { outbox: Sender<N>,
pub num_greeted: usize, state: S,
} }
enum GreetMessage { pub struct Runtime {
Name(String), router: (),
actor_queue: (),
} }
enum GreetResponse { impl Runtime {
Greeting(String), pub fn new() -> Self {
Self {
router: (),
actor_queue: (),
}
} }
type GreeterId = u64; pub fn send<M>(&self, msg: M, addr: ActorAddress) -> Result<(), M> {
Err(msg)
}
}
pub struct MessageRouter<M> {
inbox: Receiver<M>,
address_book: (),
}

49
src/ring_buffer.rs Normal file
View file

@ -0,0 +1,49 @@
use std::sync::Arc;
use crossbeam_queue::ArrayQueue;
/// The receiving end of a `crossbeam_queue::ArrayQueue`, a lock-free mpsc queue.
/// The queue is constructed by the `Receiver::new()` method.
/// Responsible for creating the `Sender` ends of itself.
///
/// Notably: The `Receiver` provides no guarentees that a sending end of the channel exists.
pub(crate) struct Receiver<T> {
queue: Arc<ArrayQueue<T>>,
}
impl<T> Receiver<T> {
/// Constructs a new `ArrayQueue` with given capacity.
///
/// # Panics
/// Will panic if capacity is passed as 0
pub fn new(capacity: usize) -> Self {
Self {
queue: Arc::new(ArrayQueue::new(capacity))
}
}
/// Attempt to retrieve a value from the queue. Returns `None` if empty
pub fn try_recv(&self) -> Option<T> {
self.queue.pop()
}
/// Construct a new `Sender` assosciated with this queue.
pub fn new_sender(&self) -> Sender<T> {
Sender { queue: self.queue.clone() }
}
}
/// The sending end of a `crossbeam_queue::ArrayQueue`, a lock free mpsc queue.
/// The queue is initialized via calling the corresponding `Receiver::<T>::new()` method,
/// and the sending end of the queue is constructed via calling `receiver.new_sender()`.
///
/// Notably: The `Sender` provides no guarentees that a receiving end of the channel exists.
pub(crate) struct Sender<T> {
queue: Arc<ArrayQueue<T>>,
}
impl<T> Sender<T> {
/// Attempt to push a value to the queue. Returns Err(value) if the queue is full.
pub fn try_send(&self, value: T) -> Result<(), T> {
self.queue.push(value)
}
}