autocommit 2022-01-02 16:30:30 CET
This commit is contained in:
parent
d6172b273f
commit
c21f231ce7
34 changed files with 874 additions and 844 deletions
395
src/client/mod.rs
Normal file
395
src/client/mod.rs
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
use crate::async_engine::{AsyncError, AsyncFd, SpawnedFuture};
|
||||
use crate::client::objects::Objects;
|
||||
use crate::ifs::wl_callback::WlCallback;
|
||||
use crate::ifs::wl_compositor::{WlCompositorError, WlCompositorObj};
|
||||
use crate::ifs::wl_display::{WlDisplay, WlDisplayError};
|
||||
use crate::ifs::wl_region::{WlRegion, WlRegionError};
|
||||
use crate::ifs::wl_registry::{WlRegistry, WlRegistryError};
|
||||
use crate::ifs::wl_shm::{WlShmError, WlShmObj};
|
||||
use crate::ifs::wl_shm_pool::{WlShmPool, WlShmPoolError};
|
||||
use crate::ifs::wl_subcompositor::WlSubcompositorObj;
|
||||
use crate::ifs::wl_surface::{WlSurface, WlSurfaceError};
|
||||
use crate::ifs::xdg_wm_base::XdgWmBaseObj;
|
||||
use crate::object::{Object, ObjectId, WL_DISPLAY_ID};
|
||||
use crate::state::State;
|
||||
use crate::utils::buffd::{BufFdError, WlFormatter, WlParser, WlParserError};
|
||||
use crate::utils::copyhashmap::CopyHashMap;
|
||||
use crate::utils::numcell::NumCell;
|
||||
use crate::utils::oneshot::{oneshot, OneshotTx};
|
||||
use crate::utils::queue::AsyncQueue;
|
||||
use ahash::AHashMap;
|
||||
use anyhow::anyhow;
|
||||
use std::cell::{Cell, RefMut};
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::future::Future;
|
||||
use std::rc::Rc;
|
||||
use thiserror::Error;
|
||||
use uapi::OwnedFd;
|
||||
|
||||
mod objects;
|
||||
mod tasks;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ClientError {
|
||||
#[error("An error occurred in the async engine")]
|
||||
Async(#[from] AsyncError),
|
||||
#[error("An error occurred reading from/writing to the client")]
|
||||
Io(#[from] BufFdError),
|
||||
#[error("An error occurred while processing a request")]
|
||||
RequestError(#[source] Box<ClientError>),
|
||||
#[error("Client tried to invoke a non-existent method")]
|
||||
InvalidMethod,
|
||||
#[error("Client tried to access non-existent object {0}")]
|
||||
InvalidObject(ObjectId),
|
||||
#[error("The message size is < 8")]
|
||||
MessageSizeTooSmall,
|
||||
#[error("The size of the message is not a multiple of 4")]
|
||||
UnalignedMessage,
|
||||
#[error("The outgoing buffer overflowed")]
|
||||
OutBufferOverflow,
|
||||
#[error("The requested client {0} does not exist")]
|
||||
ClientDoesNotExist(ClientId),
|
||||
#[error("There is no region with id {0}")]
|
||||
RegionDoesNotExist(ObjectId),
|
||||
#[error("Cannot parse the message")]
|
||||
ParserError(#[source] Box<WlParserError>),
|
||||
#[error("Server tried to allocate more than 0x1_00_00_00 ids")]
|
||||
TooManyIds,
|
||||
#[error("The server object id is out of bounds")]
|
||||
ServerIdOutOfBounds,
|
||||
#[error("The object id is unknown")]
|
||||
UnknownId,
|
||||
#[error("The id is already in use")]
|
||||
IdAlreadyInUse,
|
||||
#[error("The client object id is out of bounds")]
|
||||
ClientIdOutOfBounds,
|
||||
#[error("An error occurred in a `wl_display`")]
|
||||
WlDisplayError(#[source] Box<WlDisplayError>),
|
||||
#[error("An error occurred in a `wl_registry`")]
|
||||
WlRegistryError(#[source] Box<WlRegistryError>),
|
||||
#[error("Could not add object {0} to the client")]
|
||||
AddObjectError(ObjectId, #[source] Box<ClientError>),
|
||||
#[error("An error occurred in a `wl_surface`")]
|
||||
WlSurfaceError(#[source] Box<WlSurfaceError>),
|
||||
#[error("An error occurred in a `wl_compositor`")]
|
||||
WlCompositorError(#[source] Box<WlCompositorError>),
|
||||
#[error("An error occurred in a `wl_shm`")]
|
||||
WlShmError(#[source] Box<WlShmError>),
|
||||
#[error("An error occurred in a `wl_shm_pool`")]
|
||||
WlShmPoolError(#[source] Box<WlShmPoolError>),
|
||||
#[error("An error occurred in a `wl_region`")]
|
||||
WlRegionError(#[source] Box<WlRegionError>),
|
||||
#[error("Object {0} is not a display")]
|
||||
NotADisplay(ObjectId),
|
||||
}
|
||||
|
||||
efrom!(ClientError, ParserError, WlParserError);
|
||||
efrom!(ClientError, WlDisplayError, WlDisplayError);
|
||||
efrom!(ClientError, WlRegistryError, WlRegistryError);
|
||||
efrom!(ClientError, WlSurfaceError, WlSurfaceError);
|
||||
efrom!(ClientError, WlCompositorError, WlCompositorError);
|
||||
efrom!(ClientError, WlShmError, WlShmError);
|
||||
efrom!(ClientError, WlShmPoolError, WlShmPoolError);
|
||||
efrom!(ClientError, WlRegionError, WlRegionError);
|
||||
|
||||
impl ClientError {
|
||||
fn peer_closed(&self) -> bool {
|
||||
match self {
|
||||
ClientError::Io(BufFdError::Closed) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub struct ClientId(u64);
|
||||
|
||||
impl Display for ClientId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Clients {
|
||||
next_client_id: NumCell<u64>,
|
||||
clients: CopyHashMap<ClientId, Rc<ClientHolder>>,
|
||||
shutdown_clients: CopyHashMap<ClientId, Rc<ClientHolder>>,
|
||||
}
|
||||
|
||||
impl Clients {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
next_client_id: NumCell::new(1),
|
||||
clients: CopyHashMap::new(),
|
||||
shutdown_clients: CopyHashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> ClientId {
|
||||
ClientId(self.next_client_id.fetch_add(1))
|
||||
}
|
||||
|
||||
pub fn get(&self, id: ClientId) -> Result<Rc<Client>, ClientError> {
|
||||
match self.clients.get(&id) {
|
||||
Some(c) => Ok(c.data.clone()),
|
||||
_ => Err(ClientError::ClientDoesNotExist(id)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn(
|
||||
&self,
|
||||
id: ClientId,
|
||||
global: &Rc<State>,
|
||||
socket: OwnedFd,
|
||||
) -> Result<(), ClientError> {
|
||||
let (send, recv) = oneshot();
|
||||
let data = Rc::new(Client {
|
||||
id,
|
||||
state: global.clone(),
|
||||
socket: global.eng.fd(&Rc::new(socket))?,
|
||||
objects: Objects::new(),
|
||||
events: AsyncQueue::new(),
|
||||
shutdown: Cell::new(Some(send)),
|
||||
shutdown_sent: Cell::new(false),
|
||||
});
|
||||
data.objects
|
||||
.add_client_object(Rc::new(WlDisplay::new(&data)))
|
||||
.expect("");
|
||||
let client = Rc::new(ClientHolder {
|
||||
_handler: global.eng.spawn(tasks::client(data.clone(), recv)),
|
||||
data,
|
||||
});
|
||||
self.clients.set(client.data.id, client.clone());
|
||||
log::info!("Client {} connected", id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn kill(&self, client: ClientId) {
|
||||
log::info!("Removing client {}", client.0);
|
||||
if self.clients.remove(&client).is_none() {
|
||||
self.shutdown_clients.remove(&client);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shutdown(&self, client_id: ClientId) {
|
||||
if let Some(client) = self.clients.remove(&client_id) {
|
||||
log::info!("Shutting down client {}", client.data.id.0);
|
||||
client.data.shutdown.replace(None).unwrap().send(());
|
||||
client.data.events.push(WlEvent::Shutdown);
|
||||
client.data.shutdown_sent.set(true);
|
||||
self.shutdown_clients.set(client_id, client);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn broadcast<B>(&self, mut f: B)
|
||||
where
|
||||
B: FnMut(&Rc<Client>),
|
||||
{
|
||||
let clients = self.clients.lock();
|
||||
for client in clients.values() {
|
||||
f(&client.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ClientHolder {
|
||||
data: Rc<Client>,
|
||||
_handler: SpawnedFuture<()>,
|
||||
}
|
||||
|
||||
impl Drop for ClientHolder {
|
||||
fn drop(&mut self) {
|
||||
self.data.objects.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EventFormatter: Debug {
|
||||
fn format(self: Box<Self>, fmt: &mut WlFormatter<'_>);
|
||||
fn obj(&self) -> &dyn Object;
|
||||
}
|
||||
|
||||
pub type DynEventFormatter = Box<dyn EventFormatter>;
|
||||
|
||||
pub trait RequestParser<'a>: Debug + Sized {
|
||||
fn parse(parser: &mut WlParser<'_, 'a>) -> Result<Self, WlParserError>;
|
||||
}
|
||||
|
||||
enum WlEvent {
|
||||
Flush,
|
||||
Shutdown,
|
||||
Event(Box<dyn EventFormatter>),
|
||||
}
|
||||
|
||||
pub struct Client {
|
||||
pub id: ClientId,
|
||||
pub state: Rc<State>,
|
||||
socket: AsyncFd,
|
||||
objects: Objects,
|
||||
events: AsyncQueue<WlEvent>,
|
||||
shutdown: Cell<Option<OneshotTx<()>>>,
|
||||
shutdown_sent: Cell<bool>,
|
||||
}
|
||||
|
||||
const MAX_PENDING_EVENTS: usize = 100;
|
||||
|
||||
impl Client {
|
||||
pub fn invalid_request(&self, obj: &dyn Object, request: u32) {
|
||||
log::error!(
|
||||
"Client {} sent an invalid request {} on object {} of type {}",
|
||||
self.id.0,
|
||||
request,
|
||||
obj.id(),
|
||||
obj.interface().name(),
|
||||
);
|
||||
match self.display() {
|
||||
Ok(d) => self.fatal_event(d.invalid_request(obj, request)),
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Could not retrieve display of client {}: {:#}",
|
||||
self.id,
|
||||
anyhow!(e)
|
||||
);
|
||||
self.state.clients.kill(self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display(&self) -> Result<Rc<WlDisplay>, ClientError> {
|
||||
Ok(self.objects.get_obj(WL_DISPLAY_ID)?.into_display()?)
|
||||
}
|
||||
|
||||
pub fn parse<'a, R: RequestParser<'a>>(
|
||||
&self,
|
||||
obj: &impl Object,
|
||||
mut parser: WlParser<'_, 'a>,
|
||||
) -> Result<R, WlParserError> {
|
||||
let res = R::parse(&mut parser)?;
|
||||
parser.eof()?;
|
||||
log::trace!(
|
||||
"Client {} -> {}@{}.{:?}",
|
||||
self.id,
|
||||
obj.interface().name(),
|
||||
obj.id(),
|
||||
res
|
||||
);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn fatal_event(&self, event: Box<dyn EventFormatter>) {
|
||||
self.events.push(WlEvent::Event(event));
|
||||
self.state.clients.shutdown(self.id);
|
||||
}
|
||||
|
||||
pub fn event_locked(&self, event: Box<dyn EventFormatter>) -> bool {
|
||||
self.events.push(WlEvent::Event(event));
|
||||
self.events.size() > MAX_PENDING_EVENTS
|
||||
}
|
||||
|
||||
pub async fn event(&self, event: Box<dyn EventFormatter>) -> Result<(), ClientError> {
|
||||
self.event2(WlEvent::Event(event)).await
|
||||
}
|
||||
|
||||
async fn event2(&self, event: WlEvent) -> Result<(), ClientError> {
|
||||
self.events.push(event);
|
||||
self.check_queue_size().await
|
||||
}
|
||||
|
||||
pub async fn check_queue_size(&self) -> Result<(), ClientError> {
|
||||
if self.events.size() > MAX_PENDING_EVENTS {
|
||||
self.state.eng.yield_now().await;
|
||||
if self.events.size() > MAX_PENDING_EVENTS {
|
||||
log::error!("Client {} is too slow at fetching events", self.id.0);
|
||||
self.state.clients.kill(self.id);
|
||||
return Err(ClientError::OutBufferOverflow);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_region(&self, id: ObjectId) -> Result<Rc<WlRegion>, ClientError> {
|
||||
match self.objects.regions.get(&id) {
|
||||
Some(r) => Ok(r),
|
||||
_ => Err(ClientError::RegionDoesNotExist(id)),
|
||||
}
|
||||
}
|
||||
|
||||
fn simple_add_obj<T: Object>(&self, obj: &Rc<T>, client: bool) -> Result<(), ClientError> {
|
||||
if client {
|
||||
self.objects.add_client_object(obj.clone())
|
||||
} else {
|
||||
self.objects.add_server_object(obj.clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn simple_remove_obj<'a>(
|
||||
&'a self,
|
||||
id: ObjectId,
|
||||
) -> impl Future<Output = Result<(), ClientError>> + 'a {
|
||||
self.objects.remove_obj(self, id)
|
||||
}
|
||||
|
||||
pub fn lock_registries(&self) -> RefMut<AHashMap<ObjectId, Rc<WlRegistry>>> {
|
||||
self.objects.registries()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AddObj<T> {
|
||||
type RemoveObj<'a>: Future<Output = Result<(), ClientError>> + 'a;
|
||||
|
||||
fn add_client_obj(&self, obj: &Rc<T>) -> Result<(), ClientError> {
|
||||
self.add_obj(obj, true)
|
||||
}
|
||||
|
||||
fn add_server_obj(&self, obj: &Rc<T>) {
|
||||
self.add_obj(obj, false).expect("add_server_obj failed")
|
||||
}
|
||||
|
||||
fn add_obj(&self, obj: &Rc<T>, client: bool) -> Result<(), ClientError>;
|
||||
|
||||
fn remove_obj<'a>(&'a self, obj: &'a T) -> Self::RemoveObj<'a>;
|
||||
}
|
||||
|
||||
macro_rules! simple_add_obj {
|
||||
($ty:ty) => {
|
||||
impl AddObj<$ty> for Client {
|
||||
type RemoveObj<'a> = impl Future<Output = Result<(), ClientError>> + 'a;
|
||||
|
||||
fn add_obj(&self, obj: &Rc<$ty>, client: bool) -> Result<(), ClientError> {
|
||||
self.simple_add_obj(obj, client)
|
||||
}
|
||||
fn remove_obj<'a>(&'a self, obj: &'a $ty) -> Self::RemoveObj<'a> {
|
||||
self.simple_remove_obj(obj.id())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
simple_add_obj!(WlCompositorObj);
|
||||
simple_add_obj!(WlCallback);
|
||||
simple_add_obj!(WlRegistry);
|
||||
simple_add_obj!(WlShmObj);
|
||||
simple_add_obj!(WlShmPool);
|
||||
simple_add_obj!(WlSubcompositorObj);
|
||||
simple_add_obj!(XdgWmBaseObj);
|
||||
|
||||
macro_rules! dedicated_add_obj {
|
||||
($ty:ty, $field:ident) => {
|
||||
impl AddObj<$ty> for Client {
|
||||
type RemoveObj<'a> = impl Future<Output = Result<(), ClientError>> + 'a;
|
||||
|
||||
fn add_obj(&self, obj: &Rc<$ty>, client: bool) -> Result<(), ClientError> {
|
||||
self.simple_add_obj(obj, client)?;
|
||||
self.objects.$field.set(obj.id(), obj.clone());
|
||||
Ok(())
|
||||
}
|
||||
fn remove_obj<'a>(&'a self, obj: &'a $ty) -> Self::RemoveObj<'a> {
|
||||
self.objects.$field.remove(&obj.id());
|
||||
self.simple_remove_obj(obj.id())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
dedicated_add_obj!(WlRegion, regions);
|
||||
dedicated_add_obj!(WlSurface, surfaces);
|
||||
122
src/client/objects.rs
Normal file
122
src/client/objects.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
use crate::client::{Client, ClientError};
|
||||
use crate::ifs::wl_region::WlRegion;
|
||||
use crate::ifs::wl_registry::WlRegistry;
|
||||
use crate::ifs::wl_surface::WlSurface;
|
||||
use crate::object::{Object, ObjectId};
|
||||
use crate::utils::copyhashmap::CopyHashMap;
|
||||
use ahash::AHashMap;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Objects {
|
||||
registry: CopyHashMap<ObjectId, Rc<dyn Object>>,
|
||||
registries: CopyHashMap<ObjectId, Rc<WlRegistry>>,
|
||||
pub surfaces: CopyHashMap<ObjectId, Rc<WlSurface>>,
|
||||
pub regions: CopyHashMap<ObjectId, Rc<WlRegion>>,
|
||||
ids: RefCell<Vec<usize>>,
|
||||
}
|
||||
|
||||
pub const MIN_SERVER_ID: u32 = 0xff000000;
|
||||
const SEG_SIZE: usize = 8 * mem::size_of::<usize>();
|
||||
|
||||
impl Objects {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
registry: Default::default(),
|
||||
registries: Default::default(),
|
||||
surfaces: Default::default(),
|
||||
regions: Default::default(),
|
||||
ids: RefCell::new(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy(&self) {
|
||||
self.registry.clear();
|
||||
self.registries.clear();
|
||||
self.surfaces.clear();
|
||||
}
|
||||
|
||||
fn id(&self, client_data: &Client) -> Result<ObjectId, ClientError> {
|
||||
const MAX_ID_OFFSET: u32 = u32::MAX - MIN_SERVER_ID;
|
||||
let offset = self.id_offset();
|
||||
if offset > MAX_ID_OFFSET {
|
||||
log::error!(
|
||||
"Client {} caused the server to allocate more than 0x{:x} ids",
|
||||
client_data.id,
|
||||
MAX_ID_OFFSET + 1
|
||||
);
|
||||
return Err(ClientError::TooManyIds);
|
||||
}
|
||||
Ok(ObjectId::from_raw(MIN_SERVER_ID + offset))
|
||||
}
|
||||
|
||||
pub fn get_obj(&self, id: ObjectId) -> Result<Rc<dyn Object>, ClientError> {
|
||||
match self.registry.get(&id) {
|
||||
Some(o) => Ok(o),
|
||||
_ => Err(ClientError::UnknownId),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_server_object(&self, obj: Rc<dyn Object>) {
|
||||
let id = obj.id();
|
||||
assert!(id.raw() >= MIN_SERVER_ID);
|
||||
assert!(!self.registry.contains(&id));
|
||||
self.registry.set(id, obj.clone());
|
||||
}
|
||||
|
||||
pub fn add_client_object(&self, obj: Rc<dyn Object>) -> Result<(), ClientError> {
|
||||
let id = obj.id();
|
||||
let res = (|| {
|
||||
if id.raw() == 0 || id.raw() >= MIN_SERVER_ID {
|
||||
return Err(ClientError::ClientIdOutOfBounds);
|
||||
}
|
||||
if self.registry.contains(&id) {
|
||||
return Err(ClientError::IdAlreadyInUse);
|
||||
}
|
||||
self.registry.set(id, obj.clone());
|
||||
Ok(())
|
||||
})();
|
||||
if let Err(e) = res {
|
||||
return Err(ClientError::AddObjectError(id, Box::new(e)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove_obj(&self, client_data: &Client, id: ObjectId) -> Result<(), ClientError> {
|
||||
if self.registry.remove(&id).is_none() {
|
||||
return Err(ClientError::UnknownId);
|
||||
}
|
||||
if id.raw() >= MIN_SERVER_ID {
|
||||
let offset = (id.raw() - MIN_SERVER_ID) as usize;
|
||||
let pos = offset / SEG_SIZE;
|
||||
let seg_offset = offset % SEG_SIZE;
|
||||
let mut ids = self.ids.borrow_mut();
|
||||
if ids.len() <= pos {
|
||||
return Err(ClientError::ServerIdOutOfBounds);
|
||||
}
|
||||
ids[pos] |= 1 << seg_offset;
|
||||
}
|
||||
client_data
|
||||
.event(client_data.display()?.delete_id(id))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn registries(&self) -> RefMut<AHashMap<ObjectId, Rc<WlRegistry>>> {
|
||||
self.registries.lock()
|
||||
}
|
||||
|
||||
fn id_offset(&self) -> u32 {
|
||||
let mut ids = self.ids.borrow_mut();
|
||||
for (pos, seg) in ids.iter_mut().enumerate() {
|
||||
if *seg != 0 {
|
||||
let offset = seg.trailing_zeros();
|
||||
*seg &= !(1 << offset);
|
||||
return (pos * SEG_SIZE) as u32 + offset;
|
||||
}
|
||||
}
|
||||
ids.push(!1);
|
||||
((ids.len() - 1) * SEG_SIZE) as u32
|
||||
}
|
||||
}
|
||||
154
src/client/tasks.rs
Normal file
154
src/client/tasks.rs
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
use crate::client::{Client, ClientError, WlEvent};
|
||||
use crate::object::ObjectId;
|
||||
use crate::utils::buffd::{BufFdIn, BufFdOut, WlFormatter, WlParser};
|
||||
use crate::utils::oneshot::OneshotRx;
|
||||
use crate::utils::vec_ext::VecExt;
|
||||
use anyhow::anyhow;
|
||||
use futures::{select, FutureExt};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub async fn client(data: Rc<Client>, shutdown: OneshotRx<()>) {
|
||||
let mut recv = data.state.eng.spawn(receive(data.clone())).fuse();
|
||||
let _send = data.state.eng.spawn(send(data.clone()));
|
||||
select! {
|
||||
_ = recv => { },
|
||||
_ = shutdown.fuse() => { },
|
||||
}
|
||||
drop(recv);
|
||||
if !data.shutdown_sent.get() {
|
||||
data.events.push(WlEvent::Shutdown);
|
||||
}
|
||||
match data.state.eng.timeout(5000) {
|
||||
Ok(timeout) => {
|
||||
timeout.await;
|
||||
log::error!("Could not shut down client {} within 5 seconds", data.id.0);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Could not create a timeout: {:#}", e);
|
||||
}
|
||||
}
|
||||
data.state.clients.kill(data.id);
|
||||
}
|
||||
|
||||
async fn receive(data: Rc<Client>) {
|
||||
let display = data.display().unwrap();
|
||||
let recv = async {
|
||||
let mut buf = BufFdIn::new(data.socket.clone());
|
||||
let mut data_buf = Vec::<u32>::new();
|
||||
loop {
|
||||
let mut hdr = [0u32, 0];
|
||||
buf.read_full(&mut hdr[..]).await?;
|
||||
let obj_id = ObjectId::from_raw(hdr[0]);
|
||||
let len = (hdr[1] >> 16) as usize;
|
||||
let request = hdr[1] & 0xffff;
|
||||
let obj = match data.objects.get_obj(obj_id) {
|
||||
Ok(obj) => obj,
|
||||
_ => {
|
||||
data.fatal_event(display.invalid_object(obj_id));
|
||||
return Err(ClientError::InvalidObject(obj_id));
|
||||
}
|
||||
};
|
||||
// log::trace!("obj: {}, request: {}, len: {}", obj_id, request, len);
|
||||
if request >= obj.num_requests() {
|
||||
data.invalid_request(&*obj, request);
|
||||
return Err(ClientError::InvalidMethod);
|
||||
}
|
||||
if len < 8 {
|
||||
return Err(ClientError::MessageSizeTooSmall);
|
||||
}
|
||||
if len % 4 != 0 {
|
||||
return Err(ClientError::UnalignedMessage);
|
||||
}
|
||||
let len = len / 4 - 2;
|
||||
data_buf.clear();
|
||||
data_buf.reserve(len);
|
||||
let unused = data_buf.split_at_spare_mut_ext().1;
|
||||
buf.read_full(&mut unused[..len]).await?;
|
||||
unsafe {
|
||||
data_buf.set_len(len);
|
||||
}
|
||||
// log::trace!("{:x?}", data_buf);
|
||||
let parser = WlParser::new(&mut buf, &data_buf[..]);
|
||||
if let Err(e) = obj.handle_request(request, parser).await {
|
||||
return Err(ClientError::RequestError(Box::new(e)));
|
||||
}
|
||||
data.event2(WlEvent::Flush).await?;
|
||||
}
|
||||
};
|
||||
let res: Result<(), ClientError> = recv.await;
|
||||
if let Err(e) = res {
|
||||
if e.peer_closed() {
|
||||
log::info!("Client {} terminated the connection", data.id.0);
|
||||
data.state.clients.kill(data.id);
|
||||
} else {
|
||||
let e = anyhow!(e);
|
||||
log::error!(
|
||||
"An error occurred while trying to handle a message from client {}: {:#}",
|
||||
data.id.0,
|
||||
e
|
||||
);
|
||||
if !data.shutdown_sent.get() {
|
||||
data.fatal_event(display.implementation_error(format!("{:#}", e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn send(data: Rc<Client>) {
|
||||
let send = async {
|
||||
let mut buf = BufFdOut::new(data.socket.clone());
|
||||
let mut flush_requested = false;
|
||||
loop {
|
||||
let mut event = data.events.pop().await;
|
||||
loop {
|
||||
match event {
|
||||
WlEvent::Flush => {
|
||||
flush_requested = true;
|
||||
}
|
||||
WlEvent::Shutdown => {
|
||||
buf.flush().await?;
|
||||
return Ok(());
|
||||
}
|
||||
WlEvent::Event(e) => {
|
||||
if log::log_enabled!(log::Level::Trace) {
|
||||
let obj = e.obj();
|
||||
log::trace!(
|
||||
"Client {} <= {}@{}.{:?}",
|
||||
data.id,
|
||||
obj.interface().name(),
|
||||
obj.id(),
|
||||
e
|
||||
);
|
||||
}
|
||||
e.format(&mut WlFormatter::new(&mut buf));
|
||||
if buf.needs_flush() {
|
||||
buf.flush().await?;
|
||||
flush_requested = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
event = match data.events.try_pop() {
|
||||
Some(e) => e,
|
||||
_ => break,
|
||||
};
|
||||
}
|
||||
if mem::take(&mut flush_requested) {
|
||||
buf.flush().await?;
|
||||
}
|
||||
}
|
||||
};
|
||||
let res: Result<(), ClientError> = send.await;
|
||||
if let Err(e) = res {
|
||||
if e.peer_closed() {
|
||||
log::info!("Client {} terminated the connection", data.id.0);
|
||||
} else {
|
||||
log::error!(
|
||||
"An error occurred while sending data to client {}: {:#}",
|
||||
data.id.0,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
data.state.clients.kill(data.id);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue