Smithay / calloop

A callback-based Event Loop
MIT License
176 stars 34 forks source link

Double borrows when registering sources and idle callbacks #109

Closed i509VCB closed 1 year ago

i509VCB commented 2 years ago

Inside of pre_run and post_run, registering an event source will cause a double borrow.

This is also possible inside of the callback provided to insert_idle since registering an idle callback inside of an idle callback will double borrow.

I reproduced the double borrow with the following code:

use calloop::{
    ping::PingSource,
    EventLoop, EventSource, LoopHandle, Poll, PostAction, Readiness, Token, TokenFactory,
};

struct State {
    loop_handle: LoopHandle<'static, Self>,
}

#[test]
fn insert_in_dispatch() {
    let mut event_loop = EventLoop::try_new().unwrap();
    let handle = event_loop.handle();

    let mut state = State {
        loop_handle: handle.clone(),
    };

    let timer = OneshotRemove::new();

    handle.insert_source(timer, handle_source).unwrap();

    event_loop.run(None, &mut state, |_| {}).unwrap();
}

fn handle_source(_: (), _: &mut (), state: &mut State) {
    state
        .loop_handle
        .insert_source(OneshotRemove::new(), handle_source)
        .unwrap();
}

struct OneshotRemove {
    source: PingSource,
}

impl OneshotRemove {
    pub fn new() -> Self {
        let (ping, source) = calloop::ping::make_ping().unwrap();
        let source = Self { source };

        // Immediately ping the source to dispatch on register
        ping.ping();
        source
    }
}

impl EventSource for OneshotRemove {
    type Event = ();
    type Metadata = ();
    type Ret = ();
    type Error = calloop::Error;

    fn pre_run<F>(&mut self, mut callback: F) -> calloop::Result<()>
    where
        F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
    {
        callback((), &mut ());
        Ok(())
    }

    fn process_events<F>(
        &mut self,
        readiness: Readiness,
        token: Token,
        _callback: F,
    ) -> Result<PostAction, Self::Error>
    where
        F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
    {
        self.source
            .process_events(readiness, token, |_, _| {})
            .unwrap();

        Ok(PostAction::Remove)
    }

    fn register(
        &mut self,
        poll: &mut Poll,
        token_factory: &mut TokenFactory,
    ) -> calloop::Result<()> {
        self.source.register(poll, token_factory)
    }

    fn reregister(
        &mut self,
        poll: &mut Poll,
        token_factory: &mut TokenFactory,
    ) -> calloop::Result<()> {
        self.source.reregister(poll, token_factory)
    }

    fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> {
        self.source.unregister(poll)
    }
}

The following backtrace is produced: https://gist.github.com/i509VCB/0b27d3a5eb57ae7594243cd5b4833cf7