implement thread safe function object in rust - multithreading

I am trying to implement some stability pattern (introduced in cloud-native-go, e.g. https://github.com/cloud-native-go/examples/blob/main/ch04/circuitbreaker.go) in rust lang.
The basic idea is to wrap a function in a closure, which keeps some states about the function call. I also want my implementation to be thread safe.
I finally come up with something like this:
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
#[derive(PartialEq, Debug, Clone)]
pub(crate) enum Error {
UnReachable,
CircuitError,
}
pub(crate) type Circuit = Arc<Mutex<dyn Fn() -> Result<String, Error>>>;
pub(crate) fn fail_after(threshold: usize) -> Circuit {
let cnt = Arc::new(Mutex::new(0));
let f = move || {
let mut c = cnt.lock().unwrap();
*c += 1;
if *c > threshold {
return Err(Error::CircuitError);
} else {
return Ok("ok".to_owned());
}
};
return Arc::new(Mutex::new(f));
}
#[test]
fn test_debounce_first() {
let c = fail_after(1);
assert!(c.lock().unwrap()().is_ok());
assert!(c.lock().unwrap()().is_err());
}
#[test]
fn test_debounce_first_data_race() {
let c = fail_after(1);
let mut results = vec![];
for _ in 0..10 {
let d1 = Arc::clone(&c);
let h = thread::spawn(move || {
let r: Result<String, Error> = d1.lock().unwrap()();
r
});
results.push(h)
}
for result in results {
let r = result.join().unwrap();
assert!(r.is_ok())
}
}
(rust playground)
The multi thread test give compile error:
error[E0277]: `(dyn Fn() -> Result<String, Error> + 'static)` cannot be sent between threads safely
--> src/lib.rs:41:17
|
41 | let h = thread::spawn(move || {
| ^^^^^^^^^^^^^ `(dyn Fn() -> Result<String, Error> + 'static)` cannot be sent between threads safely
|
= help: the trait `Send` is not implemented for `(dyn Fn() -> Result<String, Error> + 'static)`
= note: required because of the requirements on the impl of `Sync` for `Mutex<(dyn Fn() -> Result<String, Error> + 'static)>`
= note: required because of the requirements on the impl of `Send` for `Arc<Mutex<(dyn Fn() -> Result<String, Error> + 'static)>>`
= note: required because it appears within the type `[closure#src/lib.rs:41:31: 44:10]`
note: required by a bound in `spawn`
I wonder how can I resolve this? I have already wrapped the function object with Arc and Mutex, why the compiler still complains?

Short answer: change dyn Fn() -> Result<String, Error> to dyn Fn() -> Result<String, Error> + Send + 'static. The original definition of the type makes no guarantees about how long the dyn Fn lives or whether or not it can be sent between threads safely.
Another note, you're probably going to have to further modify it to Box<dyn Fn() -> Result<String, Error> + Send + 'static> since I see you're constructing your Mutex with new which requires the type it contains to be Sized, however dyn Trait is unsized. I have never tried constructing a mutex with an unsized type so you may need to look into that further.
Edit
You don't need to box the dyn Fn() -> _ because the Arc<Mutex<_>> is initially constructed with the concrete closure, but then coerces to the unsized Mutex<dyn Fn() -> _> due to the third blanket implementation mentioned here and Arc's implementation of CoerceUnsized.

Related

Covariance of Box<dyn FnOnce(T)> in rust

I have a function that expects a short lived object. I would expect that I would be able to always pass it a long lived object. But I am getting a strange error when I try to encode that:
type F<'arg> = Box<dyn FnOnce(&'arg ())>;
fn contravar<'small, 'large: 'small>(f: F<'small>) -> F<'large> {
f
}
playground
Particularly:
error: lifetime may not live long enough
--> src/lib.rs:3:5
|
2 | fn contravar<'small, 'large: 'small>(f: F<'small>) -> F<'large> {
| ------ ------ lifetime `'large` defined here
| |
| lifetime `'small` defined here
3 | f
| ^ function was supposed to return data with lifetime `'large` but it is returning data with lifetime `'small`
|
= help: consider adding the following bound: `'small: 'large`
It seems like F is invariant for its argument but I would have guessed that it's contravariant. Am I missing something? Is there a way to make F<'arg> really contravariant for 'arg?
Edit: it looks like the "problem" is that rust wants to treat all generic traits the same (including Fn/FnMut/FnOnce). My opinion is that those 3 are and should be treated special especially given that they are the only way to refer to closures. For that reason I opened an issue
The Rust Reference's page on Subtyping and Variance documents that, as of Rust 1.63.0, fn(T) -> () is contravariant over T and that dyn Trait<T> + 'a is invariant over T.
FnOnce, FnMut and Fn are traits, so that means dyn FnOnce(&'a ()) is unfortunately invariant over &'a ().
// Compiles
pub fn contravariant<'a, 'b: 'a>(x: fn(&'a ())) -> fn(&'b ()) { x }
// Doesn't compile
pub fn contravariant2<'a, 'b: 'a>(x: Box<dyn FnOnce(&'a ())>) -> Box<dyn FnOnce(&'b ())> { x }
Is there a way to wrap FnOnce somehow to convince the compiler of the correct variance?
Here's what I could come up with using unsafe code. Note that I'm not making any guarantees as to whether this is sound or not. I don't know of any way to do this without unsafe code.
use std::marker::PhantomData;
trait Erased {}
impl<T> Erased for T {}
pub struct VariantBoxedFnOnce<Arg, Output> {
boxed_real_fn: Box<dyn Erased + 'static>,
_phantom_fn: PhantomData<fn(Arg) -> Output>,
}
impl<Arg, Output> VariantBoxedFnOnce<Arg, Output> {
pub fn new(real_fn: Box<dyn FnOnce(Arg) -> Output>) -> Self {
let boxed_real_fn: Box<dyn Erased + '_> = Box::new(real_fn);
let boxed_real_fn: Box<dyn Erased + 'static> = unsafe {
// Step through *const T because *mut T is invariant over T
Box::from_raw(Box::into_raw(boxed_real_fn) as *const (dyn Erased + '_) as *mut (dyn Erased + 'static))
};
Self {
boxed_real_fn,
_phantom_fn: PhantomData,
}
}
pub fn call_once(self, arg: Arg) -> Output {
let boxed_real_fn: Box<Box<dyn FnOnce(Arg) -> Output>> = unsafe {
// Based on Box<dyn Any>::downcast()
Box::from_raw(Box::into_raw(self.boxed_real_fn) as *mut Box<dyn FnOnce(Arg) -> Output>)
};
boxed_real_fn(arg)
}
}
pub fn contravariant<'a, 'b: 'a>(x: VariantBoxedFnOnce<&'a (), ()>) -> VariantBoxedFnOnce<&'b (), ()> { x }
#[cfg(test)]
mod tests {
use super::*;
fn foo(_x: &()) {}
#[test]
pub fn check_fn_does_not_require_static() {
let f = VariantBoxedFnOnce::new(Box::new(foo));
let x = ();
f.call_once(&x);
}
#[test]
pub fn check_fn_arg_is_contravariant() {
let f = VariantBoxedFnOnce::new(Box::new(foo));
let g = contravariant(f);
let x = ();
g.call_once(&x);
}
}
Here, VariantBoxedFnOnce is limited to functions taking one argument.
The trick is to store a type-erased version of the Box<dyn FnOnce(Arg) -> Output> such that Arg disappears, because we don't want the variance of VariantBoxedFnOnce<Arg, Output> to depend on Box<dyn FnOnce(Arg) -> Output> (which is invariant over Arg). However, there's also a PhantomData<fn(Arg) -> Output> field to provide the proper contravariance over Arg (and covariance over Output).
We can't use Any as our erased type, because only 'static types implement Any, and we have a step in VariantBoxedFnOnce::new() where we have a Box<dyn Erased + '_> where '_ is not guaranteed to be 'static. We then immediately "transmute" it into 'static, to avoid having a redundant lifetime parameter on VariantBoxedFnOnce, but that 'static is a lie (hence the unsafe code). call_once "downcasts" the erased type to the "original" Box<dyn FnOnce(Arg) -> Output>, except that Arg and Output may be different from the original due to variance.

