From e10ed70ccca7f640eb9a6954dc5cc30e69359538 Mon Sep 17 00:00:00 2001 From: pitust Date: Sat, 19 Mar 2022 13:57:21 +0000 Subject: [PATCH 1/2] aipc! --- userland/Cargo.lock | 47 +++- userland/Cargo.toml | 2 +- userland/apps/aipc_test/Cargo.toml | 12 ++ userland/apps/aipc_test/src/main.rs | 98 +++++++++ userland/libs/aipc-proc/Cargo.toml | 13 ++ userland/libs/aipc-proc/src/lib.rs | 275 ++++++++++++++++++++++++ userland/libs/aipc/Cargo.toml | 15 ++ userland/libs/aipc/src/async_runtime.rs | 242 +++++++++++++++++++++ userland/libs/aipc/src/lib.rs | 137 ++++++++++++ 9 files changed, 838 insertions(+), 3 deletions(-) create mode 100644 userland/apps/aipc_test/Cargo.toml create mode 100644 userland/apps/aipc_test/src/main.rs create mode 100644 userland/libs/aipc-proc/Cargo.toml create mode 100644 userland/libs/aipc-proc/src/lib.rs create mode 100644 userland/libs/aipc/Cargo.toml create mode 100644 userland/libs/aipc/src/async_runtime.rs create mode 100644 userland/libs/aipc/src/lib.rs diff --git a/userland/Cargo.lock b/userland/Cargo.lock index 1832dc249f0..b8f5258acb7 100644 --- a/userland/Cargo.lock +++ b/userland/Cargo.lock @@ -50,6 +50,40 @@ dependencies = [ "memchr", ] +[[package]] +name = "aipc" +version = "0.1.0" +dependencies = [ + "aero_ipc", + "aero_syscall", + "aipc-proc", + "hashbrown 0.11.2", + "postcard", + "serde", + "spin 0.9.2", + "std", +] + +[[package]] +name = "aipc-proc" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "aipc_test" +version = "0.1.0" +dependencies = [ + "aero_syscall", + "aipc", + "postcard", + "spin 0.9.2", + "std", +] + [[package]] name = "atomic-polyfill" version = "0.1.5" @@ -159,6 +193,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.12.0" @@ -428,7 +471,7 @@ version = "0.1.0" dependencies = [ "aero_ipc", "aero_syscall", - "hashbrown", + "hashbrown 0.12.0", "spin 0.9.2", "std", ] @@ -496,7 +539,7 @@ version = "0.1.0" dependencies = [ "aero_ipc", "aero_syscall", - "hashbrown", + "hashbrown 0.12.0", "spin 0.9.2", "std", ] diff --git a/userland/Cargo.toml b/userland/Cargo.toml index 1a52adcab02..06137a43770 100644 --- a/userland/Cargo.toml +++ b/userland/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["apps/*", "servers/*"] +members = ["apps/*", "servers/*", "libs/*"] [profile.release] debug = true diff --git a/userland/apps/aipc_test/Cargo.toml b/userland/apps/aipc_test/Cargo.toml new file mode 100644 index 00000000000..29e4e1495a8 --- /dev/null +++ b/userland/apps/aipc_test/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "aipc_test" +version = "0.1.0" +edition = "2021" +authors = ["pitust "] + +[dependencies] +std = { path = "../../libs/aero_std" } +aero_syscall = { path = "../../../src/aero_syscall" } +aipc = { path = "../../libs/aipc" } +postcard = { version = "0.7.3", features = ["alloc"] } +spin = "0.9" diff --git a/userland/apps/aipc_test/src/main.rs b/userland/apps/aipc_test/src/main.rs new file mode 100644 index 00000000000..aa379714193 --- /dev/null +++ b/userland/apps/aipc_test/src/main.rs @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021-2022 The Aero Project Developers. + * + * This file is part of The Aero Project. + * + * Aero is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Aero is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aero. If not, see . + */ +use core::sync::atomic::{AtomicUsize, Ordering, AtomicBool}; + +use aero_syscall::{sys_fork, sys_exit}; +use aipc::async_runtime::Listener; + +#[aipc::def("TestObj")] +trait TestObj { + async fn create() -> TestObj; + async fn foo(&self); + async fn kill(&self); +} +#[aipc::def("ServerControlObj")] +trait ServerControlObj { + async fn create() -> ServerControlObj; + async fn can_control(&self) -> bool; + async fn quit(&self); +} + +static COUNTER: AtomicUsize = AtomicUsize::new(1); + +struct NoData { + data: usize, +} + +#[aipc::object(TestObjSrv as TestObj)] +impl NoData { + fn create() -> NoData { + return NoData { + data: COUNTER.fetch_add(1, Ordering::SeqCst), + }; + } + fn foo(&self) { + println!("Hello from obj {}!", self.data); + } +} + +static SCS_PERMIT_ONCE: AtomicBool = AtomicBool::new(true); +pub struct ServerControlState { + permitted: bool +} +#[aipc::object(ServerControlSrv as ServerControlObj)] +impl ServerControlState { + pub fn create() -> ServerControlState { + return ServerControlState { + permitted: SCS_PERMIT_ONCE.swap(false, Ordering::SeqCst) + }; + } + pub fn can_control(&self) -> bool { + self.permitted + } + pub fn quit(&self) { + if self.permitted { + println!("[ServerControlSrv] exiting!"); + aipc::async_runtime::spawn(async { + sys_exit(0); + }); + } + } +} + + +fn main() { + let mut rt = aipc::async_runtime::AsyncRuntime::new(); + rt.spawn(async { + let pid = sys_fork().unwrap(); + if pid == 0 { + // server + ServerControlSrv::listen(); + TestObjSrv::listen(); + } else { + // client + let ctl = ServerControlObj::create(pid).await; + let o = TestObj::create(pid).await; + o.foo().await; + ctl.quit().await; + sys_exit(0); + } + }); + rt.run(); +} diff --git a/userland/libs/aipc-proc/Cargo.toml b/userland/libs/aipc-proc/Cargo.toml new file mode 100644 index 00000000000..7abd7437fcc --- /dev/null +++ b/userland/libs/aipc-proc/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "aipc-proc" +version = "0.1.0" +edition = "2021" +authors = ["pitust "] + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "1.0.82", features = ["proc-macro", "full"] } +quote = "1.0.10" +proc-macro2 = "1.0.36" diff --git a/userland/libs/aipc-proc/src/lib.rs b/userland/libs/aipc-proc/src/lib.rs new file mode 100644 index 00000000000..d3175ce0ad5 --- /dev/null +++ b/userland/libs/aipc-proc/src/lib.rs @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2021-2022 The Aero Project Developers. + * + * This file is part of The Aero Project. + * + * Aero is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Aero is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aero. If not, see . + */ +use proc_macro::TokenStream; +use syn::parse::{Parse, ParseStream}; +use syn::{ItemImpl, ItemTrait, Result, Token, Type}; + +struct ObjectImpl { + pub name: Box, + pub _as_token: Token![as], + pub ty: Box, +} +impl Parse for ObjectImpl { + fn parse(input: ParseStream) -> Result { + Ok(ObjectImpl { + name: input.parse()?, + _as_token: input.parse()?, + ty: input.parse()?, + }) + } +} + +#[proc_macro_attribute] +pub fn def(attr: TokenStream, input: TokenStream) -> TokenStream { + let input: ItemTrait = syn::parse_macro_input!(input as ItemTrait); + let attr: syn::LitStr = syn::parse_macro_input!(attr as syn::LitStr); + + let name = input.ident; + + // async fn foo(&self); + // async fn create() -> TestObj; + + let methods = input + .items + .into_iter() + .filter_map(|a| match a { + syn::TraitItem::Method(m) => Some(m), + _ => None, + }) + .collect::>(); + + let mut methods2 = vec![]; + for method in methods.into_iter() { + let inputs = method.sig.inputs.clone(); + let recievers = inputs + .iter() + .filter_map(|a| match a { + syn::FnArg::Receiver(_) => None, + syn::FnArg::Typed(r) => Some(r.ty.clone()), + }) + .collect::>(); + + let mut args2 = vec![]; + let mut serialize_args2 = vec![]; + let mut i = -1; + for recv in recievers { + i += 1; + let ident = proc_macro2::Ident::new(&format!("arg_{}", i), proc_macro2::Span::call_site()); + args2.push(quote::quote! { + #ident: #recv + }); + serialize_args2.push(quote::quote! { + #ident + }); + } + + let func_name = method.sig.ident; + let func_name_str = func_name.to_string(); + let ret = method.sig.output; + let output = if inputs.len() == 0 + || match &inputs[0] { + syn::FnArg::Receiver(_) => false, + syn::FnArg::Typed(_) => true, + } { + // static method + quote::quote! { + // TODO: i (pitust) am not happy abut these unwraps + pub async fn #func_name(target_pid: usize, #(#args2),*) #ret { + let (id, fut) = ::aipc::async_runtime::alloc_reply_id(); + let req = ::aipc::serialize_buffer(( + id - 1, + #func_name_str, + #attr, + (#(#serialize_args2,)*) + )).unwrap(); + ::aipc::__private::sys_ipc_send(target_pid, &req).unwrap(); + Self(::aipc::ClientObject { + pid: target_pid, + object_id: ::aipc::deserialize_object(&fut.await.message[8..]).unwrap() + }) + } + } + } else { + // instance method + quote::quote! { + pub async fn #func_name(&self, #(#args2),*) { + let (id, fut) = ::aipc::async_runtime::alloc_reply_id(); + let req = ::aipc::serialize_buffer(( + id - 1, + #func_name_str, + #attr, + self.0.object_id, + (#(#serialize_args2,)*) + )).unwrap(); + ::aipc::__private::sys_ipc_send(self.0.pid, &req).unwrap(); + ::aipc::deserialize_object(&fut.await.message[8..]).unwrap() + } + } + }; + methods2.push(output); + } + + quote::quote! { + struct #name(::aipc::ClientObject); + impl #name { + const __OBJECT_PATH: &'static str = #attr; + #(#methods2)* + } + } + .into() +} + +#[proc_macro_attribute] +pub fn object(attr: TokenStream, input: TokenStream) -> TokenStream { + let attr = syn::parse_macro_input!(attr as ObjectImpl); + let the_impl = syn::parse_macro_input!(input as ItemImpl); + let target = attr.ty; + + let mut fragments = vec![]; + + let name = attr.name; + let data = the_impl.self_ty.clone(); + let t_items = the_impl.items; + + for input in t_items.clone() { + let input = match input { + syn::ImplItem::Method(m) => m, + _ => return quote::quote! { compile_error!("cannot define object") }.into(), + }; + + let is_async = input.sig.asyncness.is_some(); + let tgd_nam = input.sig.ident.clone(); + let mystr = tgd_nam.to_string(); + let args: Vec<_> = input + .sig + .inputs + .iter() + .filter_map(|a| match a { + syn::FnArg::Receiver(_) => None, + syn::FnArg::Typed(t) => Some(t.ty.clone()), + }) + .map(|a| { + quote::quote! { + ::aipc::deserialize::<#a>(&mut request_deserializer) + } + }) + .collect(); + let possibly_await = if is_async { + quote::quote! {.await } + } else { + quote::quote! {} + }; + if input.sig.inputs.len() == 0 + || if let syn::FnArg::Receiver(_) = &input.sig.inputs[0] { + false + } else { + true + } + { + // this method does not have a reciever, it *must* be a factory + fragments.push(quote::quote! { + #mystr => { + let d: #data = #data::#tgd_nam ( + #( + #args + ),* + ) #possibly_await; + ::aipc::serialize_buffer(self.0.create(source, d)) + }, + }); + } else { + // method has a reciever + fragments.push(quote::quote! { + #mystr => { + let obj = self.0 + .get(source, ::aipc::deserialize::(&mut request_deserializer)?)?; + let mut obj = obj + .lock(); + + ::aipc::serialize_buffer(obj.#tgd_nam ( + #( + #args + ),* + ) #possibly_await ) + }, + }); + } + } + + let result = quote::quote! { + struct #name(::aipc::ServerObject<#data>); + impl #data { + #(#t_items)* + } + impl ::aipc::async_runtime::Listener for #name { + fn listen() { + let srv = ::aipc::__private::Arc::new(::aipc::__private::Mutex::new(Self::create_server())); + aipc::async_runtime::create_server(Box::new(move |msg| { + let srv = ::aipc::__private::Arc::clone(&srv); + aipc::async_runtime::spawn(async move { + let mut srv = srv.lock(); + match srv.service_request(msg.pid, &msg.message[8..]).await { + Some(data) => { + let data: ::aipc::__private::Vec = data; // type annotation for rust-analyzer + + let mut data = [&msg.message[0..8], &data].concat(); + data[0] += 1; // set the reply bit + ::aero_syscall::sys_ipc_send(msg.pid, &data).unwrap(); + } + None => {} + } + }); + + // TODO: we should be thruthful here + true + })); + } + } + impl #name { + fn create_server() -> #name { + #name(::aipc::ServerObject::<#data>::new()) + } + async fn service_request(&mut self, source: usize, data: &[u8]) -> Option> { + extern crate alloc; + let mut request_deserializer = ::aipc::deserializer(data); + let typ = ::aipc::deserialize::(&mut request_deserializer)?; + let call_target = ::aipc::deserialize::(&mut request_deserializer)?; + if #target::__OBJECT_PATH != call_target { + return None + } + match typ.as_str() { + #(#fragments)* + "__drop" => { + // drop the object + self.0.do_drop( + source, + ::aipc::deserialize::(&mut request_deserializer)? + ); + ::aipc::serialize_buffer(()) + }, + _ => { + None + } + } + } + } + }; + + result.into() +} diff --git a/userland/libs/aipc/Cargo.toml b/userland/libs/aipc/Cargo.toml new file mode 100644 index 00000000000..282650b8b7c --- /dev/null +++ b/userland/libs/aipc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "aipc" +version = "0.1.0" +edition = "2021" +authors = ["Anhad Singh "] + +[dependencies] +std = { path = "../../libs/aero_std" } +aero_syscall = { path = "../../../src/aero_syscall" } +aero_ipc = { path = "../../libs/aero_ipc" } +aipc-proc = { path = "../aipc-proc" } +hashbrown = "0.11.2" +postcard = { version = "0.7.3", features = ["alloc"] } +serde = { version = "1.0.136", default-features = false, features = ["alloc"] } +spin = "0.9" diff --git a/userland/libs/aipc/src/async_runtime.rs b/userland/libs/aipc/src/async_runtime.rs new file mode 100644 index 00000000000..cf171ab44bd --- /dev/null +++ b/userland/libs/aipc/src/async_runtime.rs @@ -0,0 +1,242 @@ +use alloc::{collections::VecDeque, sync::Arc}; +use core::{ + cell::RefCell, + future::Future, + mem::swap, + ops::DerefMut, + pin::Pin, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, +}; +use hashbrown::HashMap; +use spin::{mutex::Mutex, Once}; + +struct Task { + future: Pin + Send>>, +} +impl Task { + pub fn new(future: impl Future + Send + 'static) -> Task { + Task { + future: Box::pin(future), + } + } + fn poll(&mut self, context: &mut Context) -> Poll<()> { + self.future.as_mut().poll(context) + } +} + +#[derive(Default)] +pub struct Message { + pub pid: usize, + pub message: Vec, +} + +static SHOULD_THAW_ALL: AtomicBool = AtomicBool::new(false); +static MESSAGE_HANDLERS: Mutex) -> bool + Send>>>> = + Mutex::new(Once::new()); +static RX_ARENA: Mutex<[u8; 0x4000]> = Mutex::new([0; 0x4000]); + +static REPLY_TOKENS: Mutex>>> = Mutex::new(Once::new()); +static REPLY_ID: AtomicUsize = AtomicUsize::new(1); +static NEW_TASK_QUEUE: Mutex>> = Mutex::new(Once::new()); + +struct MessageFuture { + rid: usize, +} +impl Future for MessageFuture { + type Output = Message; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + // get access to REPLY_TOKENS + REPLY_TOKENS.lock().call_once(|| HashMap::new()); + let mut reply_tokens = REPLY_TOKENS.lock(); + let reply_tokens = reply_tokens.get_mut().unwrap(); + let reply_token = reply_tokens.get(&self.rid).unwrap(); + let r = reply_token.borrow(); + if r.pid == 0 { + Poll::Pending + } else { + drop(r); + drop(reply_token); + let r = reply_tokens.remove(&self.rid).unwrap(); + let t = r.take(); + Poll::Ready(t) + } + } +} + +pub fn alloc_reply_id() -> (usize, impl Future) { + // get a unique reply id + let rid = REPLY_ID.fetch_add(2, Ordering::SeqCst); + + // get access to REPLY_TOKENS + REPLY_TOKENS.lock().call_once(|| HashMap::new()); + let mut reply_tokens = REPLY_TOKENS.lock(); + let reply_tokens = reply_tokens.get_mut().unwrap(); + + // define a reply token with a dummy message + reply_tokens.insert(rid, RefCell::new(Message::default())); + + // return the two values + (rid, MessageFuture { rid }) +} +pub fn create_server(srv: Box) -> bool + Send>) { + let mut handlers = MESSAGE_HANDLERS.lock(); + handlers.call_once(|| Vec::new()); + let handlers = handlers.get_mut().unwrap(); + handlers.push(srv); +} + +pub struct AsyncRuntime { + task_queue: VecDeque, + idle_queue: VecDeque, +} + +fn new_raw_waker() -> RawWaker { + fn no_op(_: *const ()) {} + fn thaw(_: *const ()) { + SHOULD_THAW_ALL.store(true, Ordering::SeqCst); + } + fn clone(_: *const ()) -> RawWaker { + new_raw_waker() + } + + let vtable = &RawWakerVTable::new(clone, thaw, thaw, no_op); + RawWaker::new(0 as *const (), vtable) +} + +pub fn spawn(task: impl Future + Send + 'static) { + let mut new_task_queue = NEW_TASK_QUEUE.lock(); + new_task_queue.call_once(|| VecDeque::new()); + let new_task_queue = new_task_queue.get_mut().unwrap(); + new_task_queue.push_back(Task::new(task)); +} + +pub trait Listener { + fn listen(); +} + +impl AsyncRuntime { + pub fn new() -> AsyncRuntime { + AsyncRuntime { + task_queue: VecDeque::new(), + idle_queue: VecDeque::new(), + } + } + + pub fn spawn(&mut self, task: impl Future + Send + 'static) { + self.task_queue.push_back(Task::new(task)) + } + + pub fn wake_all(&mut self) { + assert!(self.task_queue.len() == 0); + swap(&mut self.task_queue, &mut self.idle_queue); + } + + pub fn maybe_wake_all(&mut self) { + if SHOULD_THAW_ALL.swap(false, Ordering::SeqCst) { + self.wake_all(); + } + } + + pub fn run(&mut self) { + // SAFETY: the waker implementation's behaviour does not violate the invariants + // for RawWakerVTable + let waker = unsafe { Waker::from_raw(new_raw_waker()) }; + + loop { + let mut progress = true; + while progress { + progress = false; + + self.maybe_wake_all(); + { + let mut new_task_queue = NEW_TASK_QUEUE.lock(); + if let Some(new_task_queue) = new_task_queue.get_mut() { + while let Some(task) = new_task_queue.pop_front() { + self.task_queue.push_back(task); + progress = true + } + } + } + + // println!(); + while let Some(mut task) = self.task_queue.pop_front() { + progress = true; + let mut context = Context::from_waker(&waker); + match task.poll(&mut context) { + Poll::Ready(()) => {} // task done + Poll::Pending => self.idle_queue.push_back(task), + } + } + } + + // all tasks that could complete completed. wait for an IPC message + let mut pid: usize = 0; + let mut arena = RX_ARENA.lock(); + match aero_syscall::sys_ipc_recv(&mut pid, arena.deref_mut(), true) { + Ok(data) => { + // move the data to the heap to release the rx arena + let d = data.to_vec(); + drop(data); + drop(arena); + + // thaw all tasks, allowing them to run again + + // have each enrolled message handler handle a message + if d.len() < 8 { + println!( + "[aipc] invalid message from pid {}: too short: {:x?}", + pid, d + ); + continue; + } + let message = Message { pid, message: d }; + if message.message[0] & 1 == 0 { + // server + let message = Arc::new(message); + let mut message_handlers = MESSAGE_HANDLERS.lock(); + message_handlers.call_once(|| Vec::new()); + let message_handlers = message_handlers.get_mut().unwrap(); + + let mut success: bool = false; + for m in message_handlers { + if m(Arc::clone(&message)) { + success = true + } + } + if success { + self.wake_all(); + } else { + println!("[aipc] message not handled: {:x?}", message.message); + } + } else { + // client + // the unwrap is fine because we cannot fail + let id: usize = postcard::from_bytes(&message.message[0..8]).unwrap(); + let reply_tokens = REPLY_TOKENS.lock(); + let reply_tokens = reply_tokens.call_once(|| HashMap::new()); + match reply_tokens.get(&id) { + Some(rq) => { + let old = rq.borrow(); + if old.pid != 0 { + println!("[aipc] message reply for request from pid {} delivered multiple times!", pid); + continue; + } + drop(old); + rq.replace(message); + self.wake_all(); + } + None => { + println!("[aipc] pid {} delivered a reply without a corresponding request!", pid); + } + } + } + } + Err(e) => { + println!("[aipc] error receiving: {:?}", e); + } + } + } + } +} diff --git a/userland/libs/aipc/src/lib.rs b/userland/libs/aipc/src/lib.rs new file mode 100644 index 00000000000..383a7b6fb77 --- /dev/null +++ b/userland/libs/aipc/src/lib.rs @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2021-2022 The Aero Project Developers. + * + * This file is part of The Aero Project. + * + * Aero is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Aero is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aero. If not, see . + */ +extern crate alloc; + +use core::marker::PhantomData; + +use alloc::sync::Arc; +use hashbrown::HashMap; +use postcard::Deserializer; +use serde::Serialize; + +pub mod async_runtime; +// private internals needed for the proc macro +pub mod __private { + pub use alloc::sync::Arc; + pub use alloc::vec::Vec; + pub use spin::Mutex; + pub use aero_syscall::sys_ipc_send; +} + +pub fn serialize_buffer(t: T) -> Option> { + match postcard::to_allocvec(&t) { + Ok(data) => Some(data), + Err(e) => { + println!("serialize error: {}", e); + None + } + } +} +pub fn deserialize_object<'a, T: serde::Deserialize<'a>>(data: &'a [u8]) -> Option { + let mut deser = deserializer(data); + match T::deserialize(&mut deser) { + Ok(value) => Some(value), + Err(e) => { + println!("deserialize error: {}", e); + None + } + } +} +pub fn deserializer<'a>(data: &'a [u8]) -> Deserializer<'a> { + Deserializer::from_bytes(data) +} +pub fn deserialize<'de, T: serde::Deserialize<'de>>(t: &mut Deserializer<'de>) -> Option { + match T::deserialize(t) { + Ok(value) => Some(value), + Err(e) => { + println!("deserialize error: {}", e); + None + } + } +} + +pub struct ServerObject { + h: HashMap>)>, + counter: usize, + _ph: PhantomData, +} +pub struct ClientObject { + pub pid: usize, + pub object_id: usize, +} +impl ServerObject { + pub fn new() -> ServerObject { + ServerObject { + h: HashMap::new(), + counter: 0, + _ph: PhantomData, + } + } + pub fn do_drop(&mut self, src: usize, id: usize) { + match self.h.get(&id) { + Some((owner, _)) => { + if *owner == src { + drop(owner); + self.h.remove(&id); + } else { + println!( + "[aipc] pid {} tried to delete object {}, which is owned by {}", + src, id, *owner + ); + } + } + None => { + println!( + "[aipc] pid {} tried to delete non-existent object {}", + src, id + ); + } + } + } + pub fn get(&mut self, src: usize, id: usize) -> Option>> { + match self.h.get(&id) { + Some((owner, data)) => { + if *owner == src { + Some(Arc::clone(data)) + } else { + println!( + "[aipc] pid {} tried to operate on object {} owned by {}", + src, id, *owner + ); + None + } + } + None => { + println!( + "[aipc] pid {} tried to operate on a non-existent object {}", + src, id + ); + None + } + } + } + pub fn create(&mut self, src: usize, data: T) -> usize { + let cntr = self.counter; + self.counter += 1; + self.h.insert(cntr, (src, Arc::new(Mutex::new(data)))); + return cntr; + } +} +pub use aipc_proc::{def, object}; +use spin::Mutex; From a70165a93c0d17b32cdc0dcd20b1bdfa88a7dd1b Mon Sep 17 00:00:00 2001 From: pitust Date: Sat, 19 Mar 2022 15:47:35 +0000 Subject: [PATCH 2/2] further improvements to aipc, rip aero_ipc --- userland/Cargo.lock | 56 ++-- userland/apps/aero_shell/Cargo.toml | 1 - userland/apps/aero_shell/src/main.rs | 32 +-- userland/apps/utest/Cargo.toml | 2 +- userland/apps/utest/src/main.rs | 24 -- userland/apps/window_test/Cargo.toml | 3 +- userland/apps/window_test/src/main.rs | 28 +- userland/libs/aero_ipc/src/interfaces.rs | 24 -- userland/libs/aero_ipc/src/lib.rs | 266 ------------------ userland/libs/aipc-proc/src/lib.rs | 11 +- userland/libs/aipc/Cargo.toml | 1 - userland/libs/aipc/src/async_runtime.rs | 23 +- userland/libs/aipc/src/lib.rs | 1 + .../libs/{aero_ipc => aipc_api}/Cargo.toml | 13 +- userland/libs/aipc_api/src/lib.rs | 42 +++ userland/servers/system_server/Cargo.toml | 3 +- userland/servers/system_server/src/main.rs | 46 ++- userland/servers/window_server/Cargo.toml | 3 +- userland/servers/window_server/src/main.rs | 36 ++- 19 files changed, 173 insertions(+), 442 deletions(-) delete mode 100644 userland/libs/aero_ipc/src/interfaces.rs delete mode 100644 userland/libs/aero_ipc/src/lib.rs rename userland/libs/{aero_ipc => aipc_api}/Cargo.toml (55%) create mode 100644 userland/libs/aipc_api/src/lib.rs diff --git a/userland/Cargo.lock b/userland/Cargo.lock index b8f5258acb7..1b3ed2bd057 100644 --- a/userland/Cargo.lock +++ b/userland/Cargo.lock @@ -2,23 +2,10 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aero_ipc" -version = "0.1.0" -dependencies = [ - "aero_syscall", - "lazy_static", - "postcard", - "serde", - "spin 0.9.2", - "std", -] - [[package]] name = "aero_shell" version = "0.1.0" dependencies = [ - "aero_ipc", "aero_syscall", "std", ] @@ -54,13 +41,22 @@ dependencies = [ name = "aipc" version = "0.1.0" dependencies = [ - "aero_ipc", "aero_syscall", "aipc-proc", "hashbrown 0.11.2", "postcard", "serde", - "spin 0.9.2", + "spin", + "std", +] + +[[package]] +name = "aipc-api" +version = "0.1.0" +dependencies = [ + "aero_syscall", + "aipc", + "serde", "std", ] @@ -80,7 +76,7 @@ dependencies = [ "aero_syscall", "aipc", "postcard", - "spin 0.9.2", + "spin", "std", ] @@ -220,7 +216,7 @@ dependencies = [ "atomic-polyfill", "hash32", "serde", - "spin 0.9.2", + "spin", "stable_deref_trait", ] @@ -237,9 +233,6 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] [[package]] name = "libc" @@ -415,12 +408,6 @@ dependencies = [ "syn", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.2" @@ -451,7 +438,7 @@ version = "0.1.0" dependencies = [ "aero_syscall", "linked_list_allocator", - "spin 0.9.2", + "spin", ] [[package]] @@ -469,10 +456,11 @@ dependencies = [ name = "system_server" version = "0.1.0" dependencies = [ - "aero_ipc", "aero_syscall", + "aipc", + "aipc-api", "hashbrown 0.12.0", - "spin 0.9.2", + "spin", "std", ] @@ -486,8 +474,8 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" name = "utest" version = "0.1.0" dependencies = [ - "aero_ipc", "aero_syscall", + "aipc", "std", "utest-proc", ] @@ -537,10 +525,11 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" name = "window_server" version = "0.1.0" dependencies = [ - "aero_ipc", "aero_syscall", + "aipc", + "aipc-api", "hashbrown 0.12.0", - "spin 0.9.2", + "spin", "std", ] @@ -548,7 +537,8 @@ dependencies = [ name = "window_test" version = "0.1.0" dependencies = [ - "aero_ipc", "aero_syscall", + "aipc", + "aipc-api", "std", ] diff --git a/userland/apps/aero_shell/Cargo.toml b/userland/apps/aero_shell/Cargo.toml index a755820f5a7..560c4bab322 100644 --- a/userland/apps/aero_shell/Cargo.toml +++ b/userland/apps/aero_shell/Cargo.toml @@ -7,4 +7,3 @@ edition = "2021" [dependencies] std = { path = "../../libs/aero_std" } aero_syscall = { path = "../../../src/aero_syscall" } -aero_ipc = { path = "../../libs/aero_ipc" } diff --git a/userland/apps/aero_shell/src/main.rs b/userland/apps/aero_shell/src/main.rs index 75905eece31..08628070a41 100644 --- a/userland/apps/aero_shell/src/main.rs +++ b/userland/apps/aero_shell/src/main.rs @@ -21,7 +21,6 @@ extern crate alloc; use core::sync::atomic::{AtomicU32, Ordering}; -use aero_ipc::SystemService; use aero_syscall::signal::*; use aero_syscall::*; @@ -126,17 +125,15 @@ fn repl(history: &mut Vec) -> Result<(), AeroSyscallError> { // if the test kernel is built instead of randomly bloating the shell // with tests :). - // let fb = sys_open("/dev/fb", OpenFlags::O_RDWR)?; + let fb = sys_open("/dev/fb", OpenFlags::O_RDWR)?; - // let buffer = &[u32::MAX; (1024 * 768)]; - // let casted = buffer.as_ptr() as *mut u8; - // let casted = unsafe { core::slice::from_raw_parts(casted, (1024 * 768) as usize) }; + let buffer = &[u32::MAX; (1024 * 768)]; + let casted = buffer.as_ptr() as *mut u8; + let casted = unsafe { core::slice::from_raw_parts(casted, (1024 * 768) as usize) }; - // println!("writing to fb"); - // sys_write(fb, casted)?; - // sys_close(fb)?; - - uwutest()?; + println!("writing to fb"); + sys_write(fb, casted)?; + sys_close(fb)?; } "pid" => { @@ -295,21 +292,6 @@ fn print_kernel_log() -> Result<(), AeroSyscallError> { cat_file(Some("/dev/kmsg")) } -fn uwutest() -> Result<(), AeroSyscallError> { - let my_pid = sys_getpid()?; - let ipc = SystemService::open(sys_ipc_discover_root()?); - - ipc.announce(my_pid, "TestServer") - .expect("Failed to announce"); - - println!( - "TestServer is at {}", - ipc.discover("TestServer").expect("Failed to discover") - ); - - Ok(()) -} - fn get_uptime() -> Result { let mut info = unsafe { core::mem::zeroed() }; diff --git a/userland/apps/utest/Cargo.toml b/userland/apps/utest/Cargo.toml index ee7e4130532..e05d34fc3cf 100644 --- a/userland/apps/utest/Cargo.toml +++ b/userland/apps/utest/Cargo.toml @@ -7,5 +7,5 @@ authors = ["Anhad Singh "] [dependencies] std = { path = "../../libs/aero_std" } aero_syscall = { path = "../../../src/aero_syscall" } -aero_ipc = { path = "../../libs/aero_ipc" } +aipc = { path = "../../libs/aipc" } utest-proc = { path = "../utest-proc" } diff --git a/userland/apps/utest/src/main.rs b/userland/apps/utest/src/main.rs index eaf30d1866f..fa73afa5f3a 100644 --- a/userland/apps/utest/src/main.rs +++ b/userland/apps/utest/src/main.rs @@ -34,7 +34,6 @@ pub struct Test<'a> { static TEST_FUNCTIONS: &[&'static Test<'static>] = &[ // TODO: Why does clone process fail? // &clone_process, - &rpc_test, &forked_pipe, &signal_handler, &dup_fds, @@ -290,26 +289,3 @@ fn clone_process() -> Result<(), AeroSyscallError> { Ok(()) } - -aero_ipc::ipc! { - trait Hello { - fn hello(favorite_number: i32) -> (); - } -} - -struct HelloServer; - -impl Hello::Server for HelloServer { - fn hello(&self, favnum: i32) { - println!("hey: {}", favnum); - } -} - -#[utest_proc::test] -fn rpc_test() -> Result<(), AeroSyscallError> { - aero_ipc::listen(Hello::handler(HelloServer {})); - let c = Hello::open(sys_getpid().unwrap()); - c.hello(3); - - Ok(()) -} diff --git a/userland/apps/window_test/Cargo.toml b/userland/apps/window_test/Cargo.toml index 4795e84b74c..816aad29031 100644 --- a/userland/apps/window_test/Cargo.toml +++ b/userland/apps/window_test/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] std = { path = "../../libs/aero_std" } aero_syscall = { path = "../../../src/aero_syscall" } -aero_ipc = { path = "../../libs/aero_ipc" } +aipc = { path = "../../libs/aipc" } +aipc-api = { path = "../../libs/aipc_api" } diff --git a/userland/apps/window_test/src/main.rs b/userland/apps/window_test/src/main.rs index dadab21ec19..d64e87bf751 100644 --- a/userland/apps/window_test/src/main.rs +++ b/userland/apps/window_test/src/main.rs @@ -17,22 +17,30 @@ * along with Aero. If not, see . */ -use aero_ipc::{SystemService, WindowService}; use aero_syscall::*; +use aipc_api::{system_server::SystemServer, window_server::WindowServer}; -fn discover_service(name: &str) -> Result { +async fn discover_service(name: &str) -> Result { let root_pid = sys_ipc_discover_root()?; - let system = SystemService::open(root_pid); + let system = SystemServer::open(root_pid).await; - system.discover(name).map_err(|_| AeroSyscallError::ENOMSG) + system + .discover(name) + .await + .map_err(|_| AeroSyscallError::ENOMSG) } -fn main() -> Result<(), AeroSyscallError> { - let window_server = WindowService::open(discover_service("WindowServer")?); +fn main() { + let mut rt = aipc::async_runtime::AsyncRuntime::new(); - window_server.create_window("Test window 1"); - window_server.create_window("Test window 2"); - window_server.create_window("Test window 3"); + rt.spawn(async { + let window_server = WindowServer::open(discover_service("WindowServer").await.unwrap()).await; - Ok(()) + window_server.create_window("Test window 1").await; + window_server.create_window("Test window 2").await; + window_server.create_window("Test window 3").await; + + sys_exit(0); + }); + rt.run(); } diff --git a/userland/libs/aero_ipc/src/interfaces.rs b/userland/libs/aero_ipc/src/interfaces.rs deleted file mode 100644 index 8e1956a8568..00000000000 --- a/userland/libs/aero_ipc/src/interfaces.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::ipc; - -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub enum SystemServiceError { - AlreadyProvided, - NotFound, -} - -pub type SystemServiceResult = Result; - -ipc! { - trait SystemService { - fn announce(pid: usize, name: &str) -> crate::SystemServiceResult<()>; - fn discover(name: &str) -> crate::SystemServiceResult; - } -} - -ipc! { - trait WindowService { - fn create_window(name: &str) -> usize; - } -} diff --git a/userland/libs/aero_ipc/src/lib.rs b/userland/libs/aero_ipc/src/lib.rs deleted file mode 100644 index ac8ab22fafa..00000000000 --- a/userland/libs/aero_ipc/src/lib.rs +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2021-2022 The Aero Project Developers. - * - * This file is part of The Aero Project. - * - * Aero is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Aero is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Aero. If not, see . - */ -#![feature(decl_macro)] - -mod interfaces; - -pub extern crate postcard; -pub extern crate serde; - -use aero_syscall::{sys_ipc_recv, sys_ipc_send}; -use core::ops::DerefMut; -use core::sync::atomic::{AtomicUsize, Ordering}; -use lazy_static::lazy_static; -use serde::Deserialize; - -pub use interfaces::*; - -/// A MessageHandler is a trait describing an IPC client -pub trait MessageHandler: Send + Sync { - fn handle(&mut self, src: usize, msg: &[u8]) -> Result>, ()>; -} - -/// A MessageTransport allows for high-level IPC exchanges over the IPC interfce. -/// It also handles the allocation of request identifiers. -pub trait MessageTransport { - fn alloc_id() -> usize; - fn free_id(id: usize); - fn exchange(meta: usize, mid: usize, data: &[u8]) -> Vec; -} - -/// A SendRecieveTransport transfers messages by using the IPC system calls. -pub struct SendRecieveTransport; - -// trust me, this seed is fine -static IDALLOC: AtomicUsize = AtomicUsize::new(0xde73_ce13_600f_e4e9); - -impl MessageTransport for SendRecieveTransport { - fn alloc_id() -> usize { - let value = IDALLOC.fetch_add(1, Ordering::SeqCst); - // a small attempt at seed obfuscation - IDALLOC.fetch_xor(value << 13, Ordering::SeqCst); - IDALLOC.fetch_xor(value >> 7, Ordering::SeqCst); - IDALLOC.fetch_xor(value << 17, Ordering::SeqCst); - return IDALLOC.fetch_add(1, Ordering::SeqCst) >> 3; - } - - fn free_id(_: usize) {} - - fn exchange(meta: usize, mid: usize, msg: &[u8]) -> Vec { - // send the data - sys_ipc_send(meta, msg).expect("exchange failed: request failed!"); - // now wait for a repsonse - loop { - // get a response - let rx = service_with_response_finding(); - match rx { - // if we got a response, - Some((srcpid, mut msg)) => { - // and the response has the correct message ID... - let mut deser = postcard::Deserializer::from_bytes(&msg); - let msgid = usize::deserialize(&mut deser) - .expect("message ID not present in the message!"); - if msgid == (mid << 1) | 1 && meta == srcpid { - // return the message contents! - return msg.split_off(core::mem::size_of::()); - } - } - None => {} - } - } - } -} - -/// The IPC inteface macro -/// -/// You can create interfaces like this: -/// ```no_run -/// aero_ipc::ipc! { -/// trait Hello { -/// fn hello(favorite_number: i32) -> (); -/// } -/// ``` -/// -/// Then, Hello::Client is the client interface, Hello::Server is the server -/// inteface and Hello::handler instantiates a MessageHandler that can be added -/// to the listening pool. -#[macro_export] -macro_rules! ipc { - { trait $nm:ident { - $( - fn $fnnm:ident($($argname:ident : $argty:ty),*) $(-> $t:ty)?; - )* - } } => { - #[allow(non_snake_case)] - pub mod $nm { - use $crate::{postcard, serde}; - pub struct Client { - pub pid: usize, - pub phantom: ::core::marker::PhantomData, - } - impl Client { - pub fn pid(&self) -> usize { - self.pid - } - $( - pub fn $fnnm(&self, $($argname: $argty),*) $(-> $t)? { - let mid = T::alloc_id(); - let msg = postcard::to_allocvec(&( - mid<<1, // messageid - concat!(stringify!($nm), "::", stringify!($fnnm)) // method - $(, $argname)* // args - )).expect("serialize failed!"); - let resp = T::exchange(self.pid, mid, &msg); - T::free_id(mid); - postcard::from_bytes(&resp).expect("deserialize failed!") - } - )* - } - - pub fn open(pid: usize) -> Client<$crate::SendRecieveTransport> { - Client { pid, phantom: ::core::marker::PhantomData{} } - } - - pub trait Server: Send + Sync { - $( - fn $fnnm(&self, $($argname: $argty),*) $(-> $t)?; - )* - } - - struct MessageHandlingProxy(T); - - pub fn handler(server: T) -> Box { - Box::new(MessageHandlingProxy(server)) - } - - impl $crate::MessageHandler for MessageHandlingProxy { - fn handle(&mut self, _: usize, msg: &[u8]) -> Result>, ()> { - use serde::Deserialize; - - let mut deser = postcard::Deserializer::from_bytes(msg); - // TODO(pitust): cache this in the recieve part of the handler - //? i don't think it would help *that* much though - let msgid: usize = usize::deserialize(&mut deser).or_else(|_e| { - println!("\x1b[31;1merr\x1b[0m message id failed to deserialize!"); - Err(()) - })?; - - let method = String::deserialize(&mut deser).or_else(|_e| { - println!("\x1b[31;1merr\x1b[0m message name failed to deserialize!"); - Err(()) - })?; - - match method.as_str() { - $( - concat!(stringify!($nm), "::", stringify!($fnnm)) => { - Ok(Some(postcard::to_allocvec(&(msgid|1, self.0.$fnnm( - $( - <$argty>::deserialize(&mut deser).or_else(|_e| { - println!("\x1b[31;1merr\x1b[0m message deserialization failed!"); - Err(()) - })? - ),* - ))).expect("reply failed to serialize!"))) - }, - )* - _ => Ok(None) - } - } - } - } - } -} - -lazy_static! { - static ref HANDLER_LIST: spin::Mutex>> = spin::Mutex::new(vec![]); - static ref RX_ARENA: spin::Mutex> = spin::Mutex::new(Box::new([0; 0x4000])); -} - -/// Register a request listener. -pub fn listen(iface: Box) { - let mut list = HANDLER_LIST - .try_lock() - .expect("cannot listen() in a request handler!"); - - list.push(iface); -} - -/// Handle an IPC request from a specified process. -pub fn handle_request(src: usize, msg: &[u8]) -> Option> { - let mut list = HANDLER_LIST - .try_lock() - .expect("cannot nest request handlers!"); - - if (msg[0] & 1) == 1 { - println!( - "\x1b[32;1mwarn\x1b[0m recieved random response from {}!", - src - ); - return None; - } - - for i in list.deref_mut() { - match i.handle(src, msg) { - Ok(Some(data)) => return Some(data), - Ok(None) => {} - Err(_) => return None, - } - } - - println!( - "\x1b[32;1mwarn\x1b[0m failed to dispatch message from {}!", - src - ); - - None -} - -fn service_with_response_finding() -> Option<(usize, Vec)> { - let mut src: usize = 0; - let mut arena = RX_ARENA.try_lock().expect("recieve arena is locked!"); - let msg = sys_ipc_recv(&mut src, arena.as_mut(), true).expect("sys_ipc_recv failed!"); - - // if it's a response - if (msg[0] & 1) == 1 { - return Some((src, msg.to_vec())); - } - - if let Some(data) = handle_request(src, msg) { - sys_ipc_send(src, &data).expect("sys_ipc_send failed, reply dropped!"); - } - - None -} - -/// Service one request from the IPC queues -pub fn service_request() { - let mut src: usize = 0; - let mut arena = RX_ARENA - .try_lock() - .expect("service_request: recieve arena is locked!"); - - let msg = sys_ipc_recv(&mut src, arena.as_mut(), true).expect("sys_ipc_recv failed!"); - - match handle_request(src, msg) { - Some(data) => { - sys_ipc_send(src, &data).expect("sys_ipc_send failed, reply dropped!"); - } - _ => {} - } -} diff --git a/userland/libs/aipc-proc/src/lib.rs b/userland/libs/aipc-proc/src/lib.rs index d3175ce0ad5..68b12bbf497 100644 --- a/userland/libs/aipc-proc/src/lib.rs +++ b/userland/libs/aipc-proc/src/lib.rs @@ -108,7 +108,7 @@ pub fn def(attr: TokenStream, input: TokenStream) -> TokenStream { } else { // instance method quote::quote! { - pub async fn #func_name(&self, #(#args2),*) { + pub async fn #func_name(&self, #(#args2),*) #ret { let (id, fut) = ::aipc::async_runtime::alloc_reply_id(); let req = ::aipc::serialize_buffer(( id - 1, @@ -126,9 +126,9 @@ pub fn def(attr: TokenStream, input: TokenStream) -> TokenStream { } quote::quote! { - struct #name(::aipc::ClientObject); + pub struct #name(::aipc::ClientObject); impl #name { - const __OBJECT_PATH: &'static str = #attr; + pub const __OBJECT_PATH: &'static str = #attr; #(#methods2)* } } @@ -166,7 +166,7 @@ pub fn object(attr: TokenStream, input: TokenStream) -> TokenStream { }) .map(|a| { quote::quote! { - ::aipc::deserialize::<#a>(&mut request_deserializer) + ::aipc::deserialize::<#a>(&mut request_deserializer).unwrap() } }) .collect(); @@ -213,7 +213,7 @@ pub fn object(attr: TokenStream, input: TokenStream) -> TokenStream { } let result = quote::quote! { - struct #name(::aipc::ServerObject<#data>); + pub struct #name(::aipc::ServerObject<#data>); impl #data { #(#t_items)* } @@ -264,6 +264,7 @@ pub fn object(attr: TokenStream, input: TokenStream) -> TokenStream { ::aipc::serialize_buffer(()) }, _ => { + println!("[aipc] call to unhandled function {}.{}", call_target, typ); None } } diff --git a/userland/libs/aipc/Cargo.toml b/userland/libs/aipc/Cargo.toml index 282650b8b7c..c277ae618b2 100644 --- a/userland/libs/aipc/Cargo.toml +++ b/userland/libs/aipc/Cargo.toml @@ -7,7 +7,6 @@ authors = ["Anhad Singh "] [dependencies] std = { path = "../../libs/aero_std" } aero_syscall = { path = "../../../src/aero_syscall" } -aero_ipc = { path = "../../libs/aero_ipc" } aipc-proc = { path = "../aipc-proc" } hashbrown = "0.11.2" postcard = { version = "0.7.3", features = ["alloc"] } diff --git a/userland/libs/aipc/src/async_runtime.rs b/userland/libs/aipc/src/async_runtime.rs index cf171ab44bd..bfbc40a71d4 100644 --- a/userland/libs/aipc/src/async_runtime.rs +++ b/userland/libs/aipc/src/async_runtime.rs @@ -1,3 +1,22 @@ +use aero_syscall::{sys_log, sys_getpid}; +/* + * Copyright (C) 2021-2022 The Aero Project Developers. + * + * This file is part of The Aero Project. + * + * Aero is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Aero is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aero. If not, see . + */ use alloc::{collections::VecDeque, sync::Arc}; use core::{ cell::RefCell, @@ -181,8 +200,8 @@ impl AsyncRuntime { drop(data); drop(arena); - // thaw all tasks, allowing them to run again - + // sys_log(&format!("[aipc] {}->{} {:02x?}", pid, sys_getpid().unwrap(), d)).unwrap(); + // have each enrolled message handler handle a message if d.len() < 8 { println!( diff --git a/userland/libs/aipc/src/lib.rs b/userland/libs/aipc/src/lib.rs index 383a7b6fb77..43595f5ff0c 100644 --- a/userland/libs/aipc/src/lib.rs +++ b/userland/libs/aipc/src/lib.rs @@ -26,6 +26,7 @@ use postcard::Deserializer; use serde::Serialize; pub mod async_runtime; + // private internals needed for the proc macro pub mod __private { pub use alloc::sync::Arc; diff --git a/userland/libs/aero_ipc/Cargo.toml b/userland/libs/aipc_api/Cargo.toml similarity index 55% rename from userland/libs/aero_ipc/Cargo.toml rename to userland/libs/aipc_api/Cargo.toml index ea4c6396fb4..4639bb7205a 100644 --- a/userland/libs/aero_ipc/Cargo.toml +++ b/userland/libs/aipc_api/Cargo.toml @@ -1,16 +1,11 @@ [package] -name = "aero_ipc" +name = "aipc-api" version = "0.1.0" -authors = ["pitust "] edition = "2021" +authors = ["pitust "] [dependencies] +std = { path = "../../libs/aero_std" } aero_syscall = { path = "../../../src/aero_syscall" } -postcard = { version = "0.7.3", features = ["alloc"] } -std = { path = "../aero_std" } +aipc = { path = "../aipc" } serde = { version = "1.0.136", default-features = false, features = ["alloc"] } -spin = "0.9" - -[dependencies.lazy_static] -version = "1.4.0" -features = ["spin_no_std"] diff --git a/userland/libs/aipc_api/src/lib.rs b/userland/libs/aipc_api/src/lib.rs new file mode 100644 index 00000000000..52ec2c7c67f --- /dev/null +++ b/userland/libs/aipc_api/src/lib.rs @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021-2022 The Aero Project Developers. + * + * This file is part of The Aero Project. + * + * Aero is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Aero is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Aero. If not, see . + */ +pub mod system_server { + use serde::{Deserialize, Serialize}; + + #[derive(Debug, Serialize, Deserialize)] + pub enum Error { + AlreadyProvided, + NotFound, + } + + #[aipc::def("SystemServer")] + pub trait SystemServer { + fn open() -> SystemServer; + fn announce(&self, pid: usize, name: &str) -> Result<(), Error>; + fn discover(&self, name: &str) -> Result; + } +} +pub mod window_server { + #[aipc::def("WindowServer")] + pub trait WindowServer { + fn open() -> WindowServer; + fn create_window(&self, name: &str) -> usize; + } +} + diff --git a/userland/servers/system_server/Cargo.toml b/userland/servers/system_server/Cargo.toml index 777ae8bc116..34885db6b6c 100644 --- a/userland/servers/system_server/Cargo.toml +++ b/userland/servers/system_server/Cargo.toml @@ -8,5 +8,6 @@ edition = "2021" hashbrown = "0.12.0" spin = "0.9.2" aero_syscall = { path = "../../../src/aero_syscall" } -aero_ipc = { path = "../../libs/aero_ipc" } +aipc = { path = "../../libs/aipc" } +aipc-api = { path = "../../libs/aipc_api" } std = { path = "../../libs/aero_std" } diff --git a/userland/servers/system_server/src/main.rs b/userland/servers/system_server/src/main.rs index 3cf2bd87183..61abc05fd66 100644 --- a/userland/servers/system_server/src/main.rs +++ b/userland/servers/system_server/src/main.rs @@ -1,3 +1,4 @@ +use aipc::async_runtime::Listener; /* * Copyright (C) 2021-2022 The Aero Project Developers. * @@ -16,11 +17,10 @@ * You should have received a copy of the GNU General Public License * along with Aero. If not, see . */ - -use aero_ipc::{SystemService, SystemServiceError, SystemServiceResult}; use aero_syscall::*; +use aipc_api::system_server::Error; use hashbrown::{hash_map::Entry, HashMap}; -use spin::RwLock; +use spin::{Once, RwLock}; // Basically the same thing that's in the init's main.rs fn fork_and_exec(path: &str, argv: &[&str], envv: &[&str]) -> Result { @@ -37,33 +37,29 @@ fn fork_and_exec(path: &str, argv: &[&str], envv: &[&str]) -> Result>, -} +static SERVICES: Once>> = Once::new(); -impl SystemServer { - fn new() -> Self { - Self { - services: RwLock::new(HashMap::with_capacity(24)), - } +struct SystemServerData; + +#[aipc::object(ConcreteSystemServer as aipc_api::system_server::SystemServer)] +impl SystemServerData { + fn open() -> SystemServerData { + SERVICES.call_once(|| RwLock::new(HashMap::new())); + SystemServerData } -} -impl SystemService::Server for SystemServer { - fn announce(&self, pid: usize, name: &str) -> SystemServiceResult<()> { + fn announce(&self, pid: usize, name: &str) -> Result<(), Error> { let name = name.to_string(); - match self.services.write().entry(name) { - Entry::Occupied(_) => Err(SystemServiceError::AlreadyProvided), + match SERVICES.get().unwrap().write().entry(name) { + Entry::Occupied(_) => Err(Error::AlreadyProvided), Entry::Vacant(entry) => { entry.insert(pid); Ok(()) @@ -71,13 +67,15 @@ impl SystemService::Server for SystemServer { } } - fn discover(&self, name: &str) -> SystemServiceResult { + fn discover(&self, name: &str) -> Result { let name = name.to_string(); - self.services + SERVICES + .get() + .unwrap() .read() .get(&name) .map(|pid| *pid) - .ok_or(SystemServiceError::NotFound) + .ok_or(Error::NotFound) } } diff --git a/userland/servers/window_server/Cargo.toml b/userland/servers/window_server/Cargo.toml index 07a613370f9..a9e5645829b 100644 --- a/userland/servers/window_server/Cargo.toml +++ b/userland/servers/window_server/Cargo.toml @@ -8,5 +8,6 @@ edition = "2021" hashbrown = "0.12.0" spin = "0.9.2" aero_syscall = { path = "../../../src/aero_syscall" } -aero_ipc = { path = "../../libs/aero_ipc" } +aipc = { path = "../../libs/aipc" } +aipc-api = { path = "../../libs/aipc_api" } std = { path = "../../libs/aero_std" } diff --git a/userland/servers/window_server/src/main.rs b/userland/servers/window_server/src/main.rs index 85cd076db57..52f22da4cdf 100644 --- a/userland/servers/window_server/src/main.rs +++ b/userland/servers/window_server/src/main.rs @@ -17,26 +17,34 @@ * along with Aero. If not, see . */ -use aero_ipc::{SystemService, WindowService}; use aero_syscall::*; +use aipc::async_runtime::Listener; fn main() { - let self_pid = sys_getpid().unwrap(); - let ipc_root = sys_ipc_discover_root().unwrap(); - let system_client = SystemService::open(ipc_root); - - system_client.announce(self_pid, "WindowServer").unwrap(); - - aero_ipc::listen(WindowService::handler(WindowServer)); - - loop { - aero_ipc::service_request(); - } + let mut rt = aipc::async_runtime::AsyncRuntime::new(); + + rt.spawn(async { + let self_pid = sys_getpid().unwrap(); + let ipc_root = sys_ipc_discover_root().unwrap(); + let system_client = aipc_api::system_server::SystemServer::open(ipc_root).await; + + system_client + .announce(self_pid, "WindowServer") + .await + .unwrap(); + + ConcreteWindowServer::listen(); + }); + rt.run(); } -struct WindowServer; +struct WindowServerData; -impl WindowService::Server for WindowServer { +#[aipc::object(ConcreteWindowServer as aipc_api::window_server::WindowServer)] +impl WindowServerData { + fn open() -> WindowServerData { + WindowServerData + } fn create_window(&self, name: &str) -> usize { println!("[window_server] creating window with name: {}", name);