Skip to content

Commit

Permalink
Implement readable/writable events
Browse files Browse the repository at this point in the history
  • Loading branch information
roblabla committed Aug 16, 2019
1 parent 6cb25e0 commit 27c7901
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 11 deletions.
76 changes: 74 additions & 2 deletions kernel/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
//! sleep (deregistering it from the scheduler). When the event is triggered,
//! the scheduler will wake the process up, allowing it work.
use core::sync::atomic::{AtomicUsize, Ordering};
use core::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
use core::fmt::Debug;
use alloc::sync::Arc;
use crate::sync::SpinLockIRQ;
use crate::sync::{SpinLock, SpinLockIRQ};
use alloc::vec::Vec;
use crate::error::UserspaceError;
use crate::process::ThreadStruct;
Expand Down Expand Up @@ -125,6 +125,78 @@ where
}
}

/// The underlying shared object of a [ReadableEvent]/[WritableEvent].
#[derive(Debug)]
struct Event {
/// The state determines whether the event is signaled or not. When it is true,
/// the event is signaled, and calls to WaitSynchronization with this event
/// will immediately return.
state: AtomicBool,
/// List of processes waiting on this IRQ. When this IRQ is triggered, all
/// those processes will be rescheduled.
waiting_processes: SpinLock<Vec<Arc<ThreadStruct>>>
}

/// Create a new pair of [WritableEvent]/[ReadableEvent].
pub fn new_pair() -> (WritableEvent, ReadableEvent) {
let event = Arc::new(Event {
state: AtomicBool::new(false),
waiting_processes: SpinLock::new(Vec::new())
});

(WritableEvent { parent: event.clone() }, ReadableEvent { parent: event })
}

/// The readable part of an event. The user shall use this end to verify if the
/// event is signaled, and wait for the signaling through wait_synchronization.
/// The user can also use this handle to clear the signaled state through
/// [ReadableEvent::clear_signal()].
#[derive(Debug, Clone)]
pub struct ReadableEvent {
/// Pointer to the shared event representation.
parent: Arc<Event>
}

impl ReadableEvent {
/// Clears the signaled state.
pub fn clear_signal(&self) {
self.parent.state.store(false, Ordering::SeqCst);
}
}

impl Waitable for ReadableEvent {
fn is_signaled(&self) -> bool {
self.parent.state.load(Ordering::SeqCst)
}
fn register(&self) {
self.parent.waiting_processes.lock().push(scheduler::get_current_thread());
}
}

/// The writable part of an event. The user shall use this end to signal (and
/// wake up threads waiting on the event).
#[derive(Debug, Clone)]
pub struct WritableEvent {
/// Pointer to the shared event representation.
parent: Arc<Event>
}

impl WritableEvent {
/// Signals the event, setting its state to signaled and waking up any
/// thread waiting on its value.
pub fn signal(&self) {
self.parent.state.store(true, Ordering::SeqCst);
let mut processes = self.parent.waiting_processes.lock();
while let Some(process) = processes.pop() {
scheduler::add_to_schedule_queue(process);
}
}
/// Clears the signaled state.
pub fn clear_signal(&self) {
self.parent.state.store(false, Ordering::SeqCst);
}
}