Function that takes an async callback and puts it in a FuturesUnordered queue

Below is code that does exactly what I want but fails on type checking. I’m guessing that it involves Pin, Box, and dyn.
Is this possible?
use async_stream::stream;
use futures::stream::{FuturesUnordered, StreamExt};
pub fn map<U, V, W>(
f: impl Fn(&U) -> W,
items: Vec<U>,
) -> impl futures::Stream<Item = V>
where
V: Send,
W: futures::Future<Output = V> + Send,
{
stream! {
let mut futures = FuturesUnordered::new();
let mut i = 2;
if 2 <= items.len() {
futures.push(tokio::spawn(f(&items[0])));
futures.push(tokio::spawn(f(&items[1])));
while let Some(result) = futures.next().await {
let y = result.unwrap();
yield y;
futures.push(tokio::spawn(f(&items[i])));
i += 1
}
}
}
}
#[tokio::main]
async fn main() {
async fn f(x: &u32) -> u32 {
x + 1
}
let input = vec![1, 2, 3];
let output = map(f, input);
futures::pin_mut!(output);
while let Some(x) = output.next().await {
println!("{:?}", x);
}
}
The exact error is:
error[E0308]: mismatched types
--> src/main.rs:40:18
|
40 | let output = map(f, input);
| ^^^ lifetime mismatch
|
= note: expected associated type `<for<'_> fn(&u32) -> impl futures::Future<Output = u32> {f} as FnOnce<(&u32,)>>::Output`
found associated type `<for<'_> fn(&u32) -> impl futures::Future<Output = u32> {f} as FnOnce<(&u32,)>>::Output`
= note: the required lifetime does not necessarily outlive the empty lifetime
I have tried many variants of changing lifetimes, adding annotations of variations on Box and Pin, and applying compiler suggestions. Rather than hacking, I'm trying to understand whether it is even possible to write code that performs this logic.
My mental model has been that Rust cannot be convinced of the safety of references, so some object needs to be boxed in order to give Rust constant (pinned) access to it. I've been trying to figure out which object(s).
As requested by #Chayim Friedman, one of the exact solutions I tried was to set the signature of map to the following.
pub fn map<U, V>(
f: impl Fn(&U) -> Pin<Box<dyn Future<Output = V> + '_>>,
items: Vec<U>,
) -> impl futures::Stream<Item = V>
where
V: Send,
The problem was that rust still complains about tokio::spawn not being sendable:
error[E0277]: `dyn futures::Future<Output = V>` cannot be sent between threads safely
--> src/main.rs:21:26
|
21 | futures.push(tokio::spawn(f(&items[0])));
| ^^^^^^^^^^^^ `dyn futures::Future<Output = V>` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `dyn futures::Future<Output = V>`
= note: required because of the requirements on the impl of `std::marker::Send` for `Unique<dyn futures::Future<Output = V>>`
= note: required because it appears within the type `Box<dyn futures::Future<Output = V>>`
= note: required because it appears within the type `Pin<Box<dyn futures::Future<Output = V>>>`
I don't think you need Pin, Box, or even references, for that matter.
You own the Vec, so just take elements out of it via move. That transfers the ownership of the elements to the f callback, which makes ownership management a lot easier.
use async_stream::stream;
use futures::stream::{FuturesUnordered, StreamExt};
pub fn map<U, W>(f: impl Fn(U) -> W, items: Vec<U>) -> impl futures::Stream<Item = W::Output>
where
W: futures::Future + Send + 'static,
W::Output: Send,
{
stream! {
// Convert into a fused iterator. Fused iterators
// are guaranteed to return `None` continuously after
// their last item.
let mut iter = items.into_iter().fuse();
let mut futures = FuturesUnordered::new();
if let Some(el) = iter.next() {
futures.push(tokio::spawn(f(el)));
}
if let Some(el) = iter.next() {
futures.push(tokio::spawn(f(el)));
}
while let Some(result) = futures.next().await {
let y = result.unwrap();
yield y;
if let Some(el) = iter.next() {
futures.push(tokio::spawn(f(el)));
}
}
}
}
#[tokio::main]
async fn main() {
async fn f(x: u32) -> u32 {
x + 1
}
let input = vec![1, 2, 3];
let output = map(f, input);
futures::pin_mut!(output);
while let Some(x) = output.next().await {
println!("{:?}", x);
}
}
2
3
4
I had to rewrite parts of your code because your version didn't have a stop criterium. It would always just crash by running out-of-bounds on the array access.

