I have some objects from third-party libraries that are neither send nor sync, yet I need to use them from other threads. Blocking is fine, locking is fine, stopping the world is fine.
Wrapping the type into a Mutex<T> makes it shareable. Wrapping the mutex into an arc Arc<Mutex<T>> does not change anything, it can still not be sent.
How can I send the value?
Have I misunderstood Arc<Mutex<T>>?
Here is the complete function:
pub fn run_server(compute: impl Fn(Request) -> Response) {
// compute cannot be shared or synced because it contains types from third party libraries
let compute = Arc::new(Mutex::new(compute));
// the passed callback must be send + sync
rouille::start_server_with_pool("localhost:3000", Some(1), move |request| {
let compute = compute.lock().unwrap();
let answer = compute(request);
answer
});
}
Wrapping in a Mutex can make a Send + !Sync type Send + Sync, but if the underlying type is not Send, there's nothing a Mutex can do. An Arc can share types, but if the underlying type is not Sync, it can't share it across threads.
More or less the only approach to share !Send + !Sync types is to not share them, and instead simulate it by having other threads use message passing to tell the thread do stuff to the object.
I can't see how to implement your suggestion it with this specific architecture. I guess I'm going to code up a simple non-multithreaded server myself, with TCP and an HTTP parsing library, as my use case is really not that complicated and I need it to "just work" (but also high performance crates from Rust).
The form of communication would be equivalent to Fn(Request) -> Response. How can I do this responding mechanism? That's certainly not what the std::sync::mpsc::channels, are meant for?
I found a solution that avoided multithreading in general using tiny_http. Using channels would be the better architecture in general, but in this case would have been overly complicated and not required. Thanks anyways for your suggetions
I realize you've already found a solution, but for future reference, I think you probably never needed !Send objects in the first place. It is likely that you just need to add a Send trait bound to your function signature.
Fn(Request) -> Response is not an object type, but a trait, meaning, essentially, an interface that multiple different types can implement. The impl keyword means that the function must accept any type that "implements" that trait. (For completeness: it also means that the exact type is determined at compile time rather than at runtime.)
Send and Sync are also traits. But, because it's possible for some types to implement Fn(Request) -> Responsewithout implementing Send, your function parameter, impl Fn(Request) -> Response, must be usable with types that are !Sync and/or !Send.
But unless you actually need to call run_server with a !Send type, this restriction is unnecessary! So you can remove it by requiring compute to implement both the Fn... trait and the Send trait. The syntax for this uses + to specify multiple traits:
I think your analysis is probably correct! But it's worth mentioning that there is one common !Send type: Rc. If the library uses Rcs to manage data and that can't be changed, then it could really be a problem.
Yes, this would be the best solution. I tried that first, but unfortunately it didn't work because the closure needed to capture variables that neither Send nor Sync. I honestly don't know why those types are not Send, I have not checked it, as they are defined in a third party library, which I can't change right now. For future visitors this approach should be tried first