Skip to main content

lion_core/state/
actor.rs

1// Copyright (C) 2026 HaiyangLi
2// SPDX-License-Identifier: AGPL-3.0-or-later
3//! Lion State Actor
4//!
5//! Corresponds to: Lion/State/Actor.lean
6//!
7//! Actor runtime for message-passing concurrency.
8
9use std::collections::VecDeque;
10
11use crate::types::{ActorId, MsgId, PluginId, SecurityLevel};
12
13/// Message queue (FIFO)
14///
15/// Corresponds to Lean: `abbrev Queue (a : Type) := List a`
16///
17/// Using VecDeque for O(1) push_back / pop_front.
18pub type Queue<T> = VecDeque<T>;
19
20/// Error type for actor operations
21#[derive(Debug)]
22pub enum ActorError {
23    /// Mailbox is full (capacity, current)
24    MailboxFull(usize, usize),
25    /// No message available
26    NoMessage,
27    /// Actor is blocked
28    Blocked(ActorId),
29}
30
31/// Opaque message payload
32///
33/// Corresponds to Lean: `opaque Data : Type`
34///
35/// In Rust, we use a Vec<u8> as the concrete representation.
36pub type Data = Vec<u8>;
37
38/// Message between actors
39///
40/// Corresponds to Lean: `structure Message`
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct Message {
43    /// Message identifier
44    ///
45    /// Corresponds to Lean: `id : MsgId`
46    pub(crate) id: MsgId,
47
48    /// Source plugin
49    ///
50    /// Corresponds to Lean: `src : PluginId`
51    pub(crate) src: PluginId,
52
53    /// Destination plugin
54    ///
55    /// Corresponds to Lean: `dst : PluginId`
56    pub(crate) dst: PluginId,
57
58    /// Security level of the message
59    ///
60    /// Corresponds to Lean: `level : SecurityLevel`
61    pub(crate) level: SecurityLevel,
62
63    /// Message body
64    ///
65    /// Corresponds to Lean: `body : Data`
66    pub(crate) body: Data,
67}
68
69impl Message {
70    /// Create a new message
71    pub fn new(id: MsgId, src: PluginId, dst: PluginId, level: SecurityLevel, body: Data) -> Self {
72        Message {
73            id,
74            src,
75            dst,
76            level,
77            body,
78        }
79    }
80
81    /// Get the message ID
82    #[inline]
83    pub fn id(&self) -> MsgId {
84        self.id
85    }
86
87    /// Get the source plugin
88    #[inline]
89    pub fn src(&self) -> PluginId {
90        self.src
91    }
92
93    /// Get the destination plugin
94    #[inline]
95    pub fn dst(&self) -> PluginId {
96        self.dst
97    }
98
99    /// Get the security level
100    #[inline]
101    pub fn level(&self) -> SecurityLevel {
102        self.level
103    }
104
105    /// Get the message body
106    #[inline]
107    pub fn body(&self) -> &Data {
108        &self.body
109    }
110
111    /// Consume the message and return the body
112    pub fn into_body(self) -> Data {
113        self.body
114    }
115}
116
117/// Runtime state of an actor
118///
119/// Corresponds to Lean: `structure ActorRuntime`
120#[derive(Debug, Clone)]
121pub struct ActorRuntime {
122    /// Router queue (in transit)
123    ///
124    /// Corresponds to Lean: `pending : Queue Message`
125    pub(crate) pending: Queue<Message>,
126
127    /// Delivered, ready to process
128    ///
129    /// Corresponds to Lean: `mailbox : Queue Message`
130    pub(crate) mailbox: Queue<Message>,
131
132    /// Mailbox capacity (bounded)
133    ///
134    /// Corresponds to Lean: `capacity : Nat`
135    pub(crate) capacity: usize,
136
137    /// Wait-for graph edge (for deadlock analysis)
138    ///
139    /// Corresponds to Lean: `blockedOn : Option ActorId`
140    pub(crate) blocked_on: Option<ActorId>,
141
142    /// Message waiting to be delivered
143    ///
144    /// Corresponds to Lean: `pendingSend : Option Message`
145    pub(crate) pending_send: Option<Message>,
146}
147
148impl ActorRuntime {
149    /// Create empty actor runtime
150    ///
151    /// Corresponds to Lean: `def ActorRuntime.empty (capacity : Nat) : ActorRuntime`
152    pub fn empty(capacity: usize) -> Self {
153        ActorRuntime {
154            pending: VecDeque::new(),
155            mailbox: VecDeque::new(),
156            capacity,
157            blocked_on: None,
158            pending_send: None,
159        }
160    }
161
162    /// Check if actor can receive (mailbox not empty)
163    ///
164    /// Corresponds to Lean: `def ActorRuntime.can_receive (ar : ActorRuntime) : Bool`
165    #[inline]
166    pub fn can_receive(&self) -> bool {
167        !self.mailbox.is_empty()
168    }
169
170    /// Check if actor can send (pending queue not full)
171    ///
172    /// Corresponds to Lean: `def ActorRuntime.can_send (ar : ActorRuntime) (targetCapacity : Nat) : Bool`
173    #[inline]
174    pub fn can_send(&self, target_capacity: usize) -> bool {
175        self.pending.len() < target_capacity
176    }
177
178    /// Check if mailbox has space
179    ///
180    /// Corresponds to Lean: `def ActorRuntime.mailbox_has_space (ar : ActorRuntime) : Bool`
181    #[inline]
182    pub fn mailbox_has_space(&self) -> bool {
183        self.mailbox.len() < self.capacity
184    }
185
186    /// Enqueue message to pending
187    ///
188    /// Corresponds to Lean: `def ActorRuntime.enqueue_pending`
189    pub fn enqueue_pending(&self, msg: Message) -> Self {
190        let mut new_pending = self.pending.clone();
191        new_pending.push_back(msg);
192        ActorRuntime {
193            pending: new_pending,
194            mailbox: self.mailbox.clone(),
195            capacity: self.capacity,
196            blocked_on: self.blocked_on,
197            pending_send: self.pending_send.clone(),
198        }
199    }
200
201    /// Enqueue message to pending (mutating version)
202    pub fn enqueue_pending_mut(&mut self, msg: Message) {
203        self.pending.push_back(msg);
204    }
205
206    /// Deliver message from pending to mailbox
207    ///
208    /// Corresponds to Lean: `def ActorRuntime.deliver`
209    ///
210    /// Returns the new state. If pending is empty or mailbox is full,
211    /// returns unchanged state.
212    pub fn deliver(&self) -> Self {
213        // Check if pending is empty
214        if self.pending.is_empty() {
215            return self.clone();
216        }
217        // Check if mailbox is full
218        if self.mailbox.len() >= self.capacity {
219            return self.clone();
220        }
221
222        // Dequeue from front of pending, push to mailbox
223        let mut new_pending = self.pending.clone();
224        let first_msg = new_pending.pop_front().expect("checked non-empty");
225        let mut new_mailbox = self.mailbox.clone();
226        new_mailbox.push_back(first_msg);
227
228        ActorRuntime {
229            pending: new_pending,
230            mailbox: new_mailbox,
231            capacity: self.capacity,
232            blocked_on: self.blocked_on,
233            pending_send: self.pending_send.clone(),
234        }
235    }
236
237    /// Deliver message from pending to mailbox (mutating version)
238    ///
239    /// Returns Ok(true) if delivered, Ok(false) if mailbox full, Err if no pending.
240    ///
241    /// # Errors
242    ///
243    /// Returns `ActorError::NoMessage` if the pending queue is empty.
244    pub fn deliver_mut(&mut self) -> Result<bool, ActorError> {
245        if self.pending.is_empty() {
246            return Err(ActorError::NoMessage);
247        }
248
249        if self.mailbox.len() < self.capacity {
250            let msg = self.pending.pop_front().expect("checked non-empty");
251            self.mailbox.push_back(msg);
252            Ok(true)
253        } else {
254            // Mailbox full, don't modify
255            Ok(false)
256        }
257    }
258
259    /// Consume message from mailbox
260    ///
261    /// Corresponds to Lean: `def ActorRuntime.consume`
262    ///
263    /// Returns `(new_state, Option<Message>)`.
264    pub fn consume(&self) -> (Self, Option<Message>) {
265        if self.mailbox.is_empty() {
266            return (self.clone(), None);
267        }
268
269        // Get first message
270        let mut new_mailbox = self.mailbox.clone();
271        let msg = new_mailbox.pop_front().expect("checked non-empty");
272
273        (
274            ActorRuntime {
275                pending: self.pending.clone(),
276                mailbox: new_mailbox,
277                capacity: self.capacity,
278                blocked_on: self.blocked_on,
279                pending_send: self.pending_send.clone(),
280            },
281            Some(msg),
282        )
283    }
284
285    /// Consume message from mailbox (mutating version)
286    pub fn consume_mut(&mut self) -> Option<Message> {
287        self.mailbox.pop_front()
288    }
289
290    /// Set blocked state (for deadlock analysis)
291    ///
292    /// Corresponds to Lean: `def ActorRuntime.set_blocked`
293    pub fn set_blocked(&self, on: ActorId) -> Self {
294        ActorRuntime {
295            pending: self.pending.clone(),
296            mailbox: self.mailbox.clone(),
297            capacity: self.capacity,
298            blocked_on: Some(on),
299            pending_send: self.pending_send.clone(),
300        }
301    }
302
303    /// Set blocked state (mutating version)
304    pub fn set_blocked_mut(&mut self, on: ActorId) {
305        self.blocked_on = Some(on);
306    }
307
308    /// Clear blocked state
309    ///
310    /// Corresponds to Lean: `def ActorRuntime.unblock`
311    pub fn unblock(&self) -> Self {
312        ActorRuntime {
313            pending: self.pending.clone(),
314            mailbox: self.mailbox.clone(),
315            capacity: self.capacity,
316            blocked_on: None,
317            pending_send: self.pending_send.clone(),
318        }
319    }
320
321    /// Clear blocked state (mutating version)
322    pub fn unblock_mut(&mut self) {
323        self.blocked_on = None;
324    }
325
326    /// Get the blocked-on actor (if any)
327    #[inline]
328    pub fn blocked_on(&self) -> Option<ActorId> {
329        self.blocked_on
330    }
331
332    /// Check if actor is blocked
333    #[inline]
334    pub fn is_blocked(&self) -> bool {
335        self.blocked_on.is_some()
336    }
337
338    /// Get the capacity
339    #[inline]
340    pub fn capacity(&self) -> usize {
341        self.capacity
342    }
343
344    /// Get the pending queue length
345    #[inline]
346    pub fn pending_len(&self) -> usize {
347        self.pending.len()
348    }
349
350    /// Get the mailbox length
351    #[inline]
352    pub fn mailbox_len(&self) -> usize {
353        self.mailbox.len()
354    }
355
356    /// Get the pending send message
357    #[inline]
358    pub fn pending_send(&self) -> Option<&Message> {
359        self.pending_send.as_ref()
360    }
361
362    /// Set the pending send message
363    pub fn set_pending_send(&mut self, msg: Option<Message>) {
364        self.pending_send = msg;
365    }
366}
367
368impl Default for ActorRuntime {
369    fn default() -> Self {
370        ActorRuntime::empty(0)
371    }
372}
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377
378    fn make_test_message(id: MsgId) -> Message {
379        Message::new(id, 1, 2, SecurityLevel::Public, vec![1, 2, 3])
380    }
381
382    #[test]
383    fn test_actor_runtime_empty() {
384        let ar = ActorRuntime::empty(10);
385        assert_eq!(ar.capacity(), 10);
386        assert_eq!(ar.pending_len(), 0);
387        assert_eq!(ar.mailbox_len(), 0);
388        assert!(!ar.can_receive());
389        assert!(!ar.is_blocked());
390    }
391
392    #[test]
393    fn test_actor_runtime_enqueue_pending() {
394        let mut ar = ActorRuntime::empty(10);
395        let msg = make_test_message(1);
396
397        ar.enqueue_pending_mut(msg);
398        assert_eq!(ar.pending_len(), 1);
399    }
400
401    #[test]
402    fn test_actor_runtime_enqueue_pending_increases() {
403        // Corresponds to Lean theorem: ActorRuntime.enqueue_pending_increases
404        let ar = ActorRuntime::empty(10);
405        let msg = make_test_message(1);
406
407        let ar2 = ar.enqueue_pending(msg);
408        assert_eq!(ar2.pending_len(), ar.pending_len() + 1);
409    }
410
411    #[test]
412    fn test_actor_runtime_enqueue_pending_preserves_mailbox() {
413        // Corresponds to Lean theorem: ActorRuntime.enqueue_pending_preserves_mailbox
414        let ar = ActorRuntime::empty(10);
415        let msg = make_test_message(1);
416
417        let ar2 = ar.enqueue_pending(msg);
418        assert_eq!(ar2.mailbox_len(), ar.mailbox_len());
419    }
420
421    #[test]
422    fn test_actor_runtime_deliver() {
423        let mut ar = ActorRuntime::empty(10);
424        let msg = make_test_message(1);
425
426        ar.enqueue_pending_mut(msg);
427        assert_eq!(ar.pending_len(), 1);
428        assert_eq!(ar.mailbox_len(), 0);
429
430        let result = ar.deliver_mut();
431        assert!(matches!(result, Ok(true)));
432        assert_eq!(ar.pending_len(), 0);
433        assert_eq!(ar.mailbox_len(), 1);
434    }
435
436    #[test]
437    fn test_actor_runtime_deliver_decreases_pending() {
438        // Corresponds to Lean theorem: ActorRuntime.deliver_decreases_pending
439        let ar = ActorRuntime::empty(10);
440        let msg = make_test_message(1);
441
442        let ar = ar.enqueue_pending(msg);
443        let ar2 = ar.deliver();
444
445        assert!(ar2.pending_len() < ar.pending_len());
446    }
447
448    #[test]
449    fn test_actor_runtime_deliver_increases_mailbox() {
450        // Corresponds to Lean theorem: ActorRuntime.deliver_increases_mailbox
451        let ar = ActorRuntime::empty(10);
452        let msg = make_test_message(1);
453
454        let ar = ar.enqueue_pending(msg);
455        let ar2 = ar.deliver();
456
457        assert_eq!(ar2.mailbox_len(), ar.mailbox_len() + 1);
458    }
459
460    #[test]
461    fn test_actor_runtime_consume() {
462        let mut ar = ActorRuntime::empty(10);
463        let msg = make_test_message(42);
464
465        ar.enqueue_pending_mut(msg);
466        ar.deliver_mut().expect("deliver should succeed");
467
468        let consumed = ar.consume_mut();
469        assert!(consumed.is_some());
470        assert_eq!(consumed.map(|m| m.id()), Some(42));
471        assert_eq!(ar.mailbox_len(), 0);
472    }
473
474    #[test]
475    fn test_actor_runtime_consume_decreases_mailbox() {
476        // Corresponds to Lean theorem: ActorRuntime.consume_decreases_mailbox
477        let ar = ActorRuntime::empty(10);
478        let msg = make_test_message(1);
479
480        let ar = ar.enqueue_pending(msg);
481        let ar = ar.deliver();
482        let (ar2, _) = ar.consume();
483
484        assert!(ar2.mailbox_len() < ar.mailbox_len());
485    }
486
487    #[test]
488    fn test_actor_runtime_consume_preserves_capacity() {
489        // Corresponds to Lean theorem: ActorRuntime.consume_preserves_capacity
490        let ar = ActorRuntime::empty(10);
491        let msg = make_test_message(1);
492
493        let ar = ar.enqueue_pending(msg);
494        let ar = ar.deliver();
495        let (ar2, _) = ar.consume();
496
497        assert_eq!(ar2.capacity(), ar.capacity());
498    }
499
500    #[test]
501    fn test_actor_runtime_consume_preserves_blocked_on() {
502        // Corresponds to Lean theorem: ActorRuntime.consume_preserves_blockedOn
503        let ar = ActorRuntime::empty(10);
504        let msg = make_test_message(1);
505
506        let ar = ar.enqueue_pending(msg);
507        let ar = ar.deliver();
508        let ar = ar.set_blocked(99);
509        let (ar2, _) = ar.consume();
510
511        assert_eq!(ar2.blocked_on(), ar.blocked_on());
512    }
513
514    #[test]
515    fn test_actor_runtime_set_blocked() {
516        // Corresponds to Lean theorem: ActorRuntime.set_blocked_sets
517        let ar = ActorRuntime::empty(10);
518        let ar2 = ar.set_blocked(42);
519
520        assert_eq!(ar2.blocked_on(), Some(42));
521    }
522
523    #[test]
524    fn test_actor_runtime_set_blocked_preserves_mailbox() {
525        // Corresponds to Lean theorem: ActorRuntime.set_blocked_preserves_mailbox
526        let ar = ActorRuntime::empty(10);
527        let ar2 = ar.set_blocked(42);
528
529        assert_eq!(ar2.mailbox_len(), ar.mailbox_len());
530    }
531
532    #[test]
533    fn test_actor_runtime_unblock_clears() {
534        // Corresponds to Lean theorem: ActorRuntime.unblock_clears
535        let ar = ActorRuntime::empty(10);
536        let ar = ar.set_blocked(42);
537        let ar2 = ar.unblock();
538
539        assert_eq!(ar2.blocked_on(), None);
540    }
541
542    #[test]
543    fn test_actor_runtime_unblock_preserves_mailbox() {
544        // Corresponds to Lean theorem: ActorRuntime.unblock_preserves_mailbox
545        let ar = ActorRuntime::empty(10);
546        let ar = ar.set_blocked(42);
547        let ar2 = ar.unblock();
548
549        assert_eq!(ar2.mailbox_len(), ar.mailbox_len());
550    }
551
552    #[test]
553    fn test_actor_runtime_mailbox_full() {
554        let mut ar = ActorRuntime::empty(2);
555
556        // Fill mailbox
557        ar.enqueue_pending_mut(make_test_message(1));
558        ar.deliver_mut().expect("deliver 1");
559        ar.enqueue_pending_mut(make_test_message(2));
560        ar.deliver_mut().expect("deliver 2");
561
562        // Mailbox should be full
563        assert!(!ar.mailbox_has_space());
564        assert_eq!(ar.mailbox_len(), 2);
565
566        // Try to deliver one more
567        ar.enqueue_pending_mut(make_test_message(3));
568        let result = ar.deliver_mut();
569        assert!(matches!(result, Ok(false))); // Not delivered (full)
570    }
571
572    #[test]
573    fn test_message_accessors() {
574        let msg = Message::new(42, 1, 2, SecurityLevel::Confidential, vec![10, 20, 30]);
575
576        assert_eq!(msg.id(), 42);
577        assert_eq!(msg.src(), 1);
578        assert_eq!(msg.dst(), 2);
579        assert_eq!(msg.level(), SecurityLevel::Confidential);
580        assert_eq!(msg.body(), &vec![10, 20, 30]);
581    }
582}