One type (function pointer) is more general than the other

I reached this error in this example, and I'm trying to understand what's happening.
use futures::future::{BoxFuture, FutureExt};
use std::sync::Arc;
use futures::lock::Mutex;
struct Message {}
struct Client {}
enum Error {
ConnectionClosed,
}
#[derive(Default)]
struct MyType{}
impl Client {
fn send_and_expect<'a, R: 'a + Default + Send>(
client: Arc<Mutex<Self>>,
message: &'a Message,
) -> BoxFuture<'a, Result<R, Error>> {
async move { Ok(R::default()) }.boxed()
}
pub fn connection_retrier<'a, R: 'a + Default + Send>(
client: Arc<Mutex<Self>>,
f: for<'b> fn(Arc<Mutex<Self>>, &'b Message) -> BoxFuture<'b, Result<R, Error>>,
f_m: &'a Message,
) -> BoxFuture<'a, Result<R, Error>>
{
async move {Ok(R::default())}.boxed()
}
async fn send_with_retry<'a>(
client: Arc<Mutex<Self>>,
message: &'a Message,
) -> Result<MyType, Error> {
let client = Arc::new(Mutex::new(Client{}));
Self::connection_retrier::<MyType>(client, Self::send_and_expect, message)
.await
}
}
Error:
error[E0308]: mismatched types
--> src/lib.rs:36:52
|
36 | Self::connection_retrier::<MyType>(client, Self::send_and_expect, message)
| ^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'b> fn(Arc<_>, &'b Message) -> Pin<Box<(dyn futures::Future<Output = std::result::Result<MyType, Error>> + std::marker::Send + 'b)>>`
found fn pointer `fn(Arc<_>, &Message) -> Pin<Box<dyn futures::Future<Output = std::result::Result<MyType, Error>> + std::marker::Send>>`
Playground
Well, connection_retrier requres a function f: for<'b> fn(Arc<Mutex<Self>>, &'b Message) -> BoxFuture<'b, Result<R, Error>>,. The one I passed seems to fit the signature.
The way I view for<'b> fn... is: I accept a function f for any lifetime 'b presented. So, for the particular lifetime 'a, send_and_expect seems to fit everything.
The only suspicion I have is for the lifetime of R being fixed at 'a in connection_retrier so the f with its for<'b> is not free to give any lifetime for its R. I think the lifetime of the R in f should be 'b. Is it possible to force f to have R with lifetime 'b?
Something like this:
f: for<'b> fn<RR: 'b>(Arc<Mutex<Self>>, &'b Message) -> BoxFuture<'b, Result<RR, Error>>,
This error looks to be caused by limitation of the compiler's ability to automatically convert generic functions which include a type bounded by a lifetime into a function pointer that is fully generic over that lifetime. I'm not quite sure why this limitation exists, but it can be worked around by wrapping the generic call in a non-generic closure. All you need to do to make your example compile is to change:
Self::send_and_expect
to
|c, m| Self::send_and_expect(c, m)
Thus hiding the generic from the compiler.

How can one await a result of a boxed future?

use futures::{future, Future};
fn test() -> Box<dyn Future<Output = bool>> {
Box::new(future::ok::<bool>(true))
}
async fn async_fn() -> bool {
let result: bool = test().await;
return result;
}
fn main(){
async_fn();
println!("Hello!");
}
Playground
Error:
error[E0277]: the trait bound `dyn core::future::future::Future<Output = bool>: std::marker::Unpin` is not satisfied
--> src/main.rs:8:24
|
8 | let result: bool = test().await;
| ^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `dyn core::future::future::Future<Output = bool>`
|
= note: required because of the requirements on the impl of `core::future::future::Future` for `std::boxed::Box<dyn core::future::future::Future<Output = bool>>`
According to the implementation:
impl<F> Future for Box<F>
where
F: Unpin + Future + ?Sized,
Boxed futures only implement the Future trait when the future inside the Box implements Unpin.
Since your function doesn't guarantee that the returned future implements Unpin, your return value will be considered to not implement Future. You'll not able to await it because your type is basically not a Future.
The solution from #Stargateur, adding an explicit type boundary to the signature, works (Playground):
fn test() -> Box<dyn Future<Output = Result<bool, ()>> + Unpin>
If you are using futures-rs, there is a helper type BoxFuture. You can use BoxedFuture without explicitly stating Unpin:
use futures::future::BoxFuture;
fn test() -> BoxFuture<'static, Result<bool, ()>> {
Box::pin(async { Ok(true) })
}
Playground
When it comes to Box and future, it almost always make sense to use Box::pin instead of Box::new:
use std::pin::Pin;
use futures::{future, Future};
fn test() -> Pin<Box<dyn Future<Output = Result<bool, ()>>>> {
Box::pin(future::ok(true))
}
async fn async_fn() -> bool {
test().await.unwrap()
}
The reason is quite interesting. Pin has a blanket implementation for Unpin:
impl<P> Unpin for Pin<P> where
P: Unpin,
And the Box<T> inside it is unconditionally Unpin:
impl<T> Unpin for Box<T> where
T: ?Sized,
So a Pin<Box<dyn Future>> is a unpinned Future. Everything works out, but why Box itself doesn't? This is one place where Deref gets in the way:
impl<T: ?Sized> Deref for Box<T> {
type Target = T;
}
await expects an unpinned Future, and the Box<dyn Future> you created with Box::new does contain a Future. So it is auto-dereferenced and the Unpin is lost unless you explicitly state it that way with Box<dyn Future + Unpin>.
Edit: #ÖmerErden is right about why Box<dyn Future> wouldn't work.

