feat: async all the functions!!

This commit is contained in:
Nova
2022-08-16 09:04:10 -04:00
parent 58b0ae1f9f
commit 6b13d47197
6 changed files with 196 additions and 189 deletions

View File

@@ -3,8 +3,6 @@ edition = "2018"
name = "stardust-xr" name = "stardust-xr"
version = "0.9.0" version = "0.9.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "1.0.57" anyhow = "1.0.57"
clap = { version = "3.1.6", features = ["derive"] } clap = { version = "3.1.6", features = ["derive"] }
@@ -15,7 +13,6 @@ flexbuffers = "2.0.0"
glam = {version = "0.20.5", features = ["mint"]} glam = {version = "0.20.5", features = ["mint"]}
lazy_static = "1.4.0" lazy_static = "1.4.0"
mint = "0.5.9" mint = "0.5.9"
mio = {version = "0.8.3", features = ["net", "os-poll", "os-ext"]}
nanoid = "0.4.0" nanoid = "0.4.0"
once_cell = "1.12.0" once_cell = "1.12.0"
parking_lot = "0.12.1" parking_lot = "0.12.1"
@@ -23,12 +20,13 @@ portable-atomic = {version = "0.3.0", features = ["float", "std"]}
rccell = "0.1.3" rccell = "0.1.3"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
slab = "0.4.6" slab = "0.4.6"
tokio = { version = "1", features = ["full"] }
thiserror = "1.0.31" thiserror = "1.0.31"
[dependencies.libstardustxr] [dependencies.libstardustxr]
path = "../libstardustxr-rs" path = "../libstardustxr-rs"
[dependencies.stereokit-rs] [dependencies.stereokit]
path = "../stereokit-rs" path = "../stereokit-rs"
default-features = false default-features = false
features = ["linux-egl"] features = ["linux-egl"]

View File

