Skip to content

Latest commit

 

History

History
147 lines (119 loc) · 4.38 KB

design-patterns.md

File metadata and controls

147 lines (119 loc) · 4.38 KB

Daemon

Controller pattern

There are a series of traits in the daemon for use with controller objects. Not all traits are required:

  • Reloadable, for controllers that need the ability to reload (typically on start)
  • ZbusAdd, for controllers that have zbus derive. These need to run on the zbus server.
  • CtrlTask, for controllers that need to run tasks every loop.
  • GetSupported, see if the hardware/functions this controller requires are supported.

The first 3 trait objects get owned by the daemon methods that required them, which is why an Arc<Mutex<T>> is required.

Generally the actual controller object will need to live in its own world as its own struct. Then for each trait that is required a new struct is required that can have the trait implemented, and that struct would have a reference to the main controller via Arc<Mutex<T>>.

Example

Main controller:

For a very simple controller that doesn't need exclusive access you can clone across threads

#[derive(Clone)]
pub struct CtrlAnime {
    <things the controller requires>
    config: Arc<Mutex<Config>>,
}

// This is the task trait used for such things as file watches, or logind
// notifications (boot/suspend/shutdown etc)
impl crate::CtrlTask for CtrlAnime {}

// The trait to easily add the controller to Zbus to enable the zbus derived functions
// to be polled, run, react etc.
impl crate::ZbusAdd for CtrlAnime {}

impl CtrlAnime {}

Otherwise, you will need to share the controller via mutex

pub struct CtrlAnime {
    <things the controller requires>
}
// Like this
#[derive(Clone)]
pub struct CtrlAnimeTask(Arc<Mutex<CtrlAnime>>);

#[derive(Clone)]
pub struct CtrlAnimeZbus(Arc<Mutex<CtrlAnime>>);

impl CtrlAnime {}

The task trait:

// Mutex should always be async mutex
pub struct CtrlAnimeTask(Arc<Mutex<CtrlAnime>>);

impl crate::CtrlTask for CtrlAnimeTask {
    // This will run once only
    async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
       let lock self.inner.lock().await;
        <some action>
        Ok(())
    }

    // This will run until the notification stream closes (which in most cases will be never)
    async fn create_tasks(&self, signal_ctxt: SignalContext<'static>) -> Result<(), RogError> {
        let inner1 = self.inner.clone();
        let inner2 = self.inner.clone();
        let inner3 = self.inner.clone();
        let inner4 = self.inner.clone();
        // This is a free method on CtrlTask trait
        self.create_sys_event_tasks(
            // Loop is required to try an attempt to get the mutex *without* blocking
            // other threads - it is possible to end up with deadlocks otherwise.
            move || loop {
                if let Some(lock) = inner1.try_lock() {
                    run_action(true, lock, inner1.clone());
                    break;
                }
            },
            move || loop {
                if let Some(lock) = inner2.try_lock() {
                    run_action(false, lock, inner2.clone());
                    break;
                }
            },
            move || loop {
                if let Some(lock) = inner3.try_lock() {
                    run_action(true, lock, inner3.clone());
                    break;
                }
            },
            move || loop {
                if let Some(lock) = inner4.try_lock() {
                    run_action(false, lock, inner4.clone());
                    break;
                }
            },
        )
        .await;
    }
}

The reloader trait

pub struct CtrlAnimeReloader(Arc<Mutex<CtrlAnime>>);

impl crate::Reloadable for CtrlAnimeReloader {
    async fn reload(&mut self) -> Result<(), RogError> {
        let lock = self.inner.lock().await;
        <some action>
        Ok(())
    }
}

The Zbus requirements:

pub struct CtrlAnimeZbus(Arc<Mutex<CtrlAnime>>);

#[async_trait]
impl crate::ZbusAdd for CtrlAnimeZbus {
    fn add_to_server(self, server: &mut zbus::ObjectServer) {
        // This is a provided free helper trait with pre-set body. It will move self in-to.
        Self::add_to_server_helper(self, "/org/asuslinux/Anime", server).await;
    }
}

#[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnimeZbus {
    async fn <zbus method>() {
       let lock = self.inner.lock().await;
        <some action>
    }
}

The controller can then be added to the daemon parts as required.