Why can't Result be treated as a Future even though it implements IntoFuture?

std::result::Result implements IntoFuture, but the following code doesn't compile:
extern crate futures; // 0.1.25
use futures::{future::Either, prelude::*, sync::mpsc};
fn example() -> impl Future<Item = (), Error = ()> {
let (tx, rx) = mpsc::channel(0);
let data = Some(1);
match data {
Some(d) => Either::A(tx.send(d).and_then(|x| Ok(())).map_err(|e| ())),
None => Either::B(Ok(()) as Result<(), ()>),
}
}
Full error message:
error[E0277]: the trait bound `std::result::Result<(), ()>: futures::Future` is not satisfied
--> src/lib.rs:5:17
|
5 | fn example() -> impl Future<Item = (), Error = ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `futures::Future` is not implemented for `std::result::Result<(), ()>`
|
= note: required because of the requirements on the impl of `futures::Future` for `futures::future::Either<futures::MapErr<futures::AndThen<futures::sink::Send<futures::sync::mpsc::Sender<{integer}>>, std::result::Result<(), futures::sync::mpsc::SendError<{integer}>>, [closure#src/lib.rs:9:50: 9:60]>, [closure#src/lib.rs:9:70: 9:76]>, std::result::Result<(), ()>>`
= note: the return type of a function must have a statically known size
Moreover, IntoFuture doesn't require Sized. Why can't Result<(), ()> be treated as a Future here?
Either only implements Future when both of its children implement Future and their types line up:
impl<A, B> Future for Either<A, B>
where
A: Future,
B: Future<Item = A::Item, Error = A::Error>,
Result does not implement Future, so placing a Result directly inside of an Either will not implement Future either.
The IntoFuture trait is orthogonal to Future. As its documentation states:
This trait is very similar to the IntoIterator trait and is intended to be used in a very similar fashion.
You can't call Iterator::map on a Vec (vec![1, 2, 3].map(...)), even though Vec implements IntoIterator, and the same logic applies to Result / Future / IntoFuture.
Most of the time, you will want to use futures::ok:
extern crate futures; // 0.1.25
use futures::{
future::{self, Either},
prelude::*,
sync::mpsc,
};
fn example() -> impl Future<Item = (), Error = ()> {
let (tx, _) = mpsc::channel(0);
let data = Some(1);
match data {
Some(d) => Either::A(tx.send(d).map(|_| ()).map_err(|_| ())),
None => Either::B(future::ok(())),
}
}
You could also choose to call into_future directly:
Either::B(Ok(()).into_future())

Resources