@@ -1,3 +1,4 @@
use super::eventloop::EventLoop;
use super::scenegraph::Scenegraph; use super::scenegraph::Scenegraph;
use crate::nodes::data; use crate::nodes::data;
use crate::nodes::field; use crate::nodes::field;
@@ -5,32 +6,52 @@ use crate::nodes::input;
use crate::nodes::item; use crate::nodes::item;
use crate::nodes::root::Root; use crate::nodes::root::Root;
use crate::nodes::spatial; use crate::nodes::spatial;
use anyhow::Result;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use libstardustxr::messenger::Messenger; use libstardustxr::messenger::Messenger;
use mio::net::UnixStream;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use std::sync::Arc; use std::sync::{Arc, Weak};
use tokio::net::UnixStream;
use tokio::sync::Notify;
use tokio::task::JoinHandle;
lazy_static! { lazy_static! {
pub static ref INTERNAL_CLIENT: Arc<Client> = Client::new_local(); pub static ref INTERNAL_CLIENT: Arc<Client> = Arc::new(Client {
event_loop: Weak::new(),
index: 0,
stop_notifier: Default::default(),
join_handle: OnceCell::new(),
messenger: None,
scenegraph: Default::default(),
root: OnceCell::new(),
});
} }
pub struct Client { pub struct Client {
event_loop: Weak<EventLoop>,
index: usize,
stop_notifier: Arc<Notify>,
join_handle: OnceCell<JoinHandle<Result<()>>>,
pub messenger: Option<Messenger>, pub messenger: Option<Messenger>,
pub scenegraph: Scenegraph, pub scenegraph: Scenegraph,
pub root: OnceCell<Arc<Root>>, pub root: OnceCell<Arc<Root>>,
} }
impl Client { impl Client {
pub fn new_local() -> Arc<Self> { pub fn from_connection(
Arc::new(Client { index: usize,
messenger: None, event_loop: &Arc<EventLoop>,
scenegraph: Default::default(), connection: UnixStream,
root: OnceCell::new(), ) -> Arc<Self> {
})
}
pub fn from_connection(connection: UnixStream) -> Arc<Self> {
println!("New client connected"); println!("New client connected");
let client = Arc::new(Client { let client = Arc::new(Client {
event_loop: Arc::downgrade(event_loop),
index,
stop_notifier: Default::default(),
join_handle: OnceCell::new(),
messenger: Some(Messenger::new(connection)), messenger: Some(Messenger::new(connection)),
scenegraph: Default::default(), scenegraph: Default::default(),
root: OnceCell::new(), root: OnceCell::new(),
@@ -42,18 +63,44 @@ impl Client {
data::create_interface(&client); data::create_interface(&client);
item::create_interface(&client); item::create_interface(&client);
input::create_interface(&client); input::create_interface(&client);
let _ = client.join_handle.set(tokio::spawn({
let client = client.clone();
async move {
let dispatch_loop = async {
loop {
client.dispatch().await?
}
};
let result = tokio::select! {
_ = client.stop_notifier.notified() => Ok(()),
e = dispatch_loop => e,
};
client.disconnect().await;
result
}
}));
client client
} }
pub fn dispatch(&self) -> Result<(), std::io::Error> {
if let Some(messenger) = &self.messenger { pub async fn dispatch(&self) -> Result<(), std::io::Error> {
messenger.dispatch(&self.scenegraph) match &self.messenger {
} else { Some(messenger) => messenger.dispatch(&self.scenegraph).await,
Err(std::io::Error::from(std::io::ErrorKind::Unsupported)) None => Err(std::io::Error::from(std::io::ErrorKind::Unsupported)),
}
}
pub async fn disconnect(&self) {
self.stop_notifier.notify_one();
if let Some(event_loop) = self.event_loop.upgrade() {
event_loop.clients.lock().await.remove(self.index);
} }
} }
} }
impl Drop for Client { impl Drop for Client {
fn drop(&mut self) { fn drop(&mut self) {
self.stop_notifier.notify_one();
println!("Client disconnected"); println!("Client disconnected");
} }
} }

View File

@@ -1,143 +1,64 @@
use super::client::Client; use super::client::Client;
use anyhow::Result; use anyhow::Result;
use libstardustxr::server; use libstardustxr::server;
use mio::net::UnixListener;
use mio::unix::pipe;
use mio::{Events, Interest, Poll, Token};
use slab::Slab; use slab::Slab;
use std::io::Write;
use std::sync::atomic::AtomicU64; use std::sync::atomic::AtomicU64;
use std::sync::Arc; use std::sync::Arc;
use std::thread::{self, JoinHandle}; use tokio::net::UnixListener;
use tokio::sync::{Mutex, Notify, OnceCell};
use tokio::task::JoinHandle;
pub static FRAME: AtomicU64 = AtomicU64::new(0); pub static FRAME: AtomicU64 = AtomicU64::new(0);
pub struct EventLoop { pub struct EventLoop {
pub socket_path: String, pub socket_path: String,
join_handle: Option<JoinHandle<Result<()>>>, stop_notifier: Arc<Notify>,
stop_write: pipe::Sender, pub clients: Mutex<Slab<OnceCell<Arc<Client>>>>,
} }
impl EventLoop { impl EventLoop {
pub fn new(timeout: Option<core::time::Duration>) -> Result<Self> { pub fn new() -> Result<(Arc<Self>, JoinHandle<Result<()>>)> {
let socket_path = server::get_free_socket_path() let socket_path = server::get_free_socket_path()
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::Other))?; .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::Other))?;
let (sender, receiver) = pipe::new()?; let socket = UnixListener::bind(socket_path.clone())?;
let socket_path_captured = socket_path.clone();
let join_handle = thread::Builder::new() let event_loop = Arc::new(EventLoop {
.name("event_loop".to_owned())
.spawn(move || EventLoop::run_loop(timeout, socket_path_captured, receiver))
.ok();
Ok(EventLoop {
socket_path, socket_path,
join_handle, stop_notifier: Default::default(),
stop_write: sender, clients: Mutex::new(Slab::new()),
}) });
let event_loop_join_handle = tokio::spawn({
let event_loop = event_loop.clone();
async move { EventLoop::event_loop(socket, event_loop).await }
});
Ok((event_loop, event_loop_join_handle))
} }
fn run_loop( async fn event_loop(socket: UnixListener, event_loop: Arc<EventLoop>) -> Result<()> {
timeout: Option<core::time::Duration>, let event_loop_async = async {
socket_path: String,
stop_receiver: pipe::Receiver,
) -> Result<()> {
let mut stop_receiver = stop_receiver;
let mut socket = UnixListener::bind(socket_path)?;
let mut clients: Slab<Option<Arc<Client>>> = Slab::new();
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(1024);
const LISTENER: Token = Token(usize::MAX - 1);
poll.registry()
.register(&mut socket, LISTENER, Interest::READABLE)?;
const STOP: Token = Token(usize::MAX);
poll.registry()
.register(&mut stop_receiver, STOP, Interest::READABLE)?;
'event_loop: loop {
match poll.poll(&mut events, timeout) {
Err(e) => {
if e.kind() == std::io::ErrorKind::Interrupted {
continue 'event_loop;
}
}
_ => {}
}
for event in &events {
match event.token() {
LISTENER => EventLoop::accept_client(&socket, &mut clients, &poll)?,
STOP => break 'event_loop,
token => EventLoop::handle_client_message(token.0, &mut clients)?,
}
}
}
clients.clear(); // for better log messages
println!("Event loop gracefully finished");
Ok(())
}
fn accept_client(
socket: &UnixListener,
clients: &mut Slab<Option<Arc<Client>>>,
poll: &Poll,
) -> Result<()> {
loop {
match socket.accept() {
Ok((mut socket, _)) => {
let client_number = clients.insert(None);
poll.registry().register(
&mut socket,
Token(client_number),
Interest::READABLE,
)?;
let client = Client::from_connection(socket);
*clients.get_mut(client_number).unwrap() = Some(client);
}
Err(e) => match e.kind() {
std::io::ErrorKind::WouldBlock => break,
_ => return Err(e.into()),
},
}
}
Ok(())
}
fn handle_client_message(
client_id: usize,
clients: &mut Slab<Option<Arc<Client>>>,
) -> Result<()> {
let client = clients.get(client_id).and_then(|client| client.as_ref());
if let Some(client) = client {
loop { loop {
let dispatch_result = client.dispatch(); let (socket, _) = socket.accept().await?;
match dispatch_result { let mut clients = event_loop.clients.lock().await;
Ok(_) => continue, let idx = clients.insert(OnceCell::new());
Err(e) => match e.kind() { let _ = clients.get(idx).unwrap().set(Client::from_connection(
std::io::ErrorKind::WouldBlock => break, idx,
std::io::ErrorKind::Interrupted => continue, &event_loop,
_ => { socket,
clients.remove(client_id); ));
break;
}
},
}
} }
};
tokio::select! {
_ = event_loop.stop_notifier.notified() => Ok(()),
e = event_loop_async => e,
} }
Ok(())
} }
} }
impl Drop for EventLoop { impl Drop for EventLoop {
fn drop(&mut self) { fn drop(&mut self) {
let buf: [u8; 1] = [1; 1]; self.stop_notifier.notify_one();
let _ = self.stop_write.write(buf.as_slice());
if let Some(handle) = self.join_handle.take() {
match handle.join() {
Ok(r) => {
if let Err(e) = r {
eprintln!("Event loop error: {}", e);
}
}
Err(e) => eprintln!("Event loop failed to rejoin with error: {:#?}", e),
}
}
} }
} }

