So if you had a MUD sever that handled each tcp connection in a separate process,
for stream in acceptor.incoming() {
match stream {
Err(e) => { /* connection failed */ }
Ok(stream) => spawn(proc() {
handle_client(stream)
})
}
}
What would be the strategy for sharing mutable world data for that server? I can imagine n connections responding to commands from the user. Each command needing to visit and possibly modify the world.
pub struct Server<'a> {
world: World<'a>
}
pub struct World<'a> {
pub chat_rooms: HashMap<&'a str, ChatRoom<'a>>
}
impl<'a> World<'a> {
pub fn new() -> World<'a> {
let mut rooms = HashMap::new();
rooms.insert("General", ChatRoom::new("General"));
rooms.insert("Help", ChatRoom::new("Help"));
World{chat_rooms: rooms}
}
}
Would Arc be the way to go?
let shared_server = Arc::new(server);
let server = shared_server.clone();
spawn(proc() {
// Work with server
});
What about scaling to 100 or 1000 users? I'm just looking for a nudge in the right direction.
An Arc
will let you access a value from multiple tasks, but it will not allow you to borrow the value mutably. The compiler cannot verify statically that only one task would borrow the value mutably at a time, and mutating a value concurrently on different tasks leads to data races.
Rust's standard library provides some types that allow mutating a shared object safely. Here are two of them:
Mutex
: This is a simple mutual exclusion lock. A Mutex
wraps the protected value, so the only way to access the value is by locking the mutex. Only one task at a time may access the wrapped value.RWLock
: This is a reader-writer lock. This kind of lock lets multiple tasks read a value at the same time, but writers must have exclusive access. This is basically the same rules that the borrow checker and RefCell
have (except the lock waits until the borrow is released, instead of failing compilation or panicking).You'll need to wrap a Mutex
or a RWLock
in an Arc
to make it accessible to multiple tasks.