move signaling to utils::signaling

This commit is contained in:
Victor Berger 2021-07-01 23:49:56 +02:00 committed by Victor Berger
parent 0ac045eb17
commit d5b033f5b5
14 changed files with 262 additions and 304 deletions

View File

@ -48,7 +48,7 @@ use smithay::{
Display, Display,
}, },
}, },
signaling::{Linkable, SignalToken, Signaler}, utils::signaling::{Linkable, SignalToken, Signaler},
wayland::{ wayland::{
output::{Mode, PhysicalProperties}, output::{Mode, PhysicalProperties},
seat::CursorImageStatus, seat::CursorImageStatus,

View File

@ -25,7 +25,7 @@ pub struct DrmDevice<A: AsRawFd + 'static> {
pub(super) dev_id: dev_t, pub(super) dev_id: dev_t,
pub(crate) internal: Arc<DrmDeviceInternal<A>>, pub(crate) internal: Arc<DrmDeviceInternal<A>>,
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
pub(super) links: RefCell<Vec<crate::signaling::SignalToken>>, pub(super) links: RefCell<Vec<crate::utils::signaling::SignalToken>>,
has_universal_planes: bool, has_universal_planes: bool,
resources: ResourceHandles, resources: ResourceHandles,
pub(super) logger: ::slog::Logger, pub(super) logger: ::slog::Logger,

View File

@ -15,7 +15,7 @@ use super::device::{DrmDevice, DrmDeviceInternal};
use super::surface::{DrmSurface, DrmSurfaceInternal}; use super::surface::{DrmSurface, DrmSurfaceInternal};
use crate::{ use crate::{
backend::session::Signal as SessionSignal, backend::session::Signal as SessionSignal,
signaling::{Linkable, Signaler}, utils::signaling::{Linkable, Signaler},
}; };
use slog::{crit, error, info, o, warn}; use slog::{crit, error, info, o, warn};

View File

@ -32,7 +32,7 @@ pub struct DrmSurface<A: AsRawFd + 'static> {
pub(super) internal: Arc<DrmSurfaceInternal<A>>, pub(super) internal: Arc<DrmSurfaceInternal<A>>,
pub(super) has_universal_planes: bool, pub(super) has_universal_planes: bool,
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
pub(super) links: RefCell<Vec<crate::signaling::SignalToken>>, pub(super) links: RefCell<Vec<crate::utils::signaling::SignalToken>>,
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -4,7 +4,7 @@ use crate::backend::input::{self as backend, Axis, InputBackend, InputEvent};
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
use crate::{ use crate::{
backend::session::{AsErrno, Session, Signal as SessionSignal}, backend::session::{AsErrno, Session, Signal as SessionSignal},
signaling::{Linkable, SignalToken, Signaler}, utils::signaling::{Linkable, SignalToken, Signaler},
}; };
use input as libinput; use input as libinput;
use input::event; use input::event;

View File

@ -39,7 +39,7 @@ use super::{
direct::{self, DirectSession, DirectSessionNotifier}, direct::{self, DirectSession, DirectSessionNotifier},
AsErrno, Session, Signal as SessionSignal, AsErrno, Session, Signal as SessionSignal,
}; };
use crate::signaling::Signaler; use crate::utils::signaling::Signaler;
use nix::fcntl::OFlag; use nix::fcntl::OFlag;
use std::{cell::RefCell, io, os::unix::io::RawFd, path::Path, rc::Rc}; use std::{cell::RefCell, io, os::unix::io::RawFd, path::Path, rc::Rc};

View File

@ -36,7 +36,7 @@
use crate::{ use crate::{
backend::session::{AsErrno, Session, Signal as SessionSignal}, backend::session::{AsErrno, Session, Signal as SessionSignal},
signaling::Signaler, utils::signaling::Signaler,
}; };
use dbus::{ use dbus::{
arg::{messageitem::MessageItem, OwnedFd}, arg::{messageitem::MessageItem, OwnedFd},

View File

@ -47,7 +47,7 @@
//! a calloop event source to have its events processed. //! a calloop event source to have its events processed.
use super::{AsErrno, Session, Signal as SessionSignal}; use super::{AsErrno, Session, Signal as SessionSignal};
use crate::signaling::Signaler; use crate::utils::signaling::Signaler;
use calloop::signals::{Signal, Signals}; use calloop::signals::{Signal, Signals};
use nix::{ use nix::{

View File

@ -22,7 +22,7 @@ use calloop::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
use crate::{ use crate::{
backend::session::{AsErrno, Session, Signal as SessionSignal}, backend::session::{AsErrno, Session, Signal as SessionSignal},
signaling::Signaler, utils::signaling::Signaler,
}; };
use slog::{debug, error, o}; use slog::{debug, error, o};

View File

@ -18,8 +18,6 @@ pub mod utils;
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
pub mod wayland; pub mod wayland;
pub mod signaling;
#[cfg(feature = "xwayland")] #[cfg(feature = "xwayland")]
pub mod xwayland; pub mod xwayland;

View File

@ -1,272 +0,0 @@
//! A general purpose signaling mechanism
//!
//! This mechanism allows inter-module communication, by letting your modules
//! register callbacks to listen for events generated by other modules. This
//! signaling mechanism is synchronous and non-threadsafe. If you need
//! ascynchronous threadsafe communication, instead consider relying on channels.
//!
//! The whole mechanism is built on the [`Signaler`](./struct.Signaler.html) type.
//! It serves both as a message sending facility and a way to register new callbacks
//! for these messages. It can be cloned and passed around between your modules with
//! `Rc`-like semantics.
//!
//! When sending a new signal with `Signaler::signal`, the provided value `E` will
//! be made accessible as a reference `&E` to all registered callback.
//!
//! Sending a signal or registering a new callback from within a callback is supported.
//! These will however take effect after the current signal is completely delivered.
//! Ordering of sent signals and callback registration is preserved.
use std::{
any::Any,
cell::RefCell,
collections::VecDeque,
fmt,
rc::{Rc, Weak},
};
/// A signaler, main type for signaling
#[derive(Debug)]
pub struct Signaler<S> {
inner: Rc<SignalInner<S>>,
}
// Manual clone impl because of type parameters
impl<S> Clone for Signaler<S> {
fn clone(&self) -> Signaler<S> {
Signaler {
inner: self.inner.clone(),
}
}
}
impl<S> Signaler<S> {
/// Create a new signaler for given signal type
pub fn new() -> Signaler<S> {
Signaler {
inner: Rc::new(SignalInner::new()),
}
}
/// Register a new callback to this signaler
///
/// This method returns a `SignalToken`, which you must keep as long
/// as you need your callback to remain in place. Dropping it will
/// disable and free your callback. If you don't plan to ever disable
/// your callback, see [`SignalToken::leak()`](./struct.SignalToken.html).
///
/// If you register a callback from within a callback of the same Signaler,
/// the new callback will only be inserted *after* the current signal is
/// completely delivered, and thus will not receive it.
#[must_use]
pub fn register<F: FnMut(&S) + 'static>(&self, f: F) -> SignalToken {
let rc = Rc::new(RefCell::new(f));
let weak = Rc::downgrade(&rc) as Weak<RefCell<dyn FnMut(&S)>>;
self.inner.insert(weak);
SignalToken { signal: rc }
}
/// Signal the callbacks
///
/// All registered callbacks will be invoked with a reference to the value
/// you provide here, after which that value will be dropped.
///
/// If this method is invoked from within a callback of the same Signaler,
/// its signalling will be delayed until the current signal is completely
/// delivered and this method will return immediately.
pub fn signal(&self, signal: S) {
self.inner.send(signal);
}
}
impl<S> Default for Signaler<S> {
fn default() -> Self {
Self::new()
}
}
/// A token associated with a callback registered to a Signaler
///
/// Dropping it will disable and drop the callback it is associated to.
/// If you don't plan to ever disable the callback, you can use the `leak`
/// method to safely get rid of this value.
#[derive(Debug)]
pub struct SignalToken {
signal: Rc<dyn Any>,
}
impl SignalToken {
/// Destroy the token without disabling the associated callback
pub fn leak(self) {
// leak the Rc, so that it is never deallocated
let _ = Rc::into_raw(self.signal);
}
}
type WeakCallback<S> = Weak<RefCell<dyn FnMut(&S)>>;
struct SignalInner<S> {
callbacks: RefCell<Vec<WeakCallback<S>>>,
pending_callbacks: RefCell<Vec<WeakCallback<S>>>,
pending_events: RefCell<VecDeque<S>>,
}
// WeakCallback does not implement debug, so we have to impl Debug manually
impl<S: fmt::Debug> fmt::Debug for SignalInner<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SignalInner")
.field("callbacks::len()", &self.callbacks.borrow().len())
.field("pending_callbacks::len()", &self.pending_callbacks.borrow().len())
.field("pending_events", &self.pending_events)
.finish()
}
}
impl<S> SignalInner<S> {
fn new() -> SignalInner<S> {
SignalInner {
callbacks: RefCell::new(Vec::new()),
pending_callbacks: RefCell::new(Vec::new()),
pending_events: RefCell::new(VecDeque::new()),
}
}
fn insert(&self, weak: WeakCallback<S>) {
// attempt to insert the new callback
if let Ok(mut guard) = self.callbacks.try_borrow_mut() {
// success, insert it
guard.push(weak);
} else {
// The callback list is already borrowed, this means that this insertion is
// done from within a callback.
// In that case, insert the callback into the pending list, `send`
// will insert it in the callback list when it is finished dispatching
// the current event.
self.pending_callbacks.borrow_mut().push(weak);
}
}
fn send(&self, event: S) {
// insert the new event into the pending list
self.pending_events.borrow_mut().push_back(event);
// now try to dispatch the events from the pending list
// new events might be added by other callbacks in the process
// so we try to completely drain it before returning
//
// If we cannot get the guard, that means an other dispatching is
// already in progress. It'll empty the pending list, so there is
// nothing more we need to do.
if let Ok(mut guard) = self.callbacks.try_borrow_mut() {
// We cannot just use `while let` because this would keep the
// borrow of self.pending_events alive during the whole loop, rather
// than just the evaluation of the condition. :/
loop {
let next_event = self.pending_events.borrow_mut().pop_front();
if let Some(event) = next_event {
// Send the message, cleaning up defunct callbacks in the process
guard.retain(|weak| {
if let Some(cb) = Weak::upgrade(weak) {
(&mut *cb.borrow_mut())(&event);
true
} else {
false
}
});
// integrate any pending callbacks resulting from the dispatching
// of this event
guard.extend(self.pending_callbacks.borrow_mut().drain(..));
} else {
break;
}
}
}
}
}
/// Trait representing the capability of an object to listen for some signals
///
/// It is provided so that the signaling system can play nicely into generic
/// constructs.
pub trait Linkable<S> {
/// Make this object listen for signals from given signaler
fn link(&mut self, signaler: Signaler<S>);
}
#[cfg(test)]
mod tests {
use super::*;
use std::{cell::Cell, rc::Rc};
#[test]
fn basic_signal() {
let signaler = Signaler::<u32>::new();
let signaled = Rc::new(Cell::new(false));
let signaled2 = signaled.clone();
let _token = signaler.register(move |_| signaled2.set(true));
signaler.signal(0);
assert!(signaled.get());
}
#[test]
fn remove_callback() {
let signaler = Signaler::<u32>::new();
let token = signaler.register(|&i| assert_eq!(i, 42));
signaler.signal(42);
::std::mem::drop(token);
signaler.signal(41);
let _token = signaler.register(|&i| assert_eq!(i, 39));
signaler.signal(39);
}
#[test]
fn delayed_signal() {
let signaler = Signaler::<u32>::new();
let mut signaled = false;
let sign2 = signaler.clone();
let _token = signaler.register(move |&i| {
if !signaled {
sign2.signal(42);
signaled = true;
} else {
assert_eq!(i, 42);
}
});
signaler.signal(0);
}
#[test]
fn delayed_register() {
let signaler = Signaler::<bool>::new();
let signaled = Rc::new(Cell::new(0u32));
let signaled2 = signaled.clone();
let sign2 = signaler.clone();
let _token1 = signaler.register(move |&original| {
signaled2.set(signaled2.get() + 1);
if original {
let signaled3 = signaled2.clone();
sign2.register(move |_| signaled3.set(signaled3.get() + 1)).leak();
sign2.signal(false);
}
});
signaler.signal(true);
// Two rounds of signals, the first triggers 1 callback, the second triggers 2
assert_eq!(signaled.get(), 3);
}
}

View File

@ -1,6 +1,7 @@
//! Various utilities functions and types //! Various utilities functions and types
mod rectangle; mod rectangle;
pub mod signaling;
pub use self::rectangle::Rectangle; pub use self::rectangle::Rectangle;

View File

@ -1,41 +1,272 @@
use std::{rc::Rc, cell::RefCell}; //! A general purpose signaling mechanism
//!
//! This mechanism allows inter-module communication, by letting your modules
//! register callbacks to listen for events generated by other modules. This
//! signaling mechanism is synchronous and non-threadsafe. If you need
//! ascynchronous threadsafe communication, instead consider relying on channels.
//!
//! The whole mechanism is built on the [`Signaler`] type.
//! It serves both as a message sending facility and a way to register new callbacks
//! for these messages. It can be cloned and passed around between your modules with
//! `Rc`-like semantics.
//!
//! When sending a new signal with [`Signaler::signal`], the provided value `E` will
//! be made accessible as a reference `&E` to all registered callback.
//!
//! Sending a signal or registering a new callback from within a callback is supported.
//! These will however take effect after the current signal is completely delivered.
//! Ordering of sent signals and callback registration is preserved.
use std::{
any::Any,
cell::RefCell,
collections::VecDeque,
fmt,
rc::{Rc, Weak},
};
/// A signaler, main type for signaling
#[derive(Debug)] #[derive(Debug)]
struct SignalerInner<E> { pub struct Signaler<S> {
closures: RefCell<Vec<Box<dyn FnMut(&mut E)>>> inner: Rc<SignalInner<S>>,
} }
impl<E> SignalerInner<E> { // Manual clone impl because of type parameters
fn new() -> SignalerInner<E> { impl<S> Clone for Signaler<S> {
SignalerInner { fn clone(&self) -> Signaler<S> {
closures: RefCell::new(Vec::new()) Signaler {
inner: self.inner.clone(),
} }
} }
} }
impl<S> Signaler<S> {
/// Create a new signaler for given signal type
pub fn new() -> Signaler<S> {
Signaler {
inner: Rc::new(SignalInner::new()),
}
}
/// Register a new callback to this signaler
///
/// This method returns a `SignalToken`, which you must keep as long
/// as you need your callback to remain in place. Dropping it will
/// disable and free your callback. If you don't plan to ever disable
/// your callback, see [`SignalToken::leak()`](./struct.SignalToken.html).
///
/// If you register a callback from within a callback of the same Signaler,
/// the new callback will only be inserted *after* the current signal is
/// completely delivered, and thus will not receive it.
#[must_use]
pub fn register<F: FnMut(&S) + 'static>(&self, f: F) -> SignalToken {
let rc = Rc::new(RefCell::new(f));
let weak = Rc::downgrade(&rc) as Weak<RefCell<dyn FnMut(&S)>>;
self.inner.insert(weak);
SignalToken { signal: rc }
}
/// Signal the callbacks
///
/// All registered callbacks will be invoked with a reference to the value
/// you provide here, after which that value will be dropped.
///
/// If this method is invoked from within a callback of the same Signaler,
/// its signalling will be delayed until the current signal is completely
/// delivered and this method will return immediately.
pub fn signal(&self, signal: S) {
self.inner.send(signal);
}
}
impl<S> Default for Signaler<S> {
fn default() -> Self {
Self::new()
}
}
/// A token associated with a callback registered to a Signaler
///
/// Dropping it will disable and drop the callback it is associated to.
/// If you don't plan to ever disable the callback, you can use the `leak`
/// method to safely get rid of this value.
#[derive(Debug)] #[derive(Debug)]
pub struct Signaler<E> { pub struct SignalToken {
inner: Rc<SignalerInner<E>> signal: Rc<dyn Any>,
} }
impl<E> Clone for Signaler<E> { impl SignalToken {
fn clone(&self) -> Signaler<E> { /// Destroy the token without disabling the associated callback
Signaler { pub fn leak(self) {
inner: self.inner.clone() // leak the Rc, so that it is never deallocated
let _ = Rc::into_raw(self.signal);
}
}
type WeakCallback<S> = Weak<RefCell<dyn FnMut(&S)>>;
struct SignalInner<S> {
callbacks: RefCell<Vec<WeakCallback<S>>>,
pending_callbacks: RefCell<Vec<WeakCallback<S>>>,
pending_events: RefCell<VecDeque<S>>,
}
// WeakCallback does not implement debug, so we have to impl Debug manually
impl<S: fmt::Debug> fmt::Debug for SignalInner<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SignalInner")
.field("callbacks::len()", &self.callbacks.borrow().len())
.field("pending_callbacks::len()", &self.pending_callbacks.borrow().len())
.field("pending_events", &self.pending_events)
.finish()
}
}
impl<S> SignalInner<S> {
fn new() -> SignalInner<S> {
SignalInner {
callbacks: RefCell::new(Vec::new()),
pending_callbacks: RefCell::new(Vec::new()),
pending_events: RefCell::new(VecDeque::new()),
}
}
fn insert(&self, weak: WeakCallback<S>) {
// attempt to insert the new callback
if let Ok(mut guard) = self.callbacks.try_borrow_mut() {
// success, insert it
guard.push(weak);
} else {
// The callback list is already borrowed, this means that this insertion is
// done from within a callback.
// In that case, insert the callback into the pending list, `send`
// will insert it in the callback list when it is finished dispatching
// the current event.
self.pending_callbacks.borrow_mut().push(weak);
}
}
fn send(&self, event: S) {
// insert the new event into the pending list
self.pending_events.borrow_mut().push_back(event);
// now try to dispatch the events from the pending list
// new events might be added by other callbacks in the process
// so we try to completely drain it before returning
//
// If we cannot get the guard, that means an other dispatching is
// already in progress. It'll empty the pending list, so there is
// nothing more we need to do.
if let Ok(mut guard) = self.callbacks.try_borrow_mut() {
// We cannot just use `while let` because this would keep the
// borrow of self.pending_events alive during the whole loop, rather
// than just the evaluation of the condition. :/
loop {
let next_event = self.pending_events.borrow_mut().pop_front();
if let Some(event) = next_event {
// Send the message, cleaning up defunct callbacks in the process
guard.retain(|weak| {
if let Some(cb) = Weak::upgrade(weak) {
(&mut *cb.borrow_mut())(&event);
true
} else {
false
}
});
// integrate any pending callbacks resulting from the dispatching
// of this event
guard.extend(self.pending_callbacks.borrow_mut().drain(..));
} else {
break;
}
}
} }
} }
} }
impl<E> Signaler<E> { /// Trait representing the capability of an object to listen for some signals
pub fn new() -> Signaler<E> { ///
Signaler { /// It is provided so that the signaling system can play nicely into generic
inner: Rc::new(SignalerInner::new()) /// constructs.
} pub trait Linkable<S> {
/// Make this object listen for signals from given signaler
fn link(&mut self, signaler: Signaler<S>);
}
#[cfg(test)]
mod tests {
use super::*;
use std::{cell::Cell, rc::Rc};
#[test]
fn basic_signal() {
let signaler = Signaler::<u32>::new();
let signaled = Rc::new(Cell::new(false));
let signaled2 = signaled.clone();
let _token = signaler.register(move |_| signaled2.set(true));
signaler.signal(0);
assert!(signaled.get());
} }
fn register_closure<F: FnMut(&mut E) + 'static>(&self, f: F) { #[test]
fn remove_callback() {
let signaler = Signaler::<u32>::new();
let token = signaler.register(|&i| assert_eq!(i, 42));
signaler.signal(42);
::std::mem::drop(token);
signaler.signal(41);
let _token = signaler.register(|&i| assert_eq!(i, 39));
signaler.signal(39);
} }
fn send_event(&self, event: &mut E) { #[test]
fn delayed_signal() {
let signaler = Signaler::<u32>::new();
let mut signaled = false;
let sign2 = signaler.clone();
let _token = signaler.register(move |&i| {
if !signaled {
sign2.signal(42);
signaled = true;
} else {
assert_eq!(i, 42);
}
});
signaler.signal(0);
}
#[test]
fn delayed_register() {
let signaler = Signaler::<bool>::new();
let signaled = Rc::new(Cell::new(0u32));
let signaled2 = signaled.clone();
let sign2 = signaler.clone();
let _token1 = signaler.register(move |&original| {
signaled2.set(signaled2.get() + 1);
if original {
let signaled3 = signaled2.clone();
sign2.register(move |_| signaled3.set(signaled3.get() + 1)).leak();
sign2.signal(false);
}
});
signaler.signal(true);
// Two rounds of signals, the first triggers 1 callback, the second triggers 2
assert_eq!(signaled.get(), 3);
} }
} }

View File

@ -49,7 +49,7 @@ use std::{
use wayland_protocols::unstable::linux_dmabuf::v1::server::{ use wayland_protocols::unstable::linux_dmabuf::v1::server::{
zwp_linux_buffer_params_v1::{ zwp_linux_buffer_params_v1::{
Error as ParamError, Request as ParamsRequest, ZwpLinuxBufferParamsV1 as BufferParams Error as ParamError, Request as ParamsRequest, ZwpLinuxBufferParamsV1 as BufferParams,
}, },
zwp_linux_dmabuf_v1, zwp_linux_dmabuf_v1,
}; };