View File

@@ -2,11 +2,15 @@ mod core;
mod nodes; mod nodes;
use self::core::eventloop::EventLoop; use self::core::eventloop::EventLoop;
use anyhow::{ensure, Result}; use anyhow::Result;
use clap::Parser; use clap::Parser;
use stereokit_rs as sk; use once_cell::sync::Lazy;
use stereokit_rs::enums::DisplayMode; use parking_lot::Mutex;
use stereokit_rs::functions::*; use std::sync::Arc;
use stereokit::{lifecycle::DisplayMode, Settings};
use tokio::runtime::Handle;
static TOKIO_HANDLE: Lazy<Mutex<Option<Handle>>> = Lazy::new(Default::default);
#[derive(Parser)] #[derive(Parser)]
#[clap(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]
@@ -14,32 +18,64 @@ struct CliArgs {
/// Force flatscreen mode and use the mouse pointer as a 3D pointer /// Force flatscreen mode and use the mouse pointer as a 3D pointer
#[clap(short, action)] #[clap(short, action)]
flatscreen: bool, flatscreen: bool,
/// Run Stardust XR as an overlay
#[clap(short, action)]
overlay: bool,
} }
fn main() -> Result<()> { fn main() -> Result<()> {
let cli_args = CliArgs::parse(); let cli_args = Arc::new(CliArgs::parse());
ctrlc::set_handler(sk_quit).expect("Error setting Ctrl-C handler");
let mut init_settings = SKSettings::default().app_name("Stardust XR"); let mut init_settings = Settings::default()
.app_name("Stardust XR")
.overlay_app(cli_args.overlay)
.overlay_priority(u32::MAX);
if cli_args.flatscreen { if cli_args.flatscreen {
init_settings = init_settings.display_preference(DisplayMode::Flatscreen); init_settings = init_settings.display_preference(DisplayMode::Flatscreen);
} }
ensure!(init_settings.init(), "StereoKit failed to initialize"); let stereokit = init_settings
.init()
.expect("StereoKit failed to initialize");
let event_loop = EventLoop::new(None).expect("Couldn't create server socket"); let event_thread = std::thread::Builder::new()
println!("Stardust socket created at {}", event_loop.socket_path); .name("event_loop".to_owned())
.spawn(event_loop)?;
let mut previous_time = 0_f64; stereokit.run(
sk_run( |_draw_ctx| {
&mut Box::new(&mut move || { nodes::root::Root::logic_step(stereokit.time_elapsed());
let current_time = unsafe { sk::sys::time_get() }; },
nodes::root::Root::logic_step(current_time - previous_time); || {
previous_time = current_time; println!("Shut down StereoKit");
}), },
&mut Box::new(&mut || {
println!("Shutting down...");
}),
); );
event_thread
.join()
.expect("Failed to cleanly shut down event loop")?;
println!("Cleanly shut down Stardust");
Ok(()) Ok(())
} }
#[tokio::main]
async fn event_loop() -> anyhow::Result<()> {
TOKIO_HANDLE.lock().replace(Handle::current());
let (event_loop, event_loop_join_handle) =
EventLoop::new().expect("Couldn't create server socket");
println!("Init event loop");
println!("Stardust socket created at {}", event_loop.socket_path);
let result = tokio::select! {
biased;
_ = tokio::signal::ctrl_c() => Ok(()),
// e = task => e?,
e = event_loop_join_handle => e?,
};
unsafe {
stereokit::sys::sk_quit();
}
result
}

