Implement domain and endpoint modules with capability management, message passing, and scheduling features
This commit is contained in:
78
crates/domain/src/endpoint/mod.rs
Normal file
78
crates/domain/src/endpoint/mod.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use types::{
|
||||
bounded::CapSlot,
|
||||
generation::Generation,
|
||||
id::{EndpointId, ProcessId, ThreadId},
|
||||
};
|
||||
|
||||
use crate::{endpoint::wait_queue::WaitQueue, error::KernelError, message::Message};
|
||||
|
||||
pub mod wait_queue;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "tests.rs"]
|
||||
mod tests;
|
||||
|
||||
pub struct Endpoint {
|
||||
id: EndpointId,
|
||||
waiting_senders: WaitQueue,
|
||||
waiting_receivers: WaitQueue,
|
||||
generation: Generation,
|
||||
}
|
||||
|
||||
impl Endpoint {
|
||||
pub fn new(id: EndpointId) -> Self {
|
||||
Self {
|
||||
id,
|
||||
waiting_senders: WaitQueue::new(),
|
||||
waiting_receivers: WaitQueue::new(),
|
||||
generation: Generation::initial(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &EndpointId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn generation(&self) -> &Generation {
|
||||
&self.generation
|
||||
}
|
||||
|
||||
pub fn enqueue_sender(&mut self, thread: ThreadId) -> Option<()> {
|
||||
self.waiting_senders.push(thread)
|
||||
}
|
||||
|
||||
pub fn dequeue_sender(&mut self) -> Option<ThreadId> {
|
||||
self.waiting_senders.pop()
|
||||
}
|
||||
|
||||
pub fn enqueue_receiver(&mut self, thread: ThreadId) -> Option<()> {
|
||||
self.waiting_receivers.push(thread)
|
||||
}
|
||||
|
||||
pub fn dequeue_receiver(&mut self) -> Option<ThreadId> {
|
||||
self.waiting_receivers.pop()
|
||||
}
|
||||
|
||||
pub fn has_waiting_sender(&self) -> bool {
|
||||
!self.waiting_senders.is_empty()
|
||||
}
|
||||
|
||||
pub fn has_waiting_receiver(&self) -> bool {
|
||||
!self.waiting_receivers.is_empty()
|
||||
}
|
||||
|
||||
pub fn revoke(&mut self) {
|
||||
self.generation = self.generation.next();
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MessagePassing {
|
||||
fn send(
|
||||
&mut self,
|
||||
sender: ProcessId,
|
||||
endpoint: CapSlot,
|
||||
message: Message,
|
||||
) -> Result<(), KernelError>;
|
||||
|
||||
fn receive(&mut self, receiver: ProcessId, endpoint: CapSlot) -> Result<Message, KernelError>;
|
||||
}
|
||||
137
crates/domain/src/endpoint/tests.rs
Normal file
137
crates/domain/src/endpoint/tests.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use crate::endpoint::wait_queue::MAX_WAITERS;
|
||||
|
||||
use super::*;
|
||||
use types::id::{EndpointId, ThreadId};
|
||||
|
||||
fn thread(id: u16) -> ThreadId {
|
||||
ThreadId::from_raw(id)
|
||||
}
|
||||
|
||||
fn test_endpoint() -> Endpoint {
|
||||
Endpoint::new(EndpointId::from_raw(1))
|
||||
}
|
||||
|
||||
// --- WaitQueue tests ---
|
||||
|
||||
#[test]
|
||||
fn empty_queue_pop_returns_none() {
|
||||
let mut queue = WaitQueue::new();
|
||||
assert!(queue.pop().is_none());
|
||||
assert!(queue.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_then_pop_returns_same_thread() {
|
||||
let mut queue = WaitQueue::new();
|
||||
queue.push(thread(1)).unwrap();
|
||||
assert_eq!(queue.pop(), Some(thread(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pop_order_is_fifo() {
|
||||
let mut queue = WaitQueue::new();
|
||||
queue.push(thread(1)).unwrap();
|
||||
queue.push(thread(2)).unwrap();
|
||||
queue.push(thread(3)).unwrap();
|
||||
|
||||
assert_eq!(queue.pop(), Some(thread(1)));
|
||||
assert_eq!(queue.pop(), Some(thread(2)));
|
||||
assert_eq!(queue.pop(), Some(thread(3)));
|
||||
assert!(queue.pop().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_queue_rejects_push() {
|
||||
let mut queue = WaitQueue::new();
|
||||
for i in 0..MAX_WAITERS {
|
||||
assert!(queue.push(thread(i as u16)).is_some());
|
||||
}
|
||||
assert!(queue.push(thread(999)).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_empty_after_drain() {
|
||||
let mut queue = WaitQueue::new();
|
||||
queue.push(thread(1)).unwrap();
|
||||
queue.push(thread(2)).unwrap();
|
||||
queue.pop();
|
||||
queue.pop();
|
||||
assert!(queue.is_empty());
|
||||
}
|
||||
|
||||
// --- Endpoint tests ---
|
||||
|
||||
#[test]
|
||||
fn new_endpoint_has_no_waiters() {
|
||||
let ep = test_endpoint();
|
||||
assert!(!ep.has_waiting_sender());
|
||||
assert!(!ep.has_waiting_receiver());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sender_waits_then_dequeues() {
|
||||
let mut ep = test_endpoint();
|
||||
ep.enqueue_sender(thread(1)).unwrap();
|
||||
|
||||
assert!(ep.has_waiting_sender());
|
||||
assert_eq!(ep.dequeue_sender(), Some(thread(1)));
|
||||
assert!(!ep.has_waiting_sender());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receiver_waits_then_dequeues() {
|
||||
let mut ep = test_endpoint();
|
||||
ep.enqueue_receiver(thread(1)).unwrap();
|
||||
|
||||
assert!(ep.has_waiting_receiver());
|
||||
assert_eq!(ep.dequeue_receiver(), Some(thread(1)));
|
||||
assert!(!ep.has_waiting_receiver());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn senders_and_receivers_are_independent() {
|
||||
let mut ep = test_endpoint();
|
||||
ep.enqueue_sender(thread(1)).unwrap();
|
||||
ep.enqueue_receiver(thread(2)).unwrap();
|
||||
|
||||
assert_eq!(ep.dequeue_sender(), Some(thread(1)));
|
||||
assert_eq!(ep.dequeue_receiver(), Some(thread(2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revoke_bumps_generation() {
|
||||
let mut ep = test_endpoint();
|
||||
let gen_before = *ep.generation();
|
||||
ep.revoke();
|
||||
let gen_after = *ep.generation();
|
||||
|
||||
assert!(!gen_before.is_valid_against(&gen_after));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rendezvous_sender_first() {
|
||||
let mut ep = test_endpoint();
|
||||
|
||||
// sender arrives first, no receiver waiting
|
||||
ep.enqueue_sender(thread(1)).unwrap();
|
||||
assert!(!ep.has_waiting_receiver());
|
||||
|
||||
// receiver arrives, finds a sender
|
||||
assert!(ep.has_waiting_sender());
|
||||
let sender = ep.dequeue_sender().unwrap();
|
||||
assert_eq!(sender, thread(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rendezvous_receiver_first() {
|
||||
let mut ep = test_endpoint();
|
||||
|
||||
// receiver arrives first, no sender waiting
|
||||
ep.enqueue_receiver(thread(1)).unwrap();
|
||||
assert!(!ep.has_waiting_sender());
|
||||
|
||||
// sender arrives, finds a receiver
|
||||
assert!(ep.has_waiting_receiver());
|
||||
let receiver = ep.dequeue_receiver().unwrap();
|
||||
assert_eq!(receiver, thread(1));
|
||||
}
|
||||
35
crates/domain/src/endpoint/wait_queue.rs
Normal file
35
crates/domain/src/endpoint/wait_queue.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use types::id::ThreadId;
|
||||
|
||||
pub const MAX_WAITERS: usize = 16;
|
||||
|
||||
pub struct WaitQueue {
|
||||
entries: [Option<ThreadId>; MAX_WAITERS],
|
||||
count: u8,
|
||||
}
|
||||
|
||||
impl WaitQueue {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
entries: [None; MAX_WAITERS],
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, thread: ThreadId) -> Option<()> {
|
||||
let slot = self.entries.iter_mut().find(|s| s.is_none())?;
|
||||
*slot = Some(thread);
|
||||
self.count += 1;
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<ThreadId> {
|
||||
let slot = self.entries.iter_mut().find(|s| s.is_some())?;
|
||||
let thread = slot.take();
|
||||
self.count -= 1;
|
||||
thread
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.count == 0
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user