strat9_kernel/ipc/
port.rs1use super::message::IpcMessage;
8use crate::{
9 process::TaskId,
10 sync::{SpinLock, WaitQueue},
11};
12use alloc::{collections::BTreeMap, sync::Arc};
13use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
14use crossbeam_queue::ArrayQueue;
15
16const PORT_QUEUE_CAPACITY: usize = 16;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
21pub struct PortId(pub u64);
22
23impl PortId {
24 pub fn as_u64(self) -> u64 {
26 self.0
27 }
28
29 pub fn from_u64(raw: u64) -> Self {
31 PortId(raw)
32 }
33}
34
35impl core::fmt::Display for PortId {
36 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38 write!(f, "{}", self.0)
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
43pub enum IpcError {
44 #[error("port not found")]
45 PortNotFound,
46 #[error("not owner of port")]
47 NotOwner,
48 #[error("port destroyed")]
49 PortDestroyed,
50}
51
52pub struct Port {
54 pub id: PortId,
56 pub owner: TaskId,
58 queue: ArrayQueue<IpcMessage>,
60 destroyed: AtomicBool,
62 send_waitq: WaitQueue,
64 recv_waitq: WaitQueue,
66}
67
68impl Port {
69 fn new(id: PortId, owner: TaskId) -> Self {
71 Port {
72 id,
73 owner,
74 queue: ArrayQueue::new(PORT_QUEUE_CAPACITY),
75 destroyed: AtomicBool::new(false),
76 send_waitq: WaitQueue::new(),
77 recv_waitq: WaitQueue::new(),
78 }
79 }
80
81 pub fn send(&self, msg: IpcMessage) -> Result<(), IpcError> {
87 let result = self.send_waitq.wait_until(|| {
88 if self.destroyed.load(Ordering::Acquire) {
89 return Some(Err(IpcError::PortDestroyed));
90 }
91 match self.queue.push(msg) {
92 Ok(()) => Some(Ok(())),
93 Err(_) => None,
94 }
95 });
96 if result.is_ok() {
97 self.recv_waitq.wake_one();
98 }
99 result
100 }
101
102 pub fn recv(&self) -> Result<IpcMessage, IpcError> {
108 let result = self.recv_waitq.wait_until(|| {
109 if let Some(msg) = self.queue.pop() {
110 return Some(Ok(msg));
111 }
112 if self.destroyed.load(Ordering::Acquire) {
113 return Some(Err(IpcError::PortDestroyed));
114 }
115 None
116 });
117 if result.is_ok() {
118 self.send_waitq.wake_one();
119 }
120 result
121 }
122
123 pub fn try_send(&self, msg: IpcMessage) -> Result<(), IpcError> {
128 match self.queue.push(msg) {
129 Ok(()) => {
130 self.recv_waitq.wake_one();
131 Ok(())
132 }
133 Err(_) => Err(IpcError::PortNotFound), }
135 }
136
137 pub fn try_recv(&self) -> Result<Option<IpcMessage>, IpcError> {
142 if let Some(msg) = self.queue.pop() {
143 self.send_waitq.wake_one();
144 return Ok(Some(msg));
145 }
146 if self.destroyed.load(Ordering::Acquire) {
147 return Err(IpcError::PortDestroyed);
148 }
149 Ok(None)
150 }
151
152 fn destroy(&self) {
154 self.destroyed.store(true, Ordering::Release);
155 self.send_waitq.wake_all();
156 self.recv_waitq.wake_all();
157 }
158
159 pub fn has_messages(&self) -> bool {
161 !self.queue.is_empty()
162 }
163
164 pub fn can_send(&self) -> bool {
166 !self.destroyed.load(Ordering::Acquire) && !self.queue.is_full()
167 }
168
169 pub fn is_destroyed(&self) -> bool {
171 self.destroyed.load(Ordering::Acquire)
172 }
173}
174
175static NEXT_PORT_ID: AtomicU64 = AtomicU64::new(1);
181
182static PORTS: SpinLock<Option<BTreeMap<PortId, Arc<Port>>>> = SpinLock::new(None);
184
185fn ensure_registry(guard: &mut Option<BTreeMap<PortId, Arc<Port>>>) {
187 if guard.is_none() {
188 *guard = Some(BTreeMap::new());
189 }
190}
191
192pub fn create_port(owner: TaskId) -> PortId {
194 let id = PortId(NEXT_PORT_ID.fetch_add(1, Ordering::Relaxed));
195 let port = Arc::new(Port::new(id, owner));
196
197 let mut registry = PORTS.lock();
198 ensure_registry(&mut *registry);
199 registry.as_mut().unwrap().insert(id, port);
200
201 log::debug!("IPC: created port {} (owner={})", id, owner);
202 id
203}
204
205pub fn get_port(id: PortId) -> Option<Arc<Port>> {
207 let registry = PORTS.lock();
208 registry.as_ref().and_then(|map| map.get(&id).cloned())
209}
210
211pub fn destroy_port(id: PortId, caller: TaskId) -> Result<(), IpcError> {
215 let port = {
216 let mut registry = PORTS.lock();
217 let map = registry.as_mut().ok_or(IpcError::PortNotFound)?;
218 let port = map.get(&id).ok_or(IpcError::PortNotFound)?;
219 if port.owner != caller {
220 return Err(IpcError::NotOwner);
221 }
222 let port = port.clone();
223 map.remove(&id);
224 port
225 };
226
227 port.destroy();
228 log::debug!("IPC: destroyed port {} (by task {})", id, caller);
229 Ok(())
230}
231
232pub fn cleanup_ports_for_task(owner: TaskId) {
241 super::reply::cancel_replies_waiting_on(owner);
242
243 let owned: alloc::vec::Vec<Arc<Port>> = {
244 let mut registry = PORTS.lock();
245 let Some(map) = registry.as_mut() else { return };
246 let ids: alloc::vec::Vec<PortId> = map
247 .iter()
248 .filter(|(_, p)| p.owner == owner)
249 .map(|(id, _)| *id)
250 .collect();
251 let mut ports = alloc::vec::Vec::with_capacity(ids.len());
252 for id in ids {
253 if let Some(p) = map.remove(&id) {
254 ports.push(p);
255 }
256 }
257 ports
258 };
259
260 for port in owned {
261 port.destroy();
262 while let Some(msg) = port.queue.pop() {
263 let sender = TaskId::from_u64(msg.sender);
264 if sender == owner {
265 continue;
266 }
267 let mut err_reply = IpcMessage::new(0x80);
268 let epipe: u32 = 32;
269 err_reply.payload[0..4].copy_from_slice(&epipe.to_le_bytes());
270 let _ = super::reply::deliver_reply(sender, err_reply);
271 }
272 }
273}