是否可以在函数调用之间释放和锁定互斥锁?

时间:2020-12-29 15:57:00

标签: rust synchronization mutex

我正在尝试使用 Mio 创建一个简单的事件循环。每个事件都会根据事件的 Token 调用关联的回调。事件循环在与其余代码不同的线程上运行。

事件可以通过 register 函数注册,但是为了注册事件,它必须将它添加到相同的 HashMap 回调(并访问相同的 Mio Poll 结构)事件循环正在迭代。

问题是回调本身可能会注册事件,在这种情况下,不可能采用互斥锁,因为事件循环具有互斥锁。是否可以在调用回调时以某种方式删除 Mutex 函数中的 start()?虽然,这似乎性能不佳。

在 Rust 中有没有更好的方法来处理这个问题?

use mio::event::Event;
use mio::net::{TcpListener, TcpStream};
use mio::{event, Events, Interest, Poll, Token};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread::JoinHandle;
use std::{io, thread};

pub trait HookCb: Send {
    fn call(&self, event: &Event);
}

impl<F: Send> HookCb for F
where
    F: Fn(&Event),
{
    fn call(&self, event: &Event) {
        self(event)
    }
}

struct EventLoopInner {
    handlers: HashMap<Token, Box<dyn HookCb>>,
    poll: Poll,
}

pub struct EventLoop {
    inner: Mutex<EventLoopInner>,
}

impl EventLoop {
    pub fn new() -> io::Result<Self> {
        Ok(Self {
            inner: Mutex::new(EventLoopInner {
                handlers: HashMap::new(),
                poll: Poll::new()?,
            }),
        })
    }

    pub fn start(&self) -> io::Result<()> {
        let mut events = Events::with_capacity(1024);

        let inner = &mut *self.inner.lock().unwrap(); // Inner mutex taken
        loop {
            inner.poll.poll(&mut events, None)?;
            for event in events.iter() {
                match event.token() {
                    Token(v) => {
                        if let Some(cb) = inner.handlers.get(&Token(v)) {
                            // TODO release the inner mutex before here so that the callback can invoke register
                            cb.call(event)
                        }
                    }
                }
            }
        }
    }

    pub fn register<S>(
        &self,
        source: &mut S,
        interest: Interest,
        cb: impl HookCb + 'static,
    ) -> io::Result<Token>
    where
        S: event::Source + std::marker::Send,
    {
        let mut inner = self.inner.lock().unwrap(); // Cannot get this lock after start() has been invoked
        let token = Token(inner.handlers.len());
        inner.poll.registry().register(source, token, interest)?;
        inner.handlers.insert(token, Box::new(cb));
        Ok(token)
    }
}

struct ServerConn {
    listener: Option<TcpListener>,
    connections: HashMap<Token, TcpStream>,
}

struct Server {
    eventloop: Arc<EventLoop>,
    thread: Option<JoinHandle<()>>,
    conn: Arc<Mutex<ServerConn>>,
}

impl Server {
    pub fn new() -> Self {
        Self {
            eventloop: Arc::new(EventLoop::new().unwrap()),
            thread: None,
            conn: Arc::new(Mutex::new(ServerConn {
                listener: None,
                connections: HashMap::new(),
            })),
        }
    }

    pub fn listen(&mut self, addr: &str) {
        {
            let mut conn = self.conn.lock().unwrap();
            conn.listener = Some(TcpListener::bind(addr.parse().unwrap()).unwrap());

            let cb_conn = Arc::clone(&self.conn);
            let cb_eventloop = Arc::clone(&self.eventloop);

            self.eventloop
                .register(
                    conn.listener.as_mut().unwrap(),
                    Interest::READABLE,
                    move |e: &Event| {
                        Self::accept_cb(e, &cb_conn, &cb_eventloop);
                    },
                )
                .unwrap();
        } // Unlock conn

        let t_eventloop = Arc::clone(&self.eventloop);
        self.thread = Some(thread::spawn(move || {
            t_eventloop.start().unwrap();
        }));

        self.thread.take().unwrap().join().unwrap(); // Temp fix to block main thread so application does not exit
    }

    fn accept_cb(_e: &Event, conn: &Arc<Mutex<ServerConn>>, evloop: &Arc<EventLoop>) {
        let mut conn_lock = conn.lock().unwrap();
        loop {
            let (mut stream, addr) = match conn_lock.listener.as_ref().unwrap().accept() {
                Ok((stream, addr)) => (stream, addr),
                Err(_) => return,
            };
            println!("Accepted connection from: {}", addr);

            // TODO can this clone be avoided?
            let cb_conn = Arc::clone(conn);
            let cb_evloop = Arc::clone(evloop);
            let token = evloop
                .register(
                    &mut stream,
                    Interest::READABLE.add(Interest::WRITABLE),
                    move |e: &Event| {
                        Self::conn_cb(e, &cb_conn, &cb_evloop);
                    },
                )
                .unwrap();
            conn_lock.connections.insert(token, stream);
        }
    }

    pub fn conn_cb(e: &Event, conn: &Arc<Mutex<ServerConn>>, _evloop: &Arc<EventLoop>) {
        let conn_lock = conn.lock().unwrap();

        let mut _connection = conn_lock.connections.get(&e.token()).unwrap();
        if e.is_writable() {
            // TODO write logic -- connection.write(b"Hello World!\n").unwrap();
            // TODO evloop.reregister(&mut connection, event.token(), Interest::READABLE)?
        }
        if e.is_readable() {
            // TODO read logic -- connection.read(&mut received_data);
        }
    }
}

fn main() {
    let mut s = Server::new();
    s.listen("127.0.0.1:8000");
}

0 个答案:

没有答案