Smithay / calloop

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

Error handling IO Errors vs Higher level errors. #202

Open vikigenius opened 1 month ago

vikigenius commented 1 month ago

I am creating an event source to handle IPC Events by reading a socket:

pub struct IPCSource {
    socket_source: calloop::generic::Generic<UnixListener>,
}

impl calloop::EventSource for IPCSource {
    type Event = Message; // Change this
    type Metadata = ();
    type Ret = Result<(), IPCError>;
    type Error = IPCError;
}

My problem is that the socket_souce.process_events callback expects to return Result<PostAction, std::io::Error> but the IPCSource callback needs to return IPCError

        self.socket_source
            .process_events(readiness, token, |socket_readiness, socket| {
                if socket_readiness.readable {
                    for stream in socket.incoming() {
                       // Would like to call the callback here
                       // But all errors should be io:Error
                    }
                }
                Ok(calloop::PostAction::Continue)
            });

I read the error handling chapter in the book: https://smithay.github.io/calloop/ch02-06-errors.html but it doesn't talk about the context of generic wrappers?

Should I be converting the IPCError to io:Error and then back to IPCError when returning from process_events? Is there a more ergonomic way to do this?

How am I supposed to handle this?

notgull commented 1 month ago

You can pass the error outside of the closure, like so:

let mut err = None;

let action = self.socket_source.process_events(readiness, token, |_, _| {
    match do_something() {
        Ok(_) => Ok(PostAction::Continue),
        Err(e) => {
            err = Some(e);
            Ok(PostAction::Continue)
        }
    }
})?;

err.map_or(Ok(action), Err)

Does this work for you?

vikigenius commented 1 month ago

Yup this does work.

I did take a look at what the generic process_events is doing:

        // If the token is invalid or not ours, skip processing.
        if self.token != Some(token) {
            return Ok(PostAction::Continue);
        }

        callback(readiness, self.file.as_mut().unwrap())

This is not a lot. Can't I just directly get a reference to the underlying object self.socket_source.get_ref and process it directly without calling process_events on it?

elinorbgr commented 1 month ago

Can't I just directly get a reference to the underlying object self.socket_source.get_ref and process it directly without calling process_events on it?

You can yes, the main reason of this logic here is as a convenience for event sources that bundle multiple sub-sources (like monitoring multiple FDs for example), this checks makes sure that only the sub-sources that are actually ready are being processed. If this is a convenience you do not need, you can bypass it.