Home > Software engineering >  How to properly use self when creating a new thread from inside a method in Rust
How to properly use self when creating a new thread from inside a method in Rust

Time:01-19

I am creating a server that stores the TcpStream objects inside a Vec to be used later.The problem is the function that listens for new connections and adds them to the Vec runs forever in a separate thread and doesn't allow other threads to read the Vec.

pub struct Server {
    pub connections: Vec<TcpStream>,
}

impl Server {
    fn listen(&mut self) {
        println!("Server is listening on port 8080");
        let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
        
        loop {
            let stream = listener.accept().unwrap().0;
            println!("New client connected: {}", stream.peer_addr().unwrap());

            //should block for write here
            self.connections.push(stream);
            //should release write lock
        }
    }

    pub fn run(self) {
        let arc_self = Arc::new(RwLock::new(self));
        let arc_self_clone = arc_self.clone();

        //blocks the lock for writing forever because of listen()
        let listener_thread = thread::spawn(move || arc_self_clone.write().unwrap().listen());

        loop {
            let mut input = String::new();
            io::stdin().read_line(&mut input).unwrap();

            if input.trim() == "1" {
                //can't read because lock blocked for writing
                for c in &arc_self.read().unwrap().connections {
                    println!("testing...");
                }
            }
        }
    }
}

In the current example the server accepts connections but does not allow the main thread to read the connections vector.I tought about making the listen function run at a fixed interval (1-5s) so it allows other threads to read the vector in that time but listener.accept() blocks the thread aniway so i don't think that is a valid solution.I would also prefer if it were to run forever if possible and block access to the vector only when it needs to write (a new client connects) and while it waits for clients to connect not block the reading access of other threads to the connections vector.

CodePudding user response:

You could just wrap connections in a RwLock instead of entire self, as shown below, but I would recommend using a proper synchronisation primitive like a channel.

pub struct Server {
    pub connections: RwLock<Vec<TcpStream>>,
}

impl Server {
    fn listen(&self) {
        println!("Server is listening on port 8080");
        let listener = TcpListener::bind("127.0.0.1:8080").unwrap();

        loop {
            let stream = listener.accept().unwrap().0;
            println!("New client connected: {}", stream.peer_addr().unwrap());

            //should block for write here
            self.connections.write().unwrap().push(stream);
            //should release write lock
        }
    }

    pub fn run(self) {
        let arc_self = Arc::new(self);
        let arc_self_clone = arc_self.clone();

        let listener_thread = thread::spawn(move || arc_self_clone.listen());

        loop {
            let mut input = String::new();
            io::stdin().read_line(&mut input).unwrap();

            if input.trim() == "1" {
                for c in &*arc_self.connections.try_read().unwrap() {
                    println!("testing...");
                }
            }
        }
    }
}
  • Related