/// An event waiting for an IRQ.
///
/// When created, is_signaled is called and the IRQ was triggered, it will
Expand Down
3 changes: 3 additions & 0 deletions kernel/src/i386/interrupt_service_routines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,8 @@ fn syscall_interrupt_dispatcher(_exception_name: &'static str, hwcontext: &mut U
(true, nr::StartThread) => hwcontext.apply0(start_thread(x0 as _)),
(true, nr::ExitThread) => hwcontext.apply0(exit_thread()),
(true, nr::SleepThread) => hwcontext.apply0(sleep_thread(x0)),
(true, nr::SignalEvent) => hwcontext.apply0(signal_event(x0 as _)),
(true, nr::ClearEvent) => hwcontext.apply0(clear_event(x0 as _)),
(true, nr::MapSharedMemory) => hwcontext.apply0(map_shared_memory(x0 as _, x1 as _, x2 as _, x3 as _)),
(true, nr::UnmapSharedMemory) => hwcontext.apply0(unmap_shared_memory(x0 as _, x1 as _, x2 as _)),
(true, nr::CloseHandle) => hwcontext.apply0(close_handle(x0 as _)),
Expand All @@ -936,6 +938,7 @@ fn syscall_interrupt_dispatcher(_exception_name: &'static str, hwcontext: &mut U
(true, nr::CreateSession) => hwcontext.apply2(create_session(x0 != 0, x1 as _)),
(true, nr::AcceptSession) => hwcontext.apply1(accept_session(x0 as _)),
(true, nr::ReplyAndReceiveWithUserBuffer) => hwcontext.apply1(reply_and_receive_with_user_buffer(UserSpacePtrMut::from_raw_parts_mut(x0 as _, x1), UserSpacePtr::from_raw_parts(x2 as _, x3), x4 as _, x5)),
(true, nr::CreateEvent) => hwcontext.apply2(create_event()),
(true, nr::CreateSharedMemory) => hwcontext.apply1(create_shared_memory(x0 as _, x1 as _, x2 as _)),
(true, nr::CreateInterruptEvent) => hwcontext.apply1(create_interrupt_event(x0, x1 as u32)),
(true, nr::QueryPhysicalAddress) => hwcontext.apply4(query_physical_address(x0 as _)),
Expand Down
33 changes: 27 additions & 6 deletions kernel/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
use crate::stack::KernelStack;
use crate::i386::process_switch::*;
use crate::paging::process_memory::ProcessMemory;
use alloc::boxed::Box;
use alloc::sync::{Arc, Weak};
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use crate::event::Waitable;
use crate::event::{IRQEvent, ReadableEvent, WritableEvent, Waitable};
use crate::sync::{SpinLockIRQ, SpinLock, Mutex};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use core::fmt::{self, Debug};
Expand Down Expand Up @@ -149,9 +148,13 @@ pub struct ThreadStruct {
/// will have relevant behavior for all the different kind of handles.
#[derive(Debug)]
pub enum Handle {
/// An event on which we can wait. Could be an IRQ, or a user-generated
/// event.
ReadableEvent(Box<dyn Waitable>),
/// A special ReadableEvent that is triggered automatically when an IRQ is
/// triggered.
InterruptEvent(IRQEvent),
/// An event on which we can wait, triggered by a WritableEvent.
ReadableEvent(ReadableEvent),
/// Trigger for an associated ReadableEvent.
WritableEvent(WritableEvent),
/// The server side of an IPC port. See [crate::ipc::port] for more information.
ServerPort(ServerPort),
/// The client side of an IPC port. See [crate::ipc::port] for more information.
Expand All @@ -176,7 +179,8 @@ impl Handle {
/// Gets the handle as a [Waitable], or return a `UserspaceError` if the handle cannot be waited on.
pub fn as_waitable(&self) -> Result<&dyn Waitable, UserspaceError> {
match *self {
Handle::ReadableEvent(ref waitable) => Ok(&**waitable),
Handle::ReadableEvent(ref waitable) => Ok(waitable),
Handle::InterruptEvent(ref waitable) => Ok(waitable),
Handle::ServerPort(ref serverport) => Ok(serverport),
Handle::ServerSession(ref serversession) => Ok(serversession),
_ => Err(UserspaceError::InvalidHandle),
Expand Down Expand Up @@ -228,6 +232,23 @@ impl Handle {
}
}

/// Casts the handle as an Arc<[WritableEvent]> if the handle is a
/// [WritableEvent], or returns a `UserspaceError`.
pub fn as_writable_event(&self) -> Result<WritableEvent, UserspaceError> {
match self {
Handle::WritableEvent(event) => Ok(event.clone()),
_ => Err(UserspaceError::InvalidHandle)
}
}

/// Casts the handle as an Arc<[ReadableEvent]>, or returns a `UserspaceError`.
pub fn as_readable_event(&self) -> Result<ReadableEvent, UserspaceError> {
match self {
Handle::ReadableEvent(event) => Ok(event.clone()),
_ => Err(UserspaceError::InvalidHandle)
}
}

/// Casts the handle as an Arc<RwLock<Vec<[PhysicalMemRegion]>>>, or returns a
/// `UserspaceError`.
pub fn as_shared_memory(&self) -> Result<Arc<RwLock<Vec<PhysicalMemRegion>>>, UserspaceError> {
Expand Down
44 changes: 42 additions & 2 deletions kernel/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use crate::paging::mapping::MappingFrames;
use crate::process::{Handle, ThreadStruct, ProcessStruct};
use crate::event::{self, Waitable};
use crate::scheduler::{self, get_current_thread, get_current_process};
use alloc::boxed::Box;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
Expand All @@ -25,6 +24,7 @@ use failure::Backtrace;
use sunrise_libkern::{MemoryInfo, MemoryAttributes, MemoryPermissions, MemoryType};
use bit_field::BitArray;
use crate::i386::gdt::{GDT, GdtIndex};
use core::convert::TryFrom;

/// Resize the heap of a process, just like a brk.
/// It can both expand, and shrink the heap.
Expand Down Expand Up @@ -103,7 +103,7 @@ pub fn create_interrupt_event(irq_num: usize, _flag: u32) -> Result<usize, Users
return Err(UserspaceError::NoSuchEntry);
}
}
let hnd = curproc.phandles.lock().add_handle(Arc::new(Handle::ReadableEvent(Box::new(event::wait_event(irq_num as u8)))));
let hnd = curproc.phandles.lock().add_handle(Arc::new(Handle::InterruptEvent(event::wait_event(irq_num as u8))));
Ok(hnd as _)
}

Expand Down Expand Up @@ -424,6 +424,35 @@ pub fn sleep_thread(nanos: usize) -> Result<(), UserspaceError> {
}
}

/// Sets the "signaled" state of an event. Calling this on an unsignalled event
/// will cause any thread waiting on this event through [wait_synchronization()]
/// to wake up. Any future calls to [wait_synchronization()] with this handle
/// will immediately return - the user has to clear the "signaled" state through
/// [clear_event()].
///
/// Takes either a [ReadableEvent] or a [WritableEvent].
pub fn signal_event(handle: u32) -> Result<(), UserspaceError> {
let proc = scheduler::get_current_process();
proc.phandles.lock().get_handle(handle)?.as_writable_event()?.signal();
Ok(())
}

/// Clear the "signaled" state of an event. After calling this on a signaled
/// event, [wait_synchronization()] on this handle will wait until
/// [signal_event()] is called once again.
///
/// Takes either a [ReadableEvent] or a [WritableEvent].
pub fn clear_event(handle: u32) -> Result<(), UserspaceError> {
let proc = scheduler::get_current_process();
let handle = proc.phandles.lock().get_handle(handle)?;
match &*handle {
Handle::ReadableEvent(event) => event.clear_signal(),
Handle::WritableEvent(event) => event.clear_signal(),
_ => Err(UserspaceError::InvalidHandle)?
}
Ok(())
}

/// Create a new Port pair. Those ports are linked to each-other: The server will
/// receive connections from the client.
pub fn create_port(max_sessions: u32, _is_light: bool, _name_ptr: UserSpacePtr<[u8; 12]>) -> Result<(usize, usize), UserspaceError>{
Expand Down Expand Up @@ -551,6 +580,17 @@ pub fn create_session(_is_light: bool, _unk: usize) -> Result<(usize, usize), Us
Ok((serverhnd as _, clienthnd as _))
}

/// Create a [WritableEvent]/[ReadableEvent] pair. Signals on the
/// [WritableEvent] will cause threads waiting on the [ReadableEvent] to wake
/// up until the signal is cleared/reset.
pub fn create_event() -> Result<(usize, usize), UserspaceError> {
let (writable, readable) = crate::event::new_pair();
let curproc = scheduler::get_current_process();
let readable = curproc.phandles.lock().add_handle(Arc::new(Handle::ReadableEvent(readable)));
let writable = curproc.phandles.lock().add_handle(Arc::new(Handle::WritableEvent(writable)));
Ok((usize::try_from(writable).unwrap(), usize::try_from(readable).unwrap()))
}

/// Maps a physical region in the address space of the process.
///
/// # Returns
Expand Down
34 changes: 34 additions & 0 deletions libuser/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,32 @@ pub fn sleep_thread(nanos: usize) -> Result<(), KernelError> {
}
}

/// Sets the "signaled" state of an event. Calling this on an unsignalled event
/// will cause any thread waiting on this event through [wait_synchronization()]
/// to wake up. Any future calls to [wait_synchronization()] with this handle
/// will immediately return - the user has to clear the "signaled" state through
/// [clear_event()].
///
/// Takes either a [ReadableEvent] or a [WritableEvent].
pub fn signal_event(event: &WritableEvent) -> Result<(), KernelError> {
unsafe {
syscall(nr::SignalEvent, (event.0).0.get() as _, 0, 0, 0, 0, 0)?;
Ok(())
}
}

/// Clear the "signaled" state of an event. After calling this on a signaled
/// event, [wait_synchronization()] on this handle will wait until
/// [signal_event()] is called once again.
///
/// Takes either a [ReadableEvent] or a [WritableEvent].
pub(crate) fn clear_event(event: HandleRef) -> Result<(), KernelError> {
unsafe {
syscall(nr::ClearEvent, event.inner.get() as _, 0, 0, 0, 0, 0)?;
Ok(())
}
}

/// Creates a shared memory handle.
///
/// Allocates the given size bytes of physical memory to back the SharedMemory.
Expand Down Expand Up @@ -366,6 +392,14 @@ pub fn reply_and_receive_with_user_buffer(buf: &mut [u8], handles: &[HandleRef<'
}
}

/// Create a [ReadableEvent]/[WritableEvent] pair.
pub fn create_event() -> Result<(WritableEvent, ReadableEvent), KernelError> {
unsafe {
let (wevent, revent, ..) = syscall(nr::CreateEvent, 0, 0, 0, 0, 0, 0)?;
Ok((WritableEvent(Handle::new(wevent as _)), ReadableEvent(Handle::new(revent as _))))
}
}

/// Create a waitable object for the given IRQ number.
///
/// Note that the process needs to be authorized to listen for the given IRQ.
Expand Down
35 changes: 34 additions & 1 deletion libuser/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,44 @@ pub struct HandleRef<'a> {
lifetime: PhantomData<&'a Handle>
}

/// An awaitable event handle, such as an IRQ event.
/// A handle on an IRQ event.
#[repr(transparent)]
#[derive(Debug)]
pub struct IRQEvent(pub Handle);

/// The readable part of an event. The user shall use this end to verify if the
/// event is signaled, and wait for the signaling through wait_synchronization.
/// The user can also use this handle to clear the signaled state through
/// [ReadableEvent::clear_signal()].
#[repr(transparent)]
#[derive(Debug)]
pub struct ReadableEvent(pub Handle);

impl ReadableEvent {
/// Clears the signaled state.
pub fn clear_signal(&self) -> Result<(), KernelError> {
syscalls::clear_event(self.0.as_ref())
}
}


/// The writable part of an event. The user shall use this end to signal (and
/// wake up threads waiting on the event).
pub struct WritableEvent(pub Handle);

impl WritableEvent {
/// Clears the signaled state.
pub fn clear_signal(&self) -> Result<(), KernelError> {
syscalls::clear_event(self.0.as_ref())
}

/// Signals the event, setting its state to signaled and waking up any
/// thread waiting on its value.
pub fn signal(&self) -> Result<(), KernelError> {
syscalls::signal_event(self)
}
}

/// The client side of an IPC session.
///
/// Usually obtained by connecting to a service through the sm: service manager.
Expand Down

0 comments on commit 27c7901

Please sign in to comment.