1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
use super::{alloc_kernel_object_id, KernelObject, KernelObjectId};
use alloc::{
collections::VecDeque,
sync::{Arc, Weak},
vec::Vec,
};
use poplar::syscall::{GetMessageError, SendMessageError, CHANNEL_MAX_NUM_HANDLES};
use spin::Mutex;
use tracing::warn;
pub struct ChannelEnd {
pub id: KernelObjectId,
pub owner: KernelObjectId,
messages: Mutex<VecDeque<Message>>,
/// The other end of the channel. If this is `None`, the channel's messages come from the kernel.
other_end: Option<Weak<ChannelEnd>>,
}
impl ChannelEnd {
pub fn new_channel(owner: KernelObjectId) -> (Arc<ChannelEnd>, Arc<ChannelEnd>) {
let mut end_a = Arc::new(ChannelEnd {
id: alloc_kernel_object_id(),
owner,
messages: Mutex::new(VecDeque::new()),
other_end: Some(Weak::default()),
});
let end_b = Arc::new(ChannelEnd {
id: alloc_kernel_object_id(),
owner,
messages: Mutex::new(VecDeque::new()),
other_end: Some(Arc::downgrade(&end_a)),
});
// TODO: is there a nicer way of doing this?
unsafe {
Arc::get_mut_unchecked(&mut end_a).other_end = Some(Arc::downgrade(&end_b));
}
(end_a, end_b)
}
pub fn new_kernel_channel(owner: KernelObjectId) -> Arc<ChannelEnd> {
Arc::new(ChannelEnd {
id: alloc_kernel_object_id(),
owner,
messages: Mutex::new(VecDeque::new()),
other_end: None,
})
}
/// Add a message *to* this `ChannelEnd`. Use `send` if you want to send a message *through* this
/// `ChannelEnd` (i.e. to the other end of the Channel).
pub fn add_message(&self, message: Message) {
self.messages.lock().push_back(message);
}
/// Send a message through this `ChannelEnd`, to be received by the other end. If this is a kernel channel, the
/// message is discarded.
pub fn send(&self, message: Message) -> Result<(), SendMessageError> {
if let Some(ref other_end) = self.other_end {
match other_end.upgrade() {
Some(other_end) => {
other_end.add_message(message);
Ok(())
}
None => Err(SendMessageError::OtherEndDisconnected),
}
} else {
warn!("Discarding message sent down kernel channel");
Ok(())
}
}
/// Try to "receive" a message from this `ChannelEnd`, potentially removing it from the queue. Note that this
/// keeps a lock over the message queue while the passed function is called - if the handling of the message
/// fails (for example, the buffer to put it into is too small), the passed function can return it with
/// `Err((message, some_error))`, and the message will be placed back into the queue (preserving message
/// order), and the error will be returned.
pub fn receive<F, R>(&self, f: F) -> Result<R, GetMessageError>
where
F: FnOnce(Message) -> Result<R, (Message, GetMessageError)>,
{
let mut message_queue = self.messages.lock();
match f(message_queue.pop_front().ok_or(GetMessageError::NoMessage)?) {
Ok(value) => Ok(value),
Err((message, err)) => {
message_queue.push_front(message);
Err(err)
}
}
}
}
impl KernelObject for ChannelEnd {
fn id(&self) -> KernelObjectId {
self.id
}
}
pub struct Message {
pub bytes: Vec<u8>,
/// The actual objects extracted from the handles transferred by a message. When a task receives this message,
/// these objects are added to that task, and the new handles are put into the message. The non-`None` entries
/// of this array must be contiguous - there cannot be a `None` entry before more non-`None` entries.
pub handle_objects: [Option<Arc<dyn KernelObject>>; CHANNEL_MAX_NUM_HANDLES],
}
impl Message {
pub fn num_handles(&self) -> usize {
self.handle_objects.iter().fold(0, |n, ref handle| if handle.is_some() { n + 1 } else { n })
}
}