strat9_kernel/ipc/channel.rs
1//! Typed MPMC blocking channel for IPC between kernel tasks and silos.
2//!
3//! ## Two levels of abstraction
4//!
5//! ### 1. Typed MPMC channel : kernel-internal
6//!
7//! [`channel`]`<T>(capacity)` returns a `(`[`Sender`]`<T>, `[`Receiver`]`<T>)` pair.
8//! Both endpoints are cloneable (Multi-Producer / Multi-Consumer).
9//! When the last `Sender` is dropped, all waiting `Receiver`s see
10//! `Err(`[`ChannelError::Disconnected`]`)`, and vice-versa.
11//!
12//! ```text
13//! let (tx, rx) = channel::<u64>(8);
14//! let tx2 = tx.clone(); // second producer
15//! let rx2 = rx.clone(); // second consumer
16//! ```
17//!
18//! ### 2. Symmetric channel : userspace IPC (silo-to-silo)
19//!
20//! [`SyncChan`] is a symmetric [`IpcMessage`] channel: any holder can send
21//! *or* receive. It is stored by [`ChanId`] in a global registry and
22//! accessed from userspace via `SYS_CHAN_*` syscalls. Destroyed explicitly
23//! via [`SyncChan::destroy`] when all userspace handles are closed.
24//!
25//! ## Blocking guarantee
26//!
27//! Both levels use [`WaitQueue::wait_until`] : the condition closure is
28//! evaluated atomically under the waiter lock, eliminating the classic
29//! lost-wakeup race without a polling loop.
30//!
31//! ## Lock ordering
32//!
33//! To avoid deadlock:
34//! - The `queue` (buffer) lock is **always** acquired *inside* the
35//! `wait_until` closure, and released *before* `wake_one()` is called.
36//! - `send_waitq.wake_one()` is called **outside** any recv closure.
37//! - `recv_waitq.wake_one()` is called **outside** any send closure.
38
39use super::message::IpcMessage;
40use crate::sync::{SpinLock, WaitQueue};
41use alloc::{collections::BTreeMap, sync::Arc};
42use core::sync::atomic::{AtomicBool, AtomicU64, AtomicU8, AtomicUsize, Ordering};
43use crossbeam_queue::ArrayQueue;
44
45const STATUS_CONNECTED: u8 = 0;
46const STATUS_SENDER_GONE: u8 = 1;
47const STATUS_RECEIVER_GONE: u8 = 2;
48
49// P2 fix: use bitset so sender/receiver closure can happen concurrently
50// without overwriting each other's state.
51const SENDERS_CLOSED: u8 = 1 << 0;
52const RECEIVERS_CLOSED: u8 = 1 << 1;
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
55pub enum ChannelError {
56 #[error("would block")]
57 WouldBlock,
58 #[error("channel disconnected")]
59 Disconnected,
60 #[error("interrupted by signal")]
61 Interrupted,
62}
63
64// ================================================================================
65// Typed MPMC channel : kernel-internal
66// ================================================================================
67
68/// Shared inner state for the typed MPMC channel.
69struct ChannelInner<T: Send> {
70 /// Bounded message queue.
71 buffer: ArrayQueue<T>,
72 /// Tasks blocked because the buffer is full (waiting to send).
73 send_waitq: WaitQueue,
74 /// Tasks blocked because the buffer is empty (waiting to receive).
75 recv_waitq: WaitQueue,
76 /// Channel status: CONNECTED / SENDER_GONE / RECEIVER_GONE.
77 status: AtomicU8,
78 /// Number of live [`Sender`] endpoints.
79 sender_count: AtomicUsize,
80 /// Number of live [`Receiver`] endpoints.
81 receiver_count: AtomicUsize,
82}
83
84impl<T: Send> ChannelInner<T> {
85 /// Creates a new instance.
86 fn new(capacity: usize) -> Self {
87 ChannelInner {
88 buffer: ArrayQueue::new(capacity.max(1)),
89 send_waitq: WaitQueue::new(),
90 recv_waitq: WaitQueue::new(),
91 status: AtomicU8::new(STATUS_CONNECTED),
92 sender_count: AtomicUsize::new(1),
93 receiver_count: AtomicUsize::new(1),
94 }
95 }
96
97 /// Returns whether sender gone.
98 #[inline]
99 fn is_sender_gone(&self) -> bool {
100 self.status.load(Ordering::Acquire) & SENDERS_CLOSED != 0
101 }
102
103 /// Returns whether receiver gone.
104 #[inline]
105 fn is_receiver_gone(&self) -> bool {
106 self.status.load(Ordering::Acquire) & RECEIVERS_CLOSED != 0
107 }
108}
109
110/// The send end of a [`channel`].
111///
112/// Cloneable (MPMC): each clone shares the same internal channel.
113/// When the last `Sender` is dropped, waiting receivers are woken with
114/// [`ChannelError::Disconnected`].
115pub struct Sender<T: Send> {
116 inner: Arc<ChannelInner<T>>,
117}
118
119impl<T: Send> Clone for Sender<T> {
120 /// Performs the clone operation.
121 fn clone(&self) -> Self {
122 self.inner.sender_count.fetch_add(1, Ordering::AcqRel);
123 Sender {
124 inner: self.inner.clone(),
125 }
126 }
127}
128
129impl<T: Send> Drop for Sender<T> {
130 /// Performs the drop operation.
131 fn drop(&mut self) {
132 if self.inner.sender_count.fetch_sub(1, Ordering::AcqRel) == 1 {
133 // Last sender gone : mark and wake blocked receivers.
134 self.inner
135 .status
136 .store(STATUS_SENDER_GONE, Ordering::Release);
137 self.inner.recv_waitq.wake_all();
138 }
139 }
140}
141
142impl<T: Send> Sender<T> {
143 /// Send a message, blocking until buffer space is available.
144 ///
145 /// Returns `Ok(())` on success, or `Err(`[`ChannelError::Disconnected`]`)`
146 /// if all receivers have been dropped before or during the send.
147 pub fn send(&self, msg: T) -> Result<(), ChannelError> {
148 let mut pending = Some(msg);
149
150 let result = self.inner.send_waitq.wait_until(|| {
151 // P2 fix: check for pending signals to avoid livelock.
152 if crate::process::signal::has_pending_signals() {
153 pending.take();
154 return Some(Err(ChannelError::Interrupted));
155 }
156 // Receiver gone: discard message and report disconnect.
157 if self.inner.is_receiver_gone() {
158 pending.take();
159 return Some(Err(ChannelError::Disconnected));
160 }
161
162 // SAFETY: `pending` is always `Some` on every invocation of this
163 // closure. It is `take`-n here and either pushed (success) or
164 // replaced (full queue => retry next wakeup).
165 let m = pending.take().unwrap();
166 match self.inner.buffer.push(m) {
167 Ok(()) => Some(Ok(())),
168 Err(m) => {
169 pending = Some(m);
170 None
171 }
172 }
173 // `buf` (queue lock) is released here, before returning from the
174 // closure : never held while wake_one() is called below.
175 });
176
177 // Wake exactly one receiver AFTER releasing the waiters lock.
178 if result.is_ok() {
179 self.inner.recv_waitq.wake_one();
180 }
181 result
182 }
183
184 /// Try to send without blocking.
185 ///
186 /// Returns `Err((msg, WouldBlock))` if the buffer is full, or
187 /// `Err((msg, Disconnected))` if all receivers are gone.
188 pub fn try_send(&self, msg: T) -> Result<(), (T, ChannelError)> {
189 if self.inner.is_receiver_gone() {
190 return Err((msg, ChannelError::Disconnected));
191 }
192 match self.inner.buffer.push(msg) {
193 Ok(()) => {
194 self.inner.recv_waitq.wake_one();
195 Ok(())
196 }
197 Err(m) => {
198 let err = if self.inner.is_receiver_gone() {
199 ChannelError::Disconnected
200 } else {
201 ChannelError::WouldBlock
202 };
203 Err((m, err))
204 }
205 }
206 }
207
208 /// Returns `true` if all receivers have been dropped.
209 pub fn is_disconnected(&self) -> bool {
210 self.inner.is_receiver_gone()
211 }
212
213 /// Create a new [`Receiver`] endpoint connected to the same channel.
214 pub fn receiver(&self) -> Receiver<T> {
215 self.inner.receiver_count.fetch_add(1, Ordering::AcqRel);
216 let _ = self.inner.status.compare_exchange(
217 STATUS_RECEIVER_GONE,
218 STATUS_CONNECTED,
219 Ordering::AcqRel,
220 Ordering::Acquire,
221 );
222 Receiver {
223 inner: self.inner.clone(),
224 }
225 }
226}
227
228/// The receive end of a [`channel`].
229///
230/// Cloneable (MPMC): each clone shares the same internal channel.
231/// When the last `Receiver` is dropped, waiting senders are woken with
232/// [`ChannelError::Disconnected`].
233pub struct Receiver<T: Send> {
234 inner: Arc<ChannelInner<T>>,
235}
236
237impl<T: Send> Clone for Receiver<T> {
238 /// Performs the clone operation.
239 fn clone(&self) -> Self {
240 self.inner.receiver_count.fetch_add(1, Ordering::AcqRel);
241 Receiver {
242 inner: self.inner.clone(),
243 }
244 }
245}
246
247impl<T: Send> Drop for Receiver<T> {
248 /// Performs the drop operation.
249 fn drop(&mut self) {
250 if self.inner.receiver_count.fetch_sub(1, Ordering::AcqRel) == 1 {
251 // Last receiver gone : mark and wake blocked senders.
252 self.inner
253 .status
254 .store(STATUS_RECEIVER_GONE, Ordering::Release);
255 self.inner.send_waitq.wake_all();
256 }
257 }
258}
259
260impl<T: Send> Receiver<T> {
261 /// Receive a message, blocking until one is available.
262 ///
263 /// Returns `Ok(msg)` on success. Returns `Err(`[`ChannelError::Disconnected`]`)`
264 /// if all senders have been dropped *and* the buffer is empty.
265 pub fn recv(&self) -> Result<T, ChannelError> {
266 let result = self.inner.recv_waitq.wait_until(|| {
267 // Try to pop under the waiters lock so we don't race with senders.
268 let msg_opt = self.inner.buffer.pop();
269 if let Some(msg) = msg_opt {
270 return Some(Ok(msg));
271 }
272 // P2 fix: check for pending signals to avoid livelock.
273 if crate::process::signal::has_pending_signals() {
274 return Some(Err(ChannelError::Interrupted));
275 }
276 // Buffer empty: check for disconnect.
277 if self.inner.is_sender_gone() {
278 return Some(Err(ChannelError::Disconnected));
279 }
280 None // keep waiting
281 });
282
283 // Wake exactly one sender AFTER releasing the waiters lock.
284 if result.is_ok() {
285 self.inner.send_waitq.wake_one();
286 }
287 result
288 }
289
290 /// Try to receive without blocking.
291 ///
292 /// Returns `Err(WouldBlock)` if the buffer is empty, or
293 /// `Err(Disconnected)` if all senders are gone and the buffer is empty.
294 pub fn try_recv(&self) -> Result<T, ChannelError> {
295 let msg_opt = self.inner.buffer.pop();
296 if let Some(msg) = msg_opt {
297 self.inner.send_waitq.wake_one();
298 return Ok(msg);
299 }
300 if self.inner.is_sender_gone() {
301 return Err(ChannelError::Disconnected);
302 }
303 Err(ChannelError::WouldBlock)
304 }
305
306 /// Returns `true` if all senders have been dropped.
307 pub fn is_disconnected(&self) -> bool {
308 self.inner.is_sender_gone()
309 }
310
311 /// Create a new [`Sender`] endpoint connected to the same channel.
312 pub fn sender(&self) -> Sender<T> {
313 self.inner.sender_count.fetch_add(1, Ordering::AcqRel);
314 let _ = self.inner.status.compare_exchange(
315 STATUS_SENDER_GONE,
316 STATUS_CONNECTED,
317 Ordering::AcqRel,
318 Ordering::Acquire,
319 );
320 Sender {
321 inner: self.inner.clone(),
322 }
323 }
324}
325
326/// Create a new bounded MPMC channel with the given `capacity`.
327///
328/// Returns `(Sender<T>, Receiver<T>)`. Both endpoints are cloneable to add
329/// more producers or consumers. The capacity is rounded up to at least 1.
330///
331/// # Example (kernel-internal)
332///
333/// ```rust,ignore
334/// let (tx, rx) = channel::<u64>(8);
335/// tx.send(42).unwrap();
336/// assert_eq!(rx.recv().unwrap(), 42);
337/// ```
338pub fn channel<T: Send>(capacity: usize) -> (Sender<T>, Receiver<T>) {
339 let inner = Arc::new(ChannelInner::new(capacity));
340 (
341 Sender {
342 inner: inner.clone(),
343 },
344 Receiver { inner },
345 )
346}
347
348// Symmetric channel (SyncChan) : userspace / silo-to-silo IPC
349/// A symmetric bounded channel over [`IpcMessage`], used by the global
350/// channel registry for silo-to-silo syscall-level IPC.
351///
352/// Unlike [`Sender`]/[`Receiver`], `SyncChan` has no directional
353/// specialisation: any caller with an `Arc<SyncChan>` can both send and
354/// receive. Destruction is explicit (via [`SyncChan::destroy`]), triggered
355/// when the last userspace handle is closed.
356pub struct SyncChan {
357 /// Original capacity used to allocate the queue.
358 capacity: usize,
359 /// Bounded message queue.
360 queue: ArrayQueue<IpcMessage>,
361 /// Tasks blocked because the queue is full.
362 send_waitq: WaitQueue,
363 /// Tasks blocked because the queue is empty.
364 recv_waitq: WaitQueue,
365 /// Set to `true` by [`SyncChan::destroy`]; wakes all blocked tasks.
366 destroyed: AtomicBool,
367}
368
369impl SyncChan {
370 /// Creates a new instance.
371 fn new(capacity: usize) -> Self {
372 SyncChan {
373 capacity,
374 queue: ArrayQueue::new(capacity.max(1)),
375 send_waitq: WaitQueue::new(),
376 recv_waitq: WaitQueue::new(),
377 destroyed: AtomicBool::new(false),
378 }
379 }
380
381 /// Returns the original capacity used to create this channel.
382 pub fn capacity(&self) -> usize {
383 self.capacity
384 }
385
386 /// Send a message, blocking until space is available.
387 ///
388 /// Returns `Err(`[`ChannelError::Disconnected`]`)` if the channel has
389 /// been destroyed while the sender was blocked.
390 pub fn send(&self, msg: IpcMessage) -> Result<(), ChannelError> {
391 let mut pending = Some(msg);
392
393 let result = self.send_waitq.wait_until(|| {
394 // P2 fix: check for pending signals to avoid livelock.
395 if crate::process::signal::has_pending_signals() {
396 pending.take();
397 return Some(Err(ChannelError::Interrupted));
398 }
399 if self.destroyed.load(Ordering::Acquire) {
400 pending.take();
401 return Some(Err(ChannelError::Disconnected));
402 }
403 // SAFETY: `pending` is always `Some` on every closure invocation.
404 let m = pending.take().unwrap();
405 match self.queue.push(m) {
406 Ok(()) => Some(Ok(())),
407 Err(m) => {
408 pending = Some(m);
409 None
410 }
411 }
412 // queue lock released here
413 });
414
415 if result.is_ok() {
416 self.recv_waitq.wake_one();
417 }
418 result
419 }
420
421 /// Try to send without blocking.
422 ///
423 /// Returns `Err(WouldBlock)` if the queue is full, or
424 /// `Err(Disconnected)` if the channel is destroyed.
425 pub fn try_send(&self, msg: IpcMessage) -> Result<(), ChannelError> {
426 if self.destroyed.load(Ordering::Acquire) {
427 return Err(ChannelError::Disconnected);
428 }
429 match self.queue.push(msg) {
430 Ok(()) => {
431 self.recv_waitq.wake_one();
432 Ok(())
433 }
434 Err(_) => Err(ChannelError::WouldBlock),
435 }
436 }
437
438 /// Receive a message, blocking until one arrives.
439 ///
440 /// Returns `Err(`[`ChannelError::Disconnected`]`)` if the channel was
441 /// destroyed while the receiver was blocked.
442 pub fn recv(&self) -> Result<IpcMessage, ChannelError> {
443 let result = self.recv_waitq.wait_until(|| {
444 let msg_opt = self.queue.pop();
445 if let Some(msg) = msg_opt {
446 return Some(Ok(msg));
447 }
448 // P2 fix: check for pending signals to avoid livelock.
449 if crate::process::signal::has_pending_signals() {
450 return Some(Err(ChannelError::Interrupted));
451 }
452 if self.destroyed.load(Ordering::Acquire) {
453 return Some(Err(ChannelError::Disconnected));
454 }
455 None
456 });
457
458 if result.is_ok() {
459 self.send_waitq.wake_one();
460 }
461 result
462 }
463
464 /// Try to receive without blocking.
465 ///
466 /// Returns `Err(WouldBlock)` if the queue is empty, or
467 /// `Err(Disconnected)` if the channel is destroyed and empty.
468 pub fn try_recv(&self) -> Result<IpcMessage, ChannelError> {
469 let msg_opt = self.queue.pop();
470 if let Some(msg) = msg_opt {
471 self.send_waitq.wake_one();
472 return Ok(msg);
473 }
474 if self.destroyed.load(Ordering::Acquire) {
475 return Err(ChannelError::Disconnected);
476 }
477 Err(ChannelError::WouldBlock)
478 }
479
480 /// Mark the channel as destroyed and wake all blocked tasks.
481 ///
482 /// Called when the last userspace handle is closed. Subsequent send/recv
483 /// operations on any still-held reference return `Disconnected`.
484 pub fn destroy(&self) {
485 self.destroyed.store(true, Ordering::Release);
486 self.send_waitq.wake_all();
487 self.recv_waitq.wake_all();
488 }
489
490 /// Returns `true` if the channel has been destroyed.
491 pub fn is_destroyed(&self) -> bool {
492 self.destroyed.load(Ordering::Acquire)
493 }
494
495 /// Returns the current number of messages buffered.
496 pub fn len(&self) -> usize {
497 self.queue.len()
498 }
499
500 /// Returns `true` if the queue is empty.
501 pub fn is_empty(&self) -> bool {
502 self.len() == 0
503 }
504
505 /// Returns whether full.
506 pub fn is_full(&self) -> bool {
507 self.queue.is_full()
508 }
509
510 /// Returns whether this can send.
511 pub fn can_send(&self) -> bool {
512 !self.destroyed.load(Ordering::Acquire) && !self.queue.is_full()
513 }
514}
515
516// Global channel registry : userspace syscall surface
517/// Unique identifier for a [`SyncChan`] in the global registry.
518#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
519pub struct ChanId(pub u64);
520
521impl ChanId {
522 /// Returns this as u64.
523 pub fn as_u64(self) -> u64 {
524 self.0
525 }
526 /// Builds this from u64.
527 pub fn from_u64(raw: u64) -> Self {
528 ChanId(raw)
529 }
530}
531
532impl core::fmt::Display for ChanId {
533 /// Performs the fmt operation.
534 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
535 write!(f, "{}", self.0)
536 }
537}
538
539/// Next channel ID to allocate.
540static NEXT_CHAN_ID: AtomicU64 = AtomicU64::new(1);
541
542/// Global registry: `ChanId => Arc<SyncChan>`.
543static CHANNELS: SpinLock<Option<BTreeMap<ChanId, Arc<SyncChan>>>> = SpinLock::new(None);
544
545/// Performs the ensure registry operation.
546fn ensure_registry(guard: &mut Option<BTreeMap<ChanId, Arc<SyncChan>>>) {
547 if guard.is_none() {
548 *guard = Some(BTreeMap::new());
549 }
550}
551
552/// Create a new [`SyncChan`] with the given capacity and register it.
553///
554/// Returns the [`ChanId`] to be returned to the creating task as a handle.
555pub fn create_channel(capacity: usize) -> ChanId {
556 let id = ChanId(NEXT_CHAN_ID.fetch_add(1, Ordering::Relaxed));
557 let chan = Arc::new(SyncChan::new(capacity));
558 let mut reg = CHANNELS.lock();
559 ensure_registry(&mut *reg);
560 reg.as_mut().unwrap().insert(id, chan);
561 log::debug!("IPC: created sync-channel {} (cap={})", id, capacity);
562 id
563}
564
565/// Look up a channel by ID. Returns a cloned `Arc<SyncChan>` if found.
566pub fn get_channel(id: ChanId) -> Option<Arc<SyncChan>> {
567 let reg = CHANNELS.lock();
568 reg.as_ref().and_then(|map| map.get(&id).cloned())
569}
570
571/// Destroy a channel: remove it from the registry and wake all waiters.
572///
573/// After this call, any thread still holding an `Arc<SyncChan>` to the
574/// same channel will see `Err(Disconnected)` on the next send/recv.
575pub fn destroy_channel(id: ChanId) -> Result<(), ChannelError> {
576 let chan = {
577 let mut reg = CHANNELS.lock();
578 let map = reg.as_mut().ok_or(ChannelError::Disconnected)?;
579 map.remove(&id).ok_or(ChannelError::Disconnected)?
580 };
581 chan.destroy();
582 log::debug!("IPC: destroyed sync-channel {}", id);
583 Ok(())
584}