Allright, currently I'm trying to design a server struct, which relies heavily on shared mutable state. The rough structure looks something like the following:
pub struct Server {
// worker pool to process incoming TCP and UDP messages, etc.
thread_pool: ThreadPool,
// handler functions which will be registered before the server is launched
// the thread_pool will use those to process the requests
handler_a: fn(...) -> ...,
handler_b: fn(...) -> ...,
handler_c: fn(...) -> ...,
// and here we go: a sh*tload of configuration parameters
main_server_name: Arc<Mutex<T>>,
main_server_port: Arc<Mutex<S>>,
language: Arc<Mutex<T>>,
mod_name: Arc<Mutex<X>>,
mod_desc: Arc<Mutex<T>>,
test_module: Arc<Mutex<Y>>,
work_as_client: Arc<Mutex<Z>>,
alive_counter: Arc<Mutex<W>>,
...
// many, many more
}
The problem is, I can't design the server struct to be somewhat stateless like an REST API. The server can change it's internal configuration parameters dynamically depending on the incoming network connections (the handler functions will change it for example). What I don't like about that design is that whenever I want to read or write to an configuration parameter, I have the use the MutexGuard like so:
let mut language_guard = self.language.lock().unwrap();
I may be totally wrong, but this seems to be a bit tedious to do this every time (especially with getters and setters to encapsulate the acquiring of the mutex). So my next idea was to put all the configuration parameters in a separate struct:
pub struct ServerConfig {
param_a,
param_b,
...
}
pub struct Server {
// worker pool to process incoming TCP and UDP messages, etc.
thread_pool: ThreadPool,
// handler functions which will be registered before the server is launched
// the thread_pool will use those to process the requests
handler_a: fn(...) -> ...,
handler_b: fn(...) -> ...,
handler_c: fn(...) -> ...,
config: Arc<Mutex<ServerConfig>>,
}
...
// and to use it
let mut config_guard = self.config.lock().unwrap();
println!("Current language = {}", config_guard.language);
config_guard.work_as_client = false;
...
Which has the disadvantage that every time a need to read or update a configuration parameter, I'm locking the whole config
variable, which seems to me would be bad for fast multithreading.
Which leads to my actual question: are there any best practices for Rust on how to handle my problem so this can be achieved in a "smoother" way?
Best regards
0xBIT