Skip to main content

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}