1use std::any::Any;
2use std::collections::VecDeque;
3use std::fmt;
4
5use crate::floor::host::{Atom, HostType, Registry};
6use crate::floor::machine::{Fault, Machine, Mail, Step, Store};
7use crate::floor::{rc_val, Val};
8
9pub type Id = usize;
10
11pub type ConnId = u64;
13
14fn sym(s: &str) -> Val {
15 Val::Sym(s.into())
16}
17
18fn pair(a: Val, b: Val) -> Val {
19 Val::Pair(rc_val(a), rc_val(b))
20}
21
22#[derive(Debug, Clone, PartialEq)]
30pub enum Addr {
31 Task(Id),
32 Remote(ConnId, Id),
33}
34
35impl Addr {
36 pub const NAME: &'static str = "addr";
37
38 pub fn val(self) -> Val {
39 Val::Atom(Atom::new(self))
40 }
41
42 pub fn of(v: &Val) -> Option<&Addr> {
43 match v {
44 Val::Atom(a) => a.downcast::<Addr>(),
45 _ => None,
46 }
47 }
48
49 fn decode(bytes: &[u8]) -> Option<Atom> {
52 fn be(b: &[u8]) -> Option<u64> {
53 b.try_into().ok().map(u64::from_be_bytes)
54 }
55 fn id(b: &[u8]) -> Option<Id> {
56 be(b).and_then(|n| Id::try_from(n).ok())
57 }
58 match bytes.split_first()? {
59 (0, rest) => Some(Atom::new(Addr::Task(id(rest)?))),
60 (1, rest) if rest.len() == 16 => {
61 Some(Atom::new(Addr::Remote(be(&rest[..8])?, id(&rest[8..])?)))
62 }
63 _ => None,
64 }
65 }
66}
67
68impl fmt::Display for Addr {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 Addr::Task(id) => write!(f, "#<task {id}>"),
74 Addr::Remote(conn, id) => write!(f, "#<task {id} on {conn}>"),
75 }
76 }
77}
78
79impl HostType for Addr {
80 fn type_name(&self) -> &'static str {
81 Addr::NAME
82 }
83
84 fn eq_atom(&self, other: &dyn HostType) -> bool {
85 other.as_any().downcast_ref::<Addr>() == Some(self)
86 }
87
88 fn encode(&self, out: &mut Vec<u8>) {
89 match self {
90 Addr::Task(id) => {
91 out.push(0);
92 out.extend_from_slice(&(*id as u64).to_be_bytes());
93 }
94 Addr::Remote(conn, id) => {
95 out.push(1);
96 out.extend_from_slice(&conn.to_be_bytes());
97 out.extend_from_slice(&(*id as u64).to_be_bytes());
98 }
99 }
100 }
101
102 fn as_any(&self) -> &dyn Any {
103 self
104 }
105}
106
107pub fn extend(r: Registry) -> Registry {
108 r.ty(Addr::NAME, Addr::decode)
109}
110
111pub fn addr(id: Id) -> Val {
112 Addr::Task(id).val()
113}
114
115enum Route {
116 Local(Id),
117 Away,
120 Bad,
121}
122
123fn route(v: &Val) -> Route {
124 match Addr::of(v) {
125 Some(Addr::Task(id)) => Route::Local(*id),
126 Some(Addr::Remote(_, _)) => Route::Away,
127 None => match v {
130 Val::Pair(tag, _) if matches!(&**tag, Val::Sym(_)) => Route::Away,
131 _ => Route::Bad,
132 },
133 }
134}
135
136pub fn down(to: Val, result: Val) -> Val {
140 pair(sym("task-down"), pair(to, result))
141}
142
143fn task_down(id: Id, result: Val) -> Val {
144 down(addr(id), result)
145}
146
147enum Request {
149 Send {
150 to: Id,
151 msg: Val,
152 },
153 Away {
154 to: Val,
155 msg: Val,
156 },
157 Spawn {
158 id: Id,
159 thunk: Val,
160 },
161 Watch {
162 watcher: Id,
163 target: Id,
164 },
165 WatchAway {
168 watcher: Id,
169 target: Val,
170 },
171 Limit {
172 target: Id,
173 turns: usize,
174 },
175}
176
177pub struct Post {
181 me: Id,
182 inbox: VecDeque<Val>,
183 out: Vec<Request>,
184 reached: bool,
185 next: Id,
188}
189
190impl Mail for Post {
191 fn spawn(&mut self, thunk: Val) -> Result<Val, Val> {
192 let id = self.next;
193 self.next += 1;
194 self.out.push(Request::Spawn { id, thunk });
195 Ok(addr(id))
196 }
197
198 fn send(&mut self, to: &Val, msg: Val) -> Result<Val, Val> {
199 match route(to) {
200 Route::Local(id) if id == self.me => self.inbox.push_back(msg),
203 Route::Local(id) => self.out.push(Request::Send { to: id, msg }),
204 Route::Away => self.out.push(Request::Away {
205 to: to.clone(),
206 msg,
207 }),
208 Route::Bad => return Err(pair(sym("bad-address"), to.clone())),
209 }
210 Ok(Val::Nil)
211 }
212
213 fn receive(&mut self) -> Option<Val> {
217 self.reached = true;
218 self.inbox.pop_front()
219 }
220
221 fn monitor(&mut self, target: &Val) -> Result<Val, Val> {
228 match route(target) {
229 Route::Local(target) => {
230 self.out.push(Request::Watch {
231 watcher: self.me,
232 target,
233 });
234 Ok(Val::Nil)
235 }
236 Route::Away => {
237 self.out.push(Request::WatchAway {
238 watcher: self.me,
239 target: target.clone(),
240 });
241 Ok(Val::Nil)
242 }
243 Route::Bad => Err(pair(sym("bad-address"), target.clone())),
244 }
245 }
246
247 fn limit(&mut self, target: &Val, turns: i64) -> Result<Val, Val> {
251 match route(target) {
252 Route::Local(target) => {
253 self.out.push(Request::Limit {
254 target,
255 turns: turns as usize,
256 });
257 Ok(Val::Nil)
258 }
259 Route::Away => Err(pair(sym("not-local"), target.clone())),
260 Route::Bad => Err(pair(sym("bad-address"), target.clone())),
261 }
262 }
263}
264
265struct Task {
266 machine: Machine,
267 store: Store<Post>,
268 parked: bool,
270 watchers: Vec<Id>,
271 turns: usize,
273 limit: Option<usize>,
275}
276
277impl Task {
278 fn new(id: Id, thunk: Val) -> Task {
281 Task {
282 machine: Machine::applying(thunk, addr(id)),
283 store: Store::new(Post {
284 me: id,
285 inbox: VecDeque::new(),
286 out: Vec::new(),
287 reached: false,
288 next: 0,
289 }),
290 parked: false,
291 watchers: Vec::new(),
292 turns: 0,
293 limit: None,
294 }
295 }
296}
297
298pub struct Heap {
299 tasks: Vec<Option<Task>>,
303 runq: VecDeque<Id>,
304 cells: Vec<Val>,
307 budget: usize,
309 exits: Vec<(Id, Val)>,
310 away: Vec<(Val, Val)>,
312 watches: Vec<(Id, Val)>,
314 live: usize,
315}
316
317impl Heap {
318 pub fn new(budget: usize) -> Heap {
319 Heap {
320 tasks: Vec::new(),
321 runq: VecDeque::new(),
322 cells: Vec::new(),
323 budget,
324 exits: Vec::new(),
325 away: Vec::new(),
326 watches: Vec::new(),
327 live: 0,
328 }
329 }
330
331 pub fn spawn(&mut self, thunk: Val) -> Id {
333 let id = self.tasks.len();
334 self.tasks.push(Some(Task::new(id, thunk)));
335 self.runq.push_back(id);
336 self.live += 1;
337 id
338 }
339
340 pub fn idle(&self) -> bool {
343 self.live == 0
344 }
345
346 pub fn post(&mut self, to: &Val, msg: Val) {
349 if let Route::Local(id) = route(to) {
350 self.deliver(id, msg);
351 }
352 }
353
354 pub fn alive(&self, id: Id) -> bool {
355 matches!(self.tasks.get(id), Some(Some(_)))
356 }
357
358 pub fn take_exits(&mut self) -> Vec<(Id, Val)> {
360 std::mem::take(&mut self.exits)
361 }
362
363 pub fn take_away(&mut self) -> Vec<(Val, Val)> {
367 std::mem::take(&mut self.away)
368 }
369
370 pub fn take_watches(&mut self) -> Vec<(Id, Val)> {
373 std::mem::take(&mut self.watches)
374 }
375
376 pub fn run(&mut self) -> Result<(), Fault> {
378 while let Some(id) = self.runq.pop_front() {
379 self.turn(id)?;
380 }
381 Ok(())
382 }
383
384 fn turn(&mut self, id: Id) -> Result<(), Fault> {
385 let Some(mut task) = self.tasks[id].take() else {
386 return Ok(());
387 };
388
389 std::mem::swap(&mut self.cells, &mut task.store.cells);
390 task.store.mail.next = self.tasks.len();
391 let outcome = task.machine.turn(&mut task.store, self.budget);
392 std::mem::swap(&mut self.cells, &mut task.store.cells);
393 let out = std::mem::take(&mut task.store.mail.out);
394
395 let blocked = matches!(outcome, Ok((Step::Blocked, _)));
396 let ended = match outcome {
397 Ok((Step::Done(Val::Raise(v)), _)) => Some(pair(sym("throw"), Val::clone(&v))),
400 Ok((Step::Done(v), _)) => Some(pair(sym("ok"), v)),
401 Err(bug) => return Err(bug),
404 Ok((Step::Running, _)) => None,
405 Ok((Step::Blocked, _)) => None,
406 };
407
408 if std::mem::take(&mut task.store.mail.reached) {
409 task.turns = 0;
410 } else {
411 task.turns += 1;
412 }
413 let ended = ended.or_else(|| {
416 task.limit
417 .is_some_and(|n| task.turns > n)
418 .then(|| pair(sym("throw"), sym("unresponsive")))
419 });
420
421 match ended {
422 Some(result) => {
423 let watchers = std::mem::take(&mut task.watchers);
424 self.drain(out);
428 for w in watchers {
429 self.deliver(w, task_down(id, result.clone()));
430 }
431 self.exits.push((id, result));
432 self.live -= 1;
433 }
434 None => {
435 self.tasks[id] = Some(task);
436 self.drain(out);
437 let task = self.tasks[id].as_mut().expect("just restored");
438 if blocked && task.store.mail.inbox.is_empty() {
439 task.parked = true;
440 } else {
441 self.runq.push_back(id);
442 }
443 }
444 }
445 Ok(())
446 }
447
448 fn drain(&mut self, out: Vec<Request>) {
449 for req in out {
450 match req {
451 Request::Send { to, msg } => self.deliver(to, msg),
452 Request::Away { to, msg } => self.away.push((to, msg)),
453 Request::WatchAway { watcher, target } => self.watches.push((watcher, target)),
454 Request::Spawn { id, thunk } => {
455 debug_assert_eq!(id, self.tasks.len(), "ids are handed out in order");
456 self.tasks.push(Some(Task::new(id, thunk)));
457 self.runq.push_back(id);
458 self.live += 1;
459 }
460 Request::Watch { watcher, target } => {
461 match self.tasks.get_mut(target).and_then(Option::as_mut) {
462 Some(task) => task.watchers.push(watcher),
463 None => {
466 let down = task_down(target, pair(sym("throw"), sym("noproc")));
467 self.deliver(watcher, down);
468 }
469 }
470 }
471 Request::Limit { target, turns } => {
473 if let Some(Some(task)) = self.tasks.get_mut(target) {
474 task.limit = Some(turns);
475 }
476 }
477 }
478 }
479 }
480
481 fn deliver(&mut self, to: Id, msg: Val) {
482 let Some(Some(task)) = self.tasks.get_mut(to) else {
483 return;
484 };
485 task.store.mail.inbox.push_back(msg);
486 let wake = task.parked;
487 task.parked = false;
488 if wake {
489 self.runq.push_back(to);
490 }
491 }
492}
493
494#[cfg(test)]
495mod tests {
496 use super::*;
497 use crate::floor::machine::NoMail;
498 use crate::floor::Env;
499
500 fn val(src: &str) -> Val {
501 let Val::Code(e) = crate::surface::compile(src).expect("compiles") else {
502 panic!("not code")
503 };
504 Machine::new(Env::default(), e)
505 .run(&mut Store::<NoMail>::default())
506 .expect("no fault")
507 }
508
509 fn ok(v: Val) -> Val {
510 pair(sym("ok"), v)
511 }
512
513 fn run(h: &mut Heap) -> Vec<(Id, Val)> {
514 h.run().expect("no bug");
515 h.take_exits()
516 }
517
518 #[test]
519 fn a_task_runs_to_completion() {
520 let mut h = Heap::new(1000);
521 let id = h.spawn(val("(lambda t (_) (+ 1 2))"));
522 assert_eq!(run(&mut h), vec![(id, ok(Val::Num(3)))]);
523 assert!(!h.alive(id));
524 }
525
526 #[test]
527 fn a_throw_is_a_result_not_a_heap_failure() {
528 let mut h = Heap::new(1000);
529 let id = h.spawn(val("(lambda t (_) (throw 'nope))"));
530 assert_eq!(run(&mut h), vec![(id, pair(sym("throw"), sym("nope")))]);
531 }
532
533 #[test]
534 fn an_empty_mailbox_is_the_only_reason_to_park() {
535 let mut h = Heap::new(1000);
536 let id = h.spawn(val("(lambda t (_) (receive 0))"));
537 assert_eq!(run(&mut h), vec![]);
539 assert!(h.alive(id));
540
541 h.post(&addr(id), Val::Num(7));
542 assert_eq!(run(&mut h), vec![(id, ok(Val::Num(7)))]);
543 }
544
545 #[test]
546 fn a_task_is_told_its_own_address() {
547 let mut h = Heap::new(1000);
548 let id = h.spawn(val("(lambda t (me) me)"));
549 assert_eq!(run(&mut h), vec![(id, ok(addr(id)))]);
550 }
551
552 #[test]
553 fn a_send_to_oneself_is_visible_to_the_next_receive() {
554 let mut h = Heap::new(1000);
555 let id = h.spawn(val("(lambda t (me) (begin (send me 5) (receive)))"));
556 assert_eq!(run(&mut h), vec![(id, ok(Val::Num(5)))]);
557 }
558
559 #[test]
560 fn a_spawned_task_gets_the_next_address_and_runs() {
561 let mut h = Heap::new(1000);
562 let parent = h.spawn(val("(lambda t (me)
563 (let ((child (spawn (lambda c (_) (send me 'hi)))))
564 (cons child (receive 0))))"));
565 let exits = run(&mut h);
566 assert_eq!(
567 exits,
568 vec![(1, ok(Val::Nil)), (parent, ok(pair(addr(1), sym("hi")))),]
569 );
570 }
571
572 #[test]
573 fn a_monitor_reports_a_death_it_did_not_see() {
574 let mut h = Heap::new(1000);
575 let watcher = h.spawn(val("(lambda t (_)
576 (let ((child (spawn (lambda c (_) 9))))
577 (begin (monitor child) (receive 0))))"));
578 let exits = run(&mut h);
579 let down = task_down(1, ok(Val::Num(9)));
580 assert_eq!(exits, vec![(1, ok(Val::Num(9))), (watcher, ok(down))]);
581 }
582
583 #[test]
584 fn a_dying_task_is_heard_before_it_is_mourned() {
585 let mut h = Heap::new(1000);
586 let watcher = h.spawn(val("(lambda t (me)
587 (let ((child (spawn (lambda c (_) (begin (send me 'answer) 9)))))
588 (begin (monitor child)
589 (let ((first (receive 0))) (cons first (receive 0))))))"));
590 let heard = pair(sym("answer"), task_down(1, ok(Val::Num(9))));
591 assert_eq!(
592 run(&mut h),
593 vec![(1, ok(Val::Num(9))), (watcher, ok(heard))]
594 );
595 }
596
597 #[test]
598 fn monitoring_a_dead_task_answers_at_once() {
599 let mut h = Heap::new(1000);
600 let id = h.spawn(val(
601 "(lambda t (_) (begin (monitor (receive)) (receive 0)))",
602 ));
603 h.post(&addr(0), addr(99));
604 let down = task_down(99, pair(sym("throw"), sym("noproc")));
605 assert_eq!(run(&mut h), vec![(id, ok(down))]);
606 }
607
608 #[test]
609 fn a_budget_rotates_tasks_rather_than_running_one_to_the_end() {
610 let src = "(lambda t (_)
614 (let ((down (lambda d (n) (if (eq? n 0) 'done (d (- n 1))))))
615 (down 200)))";
616 let mut h = Heap::new(8);
617 let a = h.spawn(val(src));
618 let b = h.spawn(val(src));
619 let exits = run(&mut h);
620 assert_eq!(exits.len(), 2);
621 assert_eq!(exits[0].0, a);
622 assert_eq!(exits[1].0, b);
623 assert_eq!(exits[0].1, ok(sym("done")));
624 }
625
626 #[test]
627 fn a_channel_carries_data_and_refuses_anything_else() {
628 let mut h = Heap::new(1000);
629 let cell = h.spawn(val("(lambda t (_) (send '(task 0) (cell-new 1)))"));
630 assert_eq!(
631 run(&mut h),
632 vec![(
633 cell,
634 pair(sym("throw"), pair(sym("not-data"), Val::Cell(0)))
635 )]
636 );
637
638 let mut h = Heap::new(1000);
641 let clo = h.spawn(val(
642 "(lambda t (_) (car (catch (send '(task 0) (cons 1 (lambda f (x) x))))))",
643 ));
644 assert_eq!(run(&mut h), vec![(clo, ok(sym("not-data")))]);
645 }
646
647 #[test]
648 fn an_address_that_is_not_one_is_a_throw() {
649 let mut h = Heap::new(1000);
650 let id = h.spawn(val("(lambda t (_) (send 0 'hi))"));
651 assert_eq!(
652 run(&mut h),
653 vec![(
654 id,
655 pair(sym("throw"), pair(sym("bad-address"), Val::Num(0)))
656 )]
657 );
658 }
659
660 #[test]
661 fn a_task_across_a_link_leaves_the_heap() {
662 let mut h = Heap::new(1000);
663 let to = Addr::Remote(2, 5).val();
664 h.spawn(val("(lambda t (_) (receive))"));
665 h.post(&addr(0), to.clone());
666 h.spawn(val("(lambda t (_) (send (receive) 'hi))"));
667 h.post(&addr(1), to.clone());
668 h.run().expect("no bug");
669 assert_eq!(h.take_away(), vec![(to, sym("hi"))]);
670 }
671
672 #[test]
673 fn an_address_the_heap_does_not_know_goes_out_rather_than_failing() {
674 let mut h = Heap::new(1000);
675 h.spawn(val("(lambda t (_) (send '(host nowhere) 'hi))"));
676 h.run().expect("no bug");
677 assert_eq!(h.take_away(), vec![(val("'(host nowhere)"), sym("hi"))]);
678 }
679
680 #[test]
681 fn tasks_in_one_heap_share_cells() {
682 let mut h = Heap::new(1000);
683 let id = h.spawn(val("(lambda t (_)
684 (let ((c (cell-new 0)))
685 (let ((child (spawn (lambda k (_) (cell-set! c 42)))))
686 (begin (monitor child) (begin (receive 0) (cell-read c))))))"));
687 assert_eq!(
688 run(&mut h),
689 vec![(1, ok(Val::Num(42))), (id, ok(Val::Num(42)))]
690 );
691 }
692}