I'm trying to port some threading C++ code to rust, hitting strange issues on my way.
I have a Server
struct defined as:
pub struct Server {
server: *mut ffi::server,
queue: Queue,
methods: Vec<SyncRequest>,
workers: ::threadpool::ThreadPool,
running_callbacks: Mutex<usize>,
}
It wraps some bare c++ pointers and a bunch of other things but is pretty much immutable at that point (all modifications throughout server lifetime happen inside SyncRequest
structs).
The code works reasonably fine if used in one thread, but getting this to work over a threadpool::ThreadPool
is surprisingly awful.
First, I want to keep Server
implementation details, such as its threading internal and not exposed to the library user. The basic use case would be for user to call server.start()
and that would kick off Server::run_rpc()
in thread pool as required, while blocking on a shared mutex. start()
is guaranteed to not return until any worker thread is alive, so I guess it's pretty sane to pass &self through into thread.
So, to the code that works:
fn schedule_callback(&mut self) {
{
let mut running_callbacks = self.running_callbacks.lock().unwrap();
*running_callbacks += 1;
}
// turn Server into something Send-able
let selfptr: *mut ::libc::types::common::c95::c_void = unsafe { ::std::mem::transmute(&self) };
// which *mut c_void isn't...
let selfiptr: usize = selfptr as usize;
self.workers.execute(move ||{
// at this point of time I have no idea where the *_self lives. Double pointer here is confusing
let _self: &mut &mut Server = unsafe { ::std::mem::transmute(selfiptr) };
_self.run_rpc();
});
}
A bunch of problems I got on the way:
Server
-owned insides don't implement Send
and I have vague ideas on how can I even implement Send
for *mut c_void
.
I can't wrap Server
in Arc
anyway, as that will require to make the method static, and expose Arc
usage to public API.
The way I wrote it is basically fitting C++ way of thinking into rust, looks horrible and code is twice as big. What would be the idiomatic way to solve this problem?