1use super::{Runtime, RuntimeError};
2use crate::agent::constants::DEFAULT_CHANNEL_BUFFER;
3use crate::utils::{BoxEventStream, receiver_into_stream};
4use crate::{
5 actor::{AnyActor, Transport},
6 error::Error,
7};
8use async_trait::async_trait;
9use autoagents_protocol::{Event, InternalEvent, RuntimeID};
10use futures_util::StreamExt;
11use log::{debug, error, info, warn};
12use std::{
13 any::{Any, TypeId},
14 collections::HashMap,
15 sync::{
16 Arc,
17 atomic::{AtomicBool, Ordering},
18 },
19};
20use tokio::sync::{Mutex, Notify, RwLock, broadcast, mpsc};
21use tokio_stream::wrappers::{BroadcastStream, errors::BroadcastStreamRecvError};
22use uuid::Uuid;
23
24const DEFAULT_INTERNAL_BUFFER: usize = 1000;
25
26#[derive(Debug)]
28struct Subscription {
29 topic_type: TypeId,
30 actors: Vec<Arc<dyn AnyActor>>,
31}
32
33#[derive(Debug)]
34pub struct SingleThreadedRuntime {
36 pub id: RuntimeID,
37 external_tx: mpsc::Sender<Event>,
39 external_rx: Mutex<Option<mpsc::Receiver<Event>>>,
40 broadcast_tx: broadcast::Sender<Event>,
42 internal_tx: mpsc::Sender<InternalEvent>,
44 internal_rx: Mutex<Option<mpsc::Receiver<InternalEvent>>>,
45 subscriptions: Arc<RwLock<HashMap<String, Subscription>>>,
47 transport: Arc<dyn Transport>,
49 shutdown_flag: Arc<AtomicBool>,
51 shutdown_notify: Arc<Notify>,
52}
53
54impl SingleThreadedRuntime {
55 pub fn new(channel_buffer: Option<usize>) -> Arc<Self> {
56 Self::with_transport(channel_buffer, Arc::new(crate::actor::LocalTransport))
57 }
58
59 pub fn with_transport(
60 channel_buffer: Option<usize>,
61 transport: Arc<dyn Transport>,
62 ) -> Arc<Self> {
63 let id = Uuid::new_v4();
64 let buffer_size = channel_buffer.unwrap_or(DEFAULT_CHANNEL_BUFFER);
65
66 let (external_tx, external_rx) = mpsc::channel(buffer_size);
68 let (internal_tx, internal_rx) = mpsc::channel(DEFAULT_INTERNAL_BUFFER);
69 let (broadcast_tx, _) = broadcast::channel(buffer_size);
70
71 Arc::new(Self {
72 id,
73 external_tx,
74 external_rx: Mutex::new(Some(external_rx)),
75 broadcast_tx,
76 internal_tx,
77 internal_rx: Mutex::new(Some(internal_rx)),
78 subscriptions: Arc::new(RwLock::new(HashMap::new())),
79 transport,
80 shutdown_flag: Arc::new(AtomicBool::new(false)),
81 shutdown_notify: Arc::new(Notify::new()),
82 })
83 }
84
85 async fn process_internal_event(&self, event: InternalEvent) -> Result<(), Error> {
87 debug!("Received internal event: {event:?}");
88 match event {
89 InternalEvent::ProtocolEvent(event) => {
90 self.process_protocol_event(*event).await?;
91 }
92 InternalEvent::Shutdown => {
93 self.shutdown_flag.store(true, Ordering::SeqCst);
94 self.shutdown_notify.notify_waiters();
95 }
96 }
97 Ok(())
98 }
99
100 async fn process_protocol_event(&self, event: Event) -> Result<(), Error> {
102 if let Event::PublishMessage {
103 topic_type,
104 topic_name,
105 message,
106 } = event
107 {
108 self.handle_publish_message(&topic_name, topic_type, message)
109 .await?;
110 } else {
111 let _ = self.broadcast_tx.send(event.clone());
113 self.external_tx
114 .send(event)
115 .await
116 .map_err(|e| RuntimeError::EventError(Box::new(e)))?;
117 }
118 Ok(())
119 }
120
121 async fn handle_publish_message(
123 &self,
124 topic_name: &str,
125 topic_type: TypeId,
126 message: Arc<dyn Any + Send + Sync>,
127 ) -> Result<(), RuntimeError> {
128 debug!("Handling publish event: {topic_name}");
129
130 let subscriptions = self.subscriptions.read().await;
131
132 if let Some(subscription) = subscriptions.get(topic_name) {
133 if subscription.topic_type != topic_type {
135 error!(
136 "Type mismatch for topic '{}': expected {:?}, got {:?}",
137 topic_name, subscription.topic_type, topic_type
138 );
139 return Err(RuntimeError::TopicTypeMismatch(
140 topic_name.to_owned(),
141 topic_type,
142 ));
143 }
144
145 for actor in &subscription.actors {
147 if let Err(e) = self
148 .transport
149 .send(actor.as_ref(), Arc::clone(&message))
150 .await
151 {
152 error!("Failed to send message to subscriber: {e}");
153 }
154 }
155 } else {
156 debug!("No subscribers for topic: {topic_name}");
157 }
158
159 Ok(())
160 }
161
162 async fn handle_subscribe(
164 &self,
165 topic_name: &str,
166 topic_type: TypeId,
167 actor: Arc<dyn AnyActor>,
168 ) -> Result<(), RuntimeError> {
169 info!("Actor subscribing to topic: {topic_name}");
170
171 let mut subscriptions = self.subscriptions.write().await;
172
173 match subscriptions.get_mut(topic_name) {
174 Some(subscription) => {
175 if subscription.topic_type != topic_type {
177 return Err(RuntimeError::TopicTypeMismatch(
178 topic_name.to_string(),
179 subscription.topic_type,
180 ));
181 }
182 subscription.actors.push(actor);
183 }
184 None => {
185 subscriptions.insert(
187 topic_name.to_string(),
188 Subscription {
189 topic_type,
190 actors: vec![actor],
191 },
192 );
193 }
194 }
195
196 Ok(())
197 }
198
199 async fn event_loop(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
201 let mut internal_rx = self
202 .internal_rx
203 .lock()
204 .await
205 .take()
206 .ok_or("Internal receiver already taken")?;
207
208 info!("Runtime event loop starting");
209
210 loop {
211 tokio::select! {
212 Some(event) = internal_rx.recv() => {
214 debug!("Processing internal event");
215
216 if matches!(event, InternalEvent::Shutdown) {
218 info!("Received shutdown event");
219 self.process_internal_event(event).await?;
220 break;
221 }
222
223 if let Err(e) = self.process_internal_event(event).await {
224 error!("Error processing internal event: {e}");
225 break;
226 }
227 }
228 _ = self.shutdown_notify.notified() => {
230 if self.shutdown_flag.load(Ordering::SeqCst) {
231 info!("Runtime received shutdown notification");
232 break;
233 }
234 }
235 else => {
237 warn!("Internal event channel closed");
238 break;
239 }
240 }
241 }
242
243 info!("Draining remaining events before shutdown");
245 while let Ok(event) = internal_rx.try_recv() {
246 if let Err(e) = self.process_internal_event(event).await {
247 error!("Error processing event during shutdown: {e}");
248 }
249 }
250
251 info!("Runtime event loop stopped");
252 Ok(())
253 }
254}
255
256#[async_trait]
257impl Runtime for SingleThreadedRuntime {
258 fn id(&self) -> RuntimeID {
259 self.id
260 }
261
262 async fn subscribe_any(
263 &self,
264 topic_name: &str,
265 topic_type: TypeId,
266 actor: Arc<dyn AnyActor>,
267 ) -> Result<(), RuntimeError> {
268 self.handle_subscribe(topic_name, topic_type, actor).await
269 }
270
271 async fn publish_any(
272 &self,
273 topic_name: &str,
274 topic_type: TypeId,
275 message: Arc<dyn Any + Send + Sync>,
276 ) -> Result<(), RuntimeError> {
277 self.handle_publish_message(topic_name, topic_type, message)
278 .await
279 }
280
281 fn tx(&self) -> mpsc::Sender<Event> {
282 let internal_tx = self.internal_tx.clone();
284 let (interceptor_tx, mut interceptor_rx) = mpsc::channel::<Event>(DEFAULT_CHANNEL_BUFFER);
285
286 tokio::spawn(async move {
287 while let Some(event) = interceptor_rx.recv().await {
288 if let Err(e) = internal_tx
289 .send(InternalEvent::ProtocolEvent(Box::new(event)))
290 .await
291 {
292 error!("Failed to forward event to internal channel: {e}");
293 break;
294 }
295 }
296 });
297
298 interceptor_tx
299 }
300
301 async fn transport(&self) -> Arc<dyn Transport> {
302 Arc::clone(&self.transport)
303 }
304
305 async fn take_event_receiver(&self) -> Option<BoxEventStream<Event>> {
306 let mut guard = self.external_rx.lock().await;
307 guard.take().map(receiver_into_stream)
308 }
309
310 async fn subscribe_events(&self) -> BoxEventStream<Event> {
311 let rx = self.broadcast_tx.subscribe();
312 let stream = BroadcastStream::new(rx)
313 .filter_map(|item: Result<Event, BroadcastStreamRecvError>| async move { item.ok() });
314 Box::pin(stream)
315 }
316
317 async fn run(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
318 info!("Starting SingleThreadedRuntime {}", self.id);
319 self.event_loop().await
320 }
321
322 async fn stop(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
323 if self.shutdown_flag.load(Ordering::SeqCst) {
324 return Ok(());
325 }
326
327 info!("Initiating runtime shutdown for {}", self.id);
328
329 if let Err(e) = self.internal_tx.send(InternalEvent::Shutdown).await {
331 if self.shutdown_flag.load(Ordering::SeqCst) {
332 return Ok(());
333 }
334 return Err(format!("Failed to send shutdown signal: {e}").into());
335 }
336
337 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
339
340 Ok(())
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347 use crate::actor::{CloneableMessage, Topic};
348 use crate::runtime::{RuntimeConfig, TypedRuntime};
349 use ractor::{Actor, ActorProcessingErr, ActorRef};
350 use tokio::time::{Duration, sleep};
351
352 #[derive(Clone, Debug)]
354 struct TestMessage {
355 content: String,
356 }
357
358 impl crate::actor::ActorMessage for TestMessage {}
359 impl CloneableMessage for TestMessage {}
360
361 struct TestActor {
363 received: Arc<Mutex<Vec<String>>>,
364 }
365
366 #[async_trait]
367 impl Actor for TestActor {
368 type Msg = TestMessage;
369 type State = ();
370 type Arguments = Arc<Mutex<Vec<String>>>;
371
372 async fn pre_start(
373 &self,
374 _myself: ActorRef<Self::Msg>,
375 _args: Self::Arguments,
376 ) -> Result<Self::State, ActorProcessingErr> {
377 Ok(())
378 }
379
380 async fn handle(
381 &self,
382 _myself: ActorRef<Self::Msg>,
383 message: Self::Msg,
384 _state: &mut Self::State,
385 ) -> Result<(), ActorProcessingErr> {
386 let mut received = self.received.lock().await;
387 received.push(message.content);
388 Ok(())
389 }
390 }
391
392 #[tokio::test]
393 async fn test_runtime_creation() {
394 let runtime = SingleThreadedRuntime::new(None);
395 assert_ne!(runtime.id(), Uuid::nil());
396 }
397
398 #[tokio::test]
399 async fn test_publish_subscribe_cloneable() {
400 let runtime = SingleThreadedRuntime::new(Some(10));
401 let runtime_handle = runtime.clone();
402
403 let runtime_task = tokio::spawn(async move { runtime_handle.run().await });
405
406 let received = Arc::new(Mutex::new(Vec::new()));
408 let (actor_ref, _actor_handle) = Actor::spawn(
409 None,
410 TestActor {
411 received: received.clone(),
412 },
413 received.clone(),
414 )
415 .await
416 .unwrap();
417
418 let topic = Topic::<TestMessage>::new("test_topic");
420 runtime.subscribe(&topic, actor_ref).await.unwrap();
421
422 runtime
424 .publish(
425 &topic,
426 TestMessage {
427 content: "Hello".to_string(),
428 },
429 )
430 .await
431 .unwrap();
432
433 runtime
434 .publish(
435 &topic,
436 TestMessage {
437 content: "World".to_string(),
438 },
439 )
440 .await
441 .unwrap();
442
443 sleep(Duration::from_millis(100)).await;
445
446 let received_msgs = received.lock().await;
448 assert_eq!(received_msgs.len(), 2);
449 assert_eq!(received_msgs[0], "Hello");
450 assert_eq!(received_msgs[1], "World");
451
452 runtime.stop().await.unwrap();
454 runtime_task.abort();
455 }
456
457 #[tokio::test]
458 async fn test_type_safety() {
459 let runtime = SingleThreadedRuntime::new(None);
460 let runtime_handle = runtime.clone();
461
462 let runtime_task = tokio::spawn(async move { runtime_handle.run().await });
464
465 let topic_name = "typed_topic";
467 let topic1 = Topic::<TestMessage>::new(topic_name);
468
469 let received = Arc::new(Mutex::new(Vec::new()));
470 let (actor_ref, _) = Actor::spawn(
471 None,
472 TestActor {
473 received: received.clone(),
474 },
475 received.clone(),
476 )
477 .await
478 .unwrap();
479
480 runtime.subscribe(&topic1, actor_ref).await.unwrap();
481
482 sleep(Duration::from_millis(50)).await;
484
485 #[derive(Clone)]
487 struct OtherMessage;
488 impl crate::actor::ActorMessage for OtherMessage {}
489 impl CloneableMessage for OtherMessage {}
490
491 let topic2 = Topic::<OtherMessage>::new(topic_name);
492
493 struct OtherActor;
494 #[async_trait]
495 impl Actor for OtherActor {
496 type Msg = OtherMessage;
497 type State = ();
498 type Arguments = ();
499
500 async fn pre_start(
501 &self,
502 _myself: ActorRef<Self::Msg>,
503 _args: Self::Arguments,
504 ) -> Result<Self::State, ActorProcessingErr> {
505 Ok(())
506 }
507
508 async fn handle(
509 &self,
510 _myself: ActorRef<Self::Msg>,
511 _message: Self::Msg,
512 _state: &mut Self::State,
513 ) -> Result<(), ActorProcessingErr> {
514 Ok(())
515 }
516 }
517
518 let (other_ref, _) = Actor::spawn(None, OtherActor, ()).await.unwrap();
519
520 let result = runtime.subscribe(&topic2, other_ref).await;
522
523 assert!(result.is_err());
525
526 if let Err(RuntimeError::TopicTypeMismatch(topic, _)) = result {
528 assert_eq!(topic, topic_name);
529 } else {
530 panic!("Expected TopicTypeMismatch error");
531 }
532
533 runtime.stop().await.unwrap();
535 runtime_task.abort();
536 }
537
538 #[tokio::test]
539 async fn test_message_ordering() {
540 let runtime = SingleThreadedRuntime::new(Some(10));
541 let runtime_handle = runtime.clone();
542
543 let runtime_task = tokio::spawn(async move { runtime_handle.run().await });
545
546 let received = Arc::new(Mutex::new(Vec::new()));
548 let (actor_ref, _actor_handle) = Actor::spawn(
549 None,
550 TestActor {
551 received: received.clone(),
552 },
553 received.clone(),
554 )
555 .await
556 .unwrap();
557
558 let topic = Topic::<TestMessage>::new("order_test");
560 runtime.subscribe(&topic, actor_ref).await.unwrap();
561
562 for i in 0..10 {
564 runtime
565 .publish(
566 &topic,
567 TestMessage {
568 content: format!("Message {i}"),
569 },
570 )
571 .await
572 .unwrap();
573 }
574
575 sleep(Duration::from_millis(200)).await;
577
578 let received_msgs = received.lock().await;
580 assert_eq!(received_msgs.len(), 10);
581
582 for (i, msg) in received_msgs.iter().enumerate() {
583 assert_eq!(msg, &format!("Message {i}"));
584 }
585
586 runtime.stop().await.unwrap();
588 runtime_task.abort();
589 }
590
591 #[tokio::test]
592 async fn test_runtime_multiple_topics() {
593 let runtime = SingleThreadedRuntime::new(Some(10));
594 let runtime_handle = runtime.clone();
595
596 let runtime_task = tokio::spawn(async move { runtime_handle.run().await });
598
599 let topic1 = Topic::<TestMessage>::new("topic1");
601 let topic2 = Topic::<TestMessage>::new("topic2");
602
603 let received1 = Arc::new(Mutex::new(Vec::new()));
604 let received2 = Arc::new(Mutex::new(Vec::new()));
605
606 let (actor_ref1, _) = Actor::spawn(
607 None,
608 TestActor {
609 received: received1.clone(),
610 },
611 received1.clone(),
612 )
613 .await
614 .unwrap();
615
616 let (actor_ref2, _) = Actor::spawn(
617 None,
618 TestActor {
619 received: received2.clone(),
620 },
621 received2.clone(),
622 )
623 .await
624 .unwrap();
625
626 runtime.subscribe(&topic1, actor_ref1).await.unwrap();
628 runtime.subscribe(&topic2, actor_ref2).await.unwrap();
629 sleep(Duration::from_millis(50)).await;
630
631 let message1 = TestMessage {
633 content: "topic1_message".to_string(),
634 };
635 runtime.publish(&topic1, message1).await.unwrap();
636 sleep(Duration::from_millis(50)).await;
637
638 let message2 = TestMessage {
640 content: "topic2_message".to_string(),
641 };
642 runtime.publish(&topic2, message2).await.unwrap();
643 sleep(Duration::from_millis(50)).await;
644
645 let received_msgs1 = received1.lock().await;
647 let received_msgs2 = received2.lock().await;
648
649 assert_eq!(received_msgs1.len(), 1);
650 assert_eq!(received_msgs1[0], "topic1_message");
651
652 assert_eq!(received_msgs2.len(), 1);
653 assert_eq!(received_msgs2[0], "topic2_message");
654
655 runtime.stop().await.unwrap();
657 runtime_task.abort();
658 }
659
660 #[tokio::test]
661 async fn test_runtime_subscribe_multiple_actors_same_topic() {
662 let runtime = SingleThreadedRuntime::new(Some(10));
663 let runtime_handle = runtime.clone();
664
665 let runtime_task = tokio::spawn(async move { runtime_handle.run().await });
667
668 let topic = Topic::<TestMessage>::new("shared_topic");
669
670 let received1 = Arc::new(Mutex::new(Vec::new()));
671 let received2 = Arc::new(Mutex::new(Vec::new()));
672
673 let (actor_ref1, _) = Actor::spawn(
674 None,
675 TestActor {
676 received: received1.clone(),
677 },
678 received1.clone(),
679 )
680 .await
681 .unwrap();
682
683 let (actor_ref2, _) = Actor::spawn(
684 None,
685 TestActor {
686 received: received2.clone(),
687 },
688 received2.clone(),
689 )
690 .await
691 .unwrap();
692
693 runtime.subscribe(&topic, actor_ref1).await.unwrap();
695 runtime.subscribe(&topic, actor_ref2).await.unwrap();
696 sleep(Duration::from_millis(50)).await;
697
698 let message = TestMessage {
700 content: "broadcast_message".to_string(),
701 };
702 runtime.publish(&topic, message).await.unwrap();
703 sleep(Duration::from_millis(100)).await;
704
705 let received_msgs1 = received1.lock().await;
707 let received_msgs2 = received2.lock().await;
708
709 assert_eq!(received_msgs1.len(), 1);
710 assert_eq!(received_msgs1[0], "broadcast_message");
711
712 assert_eq!(received_msgs2.len(), 1);
713 assert_eq!(received_msgs2[0], "broadcast_message");
714
715 runtime.stop().await.unwrap();
717 runtime_task.abort();
718 }
719
720 #[test]
721 fn test_runtime_config_creation() {
722 let config = RuntimeConfig {
723 queue_size: Some(100),
724 };
725 assert_eq!(config.queue_size, Some(100));
726 }
727
728 #[test]
729 fn test_runtime_id_generation() {
730 let runtime1 = SingleThreadedRuntime::new(None);
731 let runtime2 = SingleThreadedRuntime::new(None);
732
733 assert_ne!(runtime1.id(), runtime2.id());
734 }
735}