View File

@@ -5,6 +5,7 @@ use super::item::{Item, ItemAcceptor, ItemUI};
use super::spatial::Spatial; use super::spatial::Spatial;
use crate::core::client::Client; use crate::core::client::Client;
use crate::core::registry::Registry; use crate::core::registry::Registry;
use crate::TOKIO_HANDLE;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use libstardustxr::scenegraph::ScenegraphError; use libstardustxr::scenegraph::ScenegraphError;
use nanoid::nanoid; use nanoid::nanoid;
@@ -156,7 +157,8 @@ impl Node {
} }
} }
pub fn send_remote_signal(&self, method: &str, data: &[u8]) -> Result<()> { pub fn send_remote_signal(&self, method: &str, data: &[u8]) -> Result<()> {
self.aliases let _ = self
.aliases
.get_valid_contents() .get_valid_contents()
.iter() .iter()
.filter(|alias| alias.remote_signals.iter().any(|e| e == &method)) .filter(|alias| alias.remote_signals.iter().any(|e| e == &method))
@@ -167,25 +169,28 @@ impl Node {
.unwrap() .unwrap()
.send_remote_signal(method, data); .send_remote_signal(method, data);
}); });
self.get_client() let client = self.get_client();
.messenger let path = self.path.clone();
.as_ref() let method = method.to_string();
.ok_or_else(|| anyhow!("Node's client has no messenger"))? let data = data.to_vec();
.send_remote_signal(self.path.as_str(), method, data) TOKIO_HANDLE.lock().as_ref().unwrap().spawn(async move {
.map_err(|_| anyhow!("Unable to write in messenger")) if let Some(messenger) = client.messenger.as_ref() {
let _ = messenger
.send_remote_signal(path.as_str(), method.as_str(), data.as_slice())
.await;
}
});
Ok(())
} }
pub fn execute_remote_method( pub async fn execute_remote_method(&self, method: &str, data: Vec<u8>) -> Result<Vec<u8>> {
&self, match self.get_client().messenger.as_ref() {
method: &str, None => Err(anyhow!("Messenger does not exist for this node's client")),
data: &[u8], Some(messenger) => {
callback: Box<dyn FnOnce(&[u8]) + Send + Sync>, messenger
) -> Result<()> { .execute_remote_method(self.path.as_str(), method, &data)
self.get_client() .await
.messenger }
.as_ref() }
.ok_or_else(|| anyhow!("Node's client has no messenger"))?
.execute_remote_method(self.path.as_str(), method, data, callback)
.map_err(|_| anyhow!("Unable to write in messenger"))
} }
} }

View File

@@ -156,23 +156,23 @@ impl InputHandler {
return; return;
} }
let serialized_data = distance_link.serialize(); match distance_link.serialize() {
None => InputHandler::next_input(old_frame, distance_links),
Some(data) => {
let node = self.node.upgrade().unwrap();
if let Some(data) = serialized_data { tokio::spawn(async move {
let _ = self.node.upgrade().unwrap().execute_remote_method( let data = node.execute_remote_method("input", data).await;
"input", if let Ok(data) = data {
&data, let capture = flexbuffers::Reader::get_root(data.as_slice())
Box::new(move |data| { .and_then(|data| data.get_bool())
let capture = flexbuffers::Reader::get_root(data) .unwrap_or(false);
.and_then(|data| data.get_bool()) if !distance_links.is_empty() && !capture {
.unwrap_or(false); InputHandler::next_input(old_frame, distance_links);
if !distance_links.is_empty() && !capture { }
InputHandler::next_input(old_frame, distance_links);
} }
}), });
); }
} else {
InputHandler::next_input(old_frame, distance_links);
} }
} }