How to share epoll fd between threads


I am trying to implement an epoll loop like I would do it in C/C++ but I get problems in sharing the epollfd.
epoll::wait should run in an own thread so that I can add,del,mod filedescriptors from outside and close the epollfd from outside.

I want to use epoll::wait with an infinite timeout. So this call is blocking. It would be released if the epollfd is closed.

If I encapsulate the epollfd into an Arc::Mutex it would block on epoll::wait and I got a deadlock on add,del,mod filedescriptors and on clonsing the epollfd from outside.

How could I solve this in Rust?


use std::{thread::JoinHandle, sync::{atomic::{AtomicBool, Ordering, AtomicI32}, Arc}};

struct Io {
    epfd: i32,
    hdl: Option<JoinHandle<()>>,
    is_running: Arc<AtomicBool>,

impl Drop for Io {
    fn drop(&mut self) {
        println!("1");, Ordering::Relaxed);   
        //I think it is not working because this is not the epollfd that is used in epoll::wait    
        if let Some(hdl) = self.hdl.take() {

impl Io {
    pub fn new() -> std::io::Result<Self> {
        let epfd = epoll::create(false)?;

        Ok(Self {
            hdl: None,
            is_running: Arc::new(AtomicBool::new(false)),

    pub fn add(&self, fd: i32) {
            epoll::Event::new(epoll::Events::EPOLLIN | epoll::Events::EPOLLRDHUP | epoll::Events::EPOLLHUP | epoll::Events::EPOLLERR,

    fn start(&mut self) {, Ordering::Relaxed);

        //How to share epfd properly? 
        let epfd = self.epfd;   

        let is_running = Arc::clone(&self.is_running);
        self.hdl = Some(std::thread::spawn(move || {
            while is_running.load(Ordering::Relaxed) {
                println!("before epoll");
                let mut events = [epoll::Event::new(epoll::Events::empty(), 0); 10];
                let event_count = epoll::wait(epfd, -1, &mut events).unwrap();
                println!("after epoll");

Just make all the methods take &self. Then there is no problem sharing it.

I could do that, but then I have to discard the Joinhandle and create a detached thread. It is a long running thread and should run as long as the Io-Object lives. So will it be cleaned automatically by Rust/OS after drop of Io was called?

You can still use &mut self in your destructor.