Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thread Safety Error on ReadHandler #65

Closed
Jarves42 opened this issue Aug 10, 2020 · 3 comments
Closed

Thread Safety Error on ReadHandler #65

Jarves42 opened this issue Aug 10, 2020 · 3 comments

Comments

@Jarves42
Copy link

I have created a TCP server which adds a random number to evmap and sends all the value for a given test key.

I am facing std::cell::Cell<()> cannot be shared between threads safely error if I spawn a thread for each HTTP req.

evmap is concurrent data struct so I think we are able to do crud op concurrently using multiple threads.

Below is the exact code.

Please point out if anything wrong I am doing or any change by which I can read/update evmap using threads.

use std::sync::Mutex;
use rand::Rng;
use std::net::{TcpListener};
use std::io::{Write};
use std::thread;

fn main() {
    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
    println!("Listening for connections on port {}", 8080);

    let (read_handler, write_handler) = evmap::new();
    let write_lock = Mutex::new(write_handler);

    for stream in listener.incoming() {
        match stream {
            Ok(mut stream) => {
                thread::spawn(|| {
                    let mut rand_generator = rand::thread_rng();
                    let mut lock = write_lock.lock().unwrap();
                    let random_number = rand_generator.gen_range(0, 100);
                    lock.insert("1".to_string(), random_number);
                    lock.refresh();

                    let mut response = String::new();
                    if let Some(values) = read_handler.clone().get("1") {
                        for value in values.iter() {
                            if response.len() > 0 {
                                response.push_str("~");
                            }
                            response.push_str(format!("{}", *value).as_str());
                        }
                    }

                    let response = format!("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n<html><body>{}</body></html>\r\n", response);
                    stream.write(response.as_bytes()).unwrap();
                });
            }
            Err(e) => {
                println!("Unable to connect: {}", e);
            }
        }
    }
}
@jonhoo
Copy link
Owner

jonhoo commented Aug 10, 2020

Hi there! So, what you're running into is that evmap does not let you share a single ReadHandle. Instead, you need to keep one clone of the handle for each concurrent place you want to access the map from. In your case above, just add

let read_handler = read_handler.clone()

above the call to thread::spawn, and make the closure move ||. That should do it.

@Shoowa
Copy link

Shoowa commented Aug 12, 2020

After cloning the read_handler like @jonhoo advised, I think the mutex can be wrapped with a thread-safe reference-counting pointer, i.e. Arc.

let write_lock = Arc::new(Mutex::new(write_handler));

And then inside the OK branch, cloning the reference to the lock with Arc will successfully pass into the closure.

let write_lock = Arc::clone(&write_lock);

After inserting the aforementioned changes, I successfully ran the script.

@jonhoo
Copy link
Owner

jonhoo commented Aug 12, 2020

Yes, the write handle does not support concurrent operations in evmap, so you'd need to wrap it in a Mutex to share it :)

@jonhoo jonhoo closed this as completed Nov 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants