Calling an FnMut callback from another thread - multithreading

I am writing a Phoenix client library for Rust, taking advantage of the async websocket client from rust-websockets. Right now I am having trouble figuring out how to pass callback functions into the thread that is handling the websocket traffic. I have a simplified struct:
pub struct Socket {
endpoint: String,
connected: Arc<AtomicBool>,
state_change_close: Option<Box<FnMut(String)>>,
}
This struct has a connect function laid out as follows:
pub fn connect(&mut self) -> Result<(), String> {
if self.connected.load(Ordering::Relaxed) {
return Ok(())
}
// Copy endpoint string, otherwise we get an error on thread::spawn
let connection_string = self.endpoint.clone();
let (usr_msg, stdin_ch) = mpsc::channel(0);
let connection_thread = thread::spawn(move || {
// tokio core for running event loop
let mut core = Core::new().unwrap();
let runner = ClientBuilder::new(&connection_string)
.unwrap()
.add_protocol("rust-websocket")
.async_connect_insecure(&core.handle())
.and_then(|(duplex, _)| {
let (sink, stream) = duplex.split();
stream.filter_map(|message| {
println!("Received Message: {:?}", message);
match message {
OwnedMessage::Close(e) => {
// This is the line where I am trying to call the callback
if let Some(ref mut func) = self.state_change_close {
(func)(e.unwrap().reason);
}
Some(OwnedMessage::Close(e))
},
_ => None,
}
})
.select(stdin_ch.map_err(|_| WebSocketError::NoDataAvailable))
.forward(sink)
});
// Start the event loop
core.run(runner).unwrap();
});
self.connected.store(true, Ordering::Relaxed);
return Ok(())
}
When I try to compile this code I get the following error:
error[E0277]: the trait bound `std::ops::FnMut(std::string::String) + 'static: std::marker::Send` is not satisfied
--> src\socket.rs:99:29
|
99 | let connection_thread = thread::spawn(move || {
| ^^^^^^^^^^^^^ the trait `std::marker::Send` is not implemented for `std::ops::FnMut(std::string::String) + 'static`
|
I have tried changing the type of state_change_close to a Mutex<Option<...>> to avoid thread safety issues, but that did not help with this problem. Is what I'm trying to do possible?

After doing some more research I realized that I just had to modify Option<Box<FnMut(String)>> to be Option<Box<FnMut(String) + Send>> and copy that around my code to everywhere that the callback might be set. Learning more about trait objects!

Related

Lifetime issue returning a stream with hyper/azure sdk

I've been trying to return a stream, as I've done with tokio::fs::File however I am getting a lifetime error on the BlobClient.
error[E0597]: `blob` does not live long enough
--> src\main.rs:20:27
|
20 | let stream = Box::pin(blob.get().stream(128));
| ^^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `blob` is borrowed for `'static`
...
24 | }
| - `blob` dropped here while still borrowed
I've tried a bunch of different ways of handling the stream but I can't navigate around this lifetime error. I'm sure it might be something simple that I just keep overlooking. Thanks for any assistance.
Here's a repo of what I'm trying to do:
use std::{convert::Infallible, net::SocketAddr};
use azure_core::new_http_client;
use azure_storage::{
blob::prelude::{AsBlobClient, AsContainerClient},
clients::{AsStorageClient, StorageAccountClient},
};
use futures::TryStreamExt;
use hyper::{
service::{make_service_fn, service_fn},
Body, Request, Response, Server,
};
async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
let http_client = new_http_client();
let storage_account_client = StorageAccountClient::new_access_key(http_client.clone(), "account", "key");
let storage_client = storage_account_client.as_storage_client();
let blob = storage_client.as_container_client("container").as_blob_client("blob");
let stream = Box::pin(blob.get().stream(128));
let s = stream.and_then(|f| futures::future::ok(f.data));
Ok(Response::new(Body::wrap_stream(s)))
}
#[tokio::main]
async fn main() {
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let make_service = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) });
let server = Server::bind(&addr).serve(make_service);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
}
The problem is that the stream borrows from blob, but the wrap_stream() function only accepts 'static streams. A workaround is to construct the stream in a new task, and send back the stream items through a channel. The following helper function helps implement this approach:
/// Creates a `'static` stream from a closure returning a (possibly) non-`'static` stream.
///
/// The stream items, closure, and closure argument are still restricted to being `'static`,
/// but the closure can return a non-`'static` stream that borrows from the closure
/// argument.
fn make_static_stream<T, F, U>(
make_stream: F,
mut make_stream_arg: U,
) -> impl Stream<Item = T>
where
T: Send + 'static,
F: FnOnce(&mut U) -> BoxStream<'_, T> + Send + 'static,
U: Send + 'static,
{
let (mut tx, rx) = futures::channel::mpsc::channel(0);
tokio::spawn(async move {
let stream = make_stream(&mut make_stream_arg);
pin_mut!(stream);
while let Some(item) = stream.next().await {
if tx.feed(item).await.is_err() {
// Receiver dropped
break;
}
}
tx.close().await.ok();
});
rx
}
Here's how you would use it in the original code:
// ...
let stream = make_static_stream(
|blob| blob.get().stream(128).map_ok(|x| x.data).boxed(),
blob,
);
Ok(Response::new(Body::wrap_stream(stream)))
}

Hyper TLS cannot be run in Tokio Thread - Does not implement Send

In the server example provided by the HyperTLS library, the server provided cannot be run in a tokio::spawn() thread without throwing a litany of errors all stemming from the fact that:
dyn futures_util::Stream<Item = Result<tokio_rustls::server::TlsStream<tokio::net::TcpStream>, std::io::Error>> cannot be sent between threads safely.
The server definition below is exactly like that found in the example with the exception of the config file used to pass in config information.
#[derive(Debug, Clone)]
pub struct HTTPSServer {}
impl HTTPSServer {
pub async fn start<'a>(config: HTTPSServerConfig<'a>) -> Result<(), HTTPSServerError> {
// Build TLS configuration.
let tls_cfg = {
// Load public certificate.
let certs = load_certs(&config.cert_path)?;
// Load private key.
let key = load_private_key(&config.key_path)?;
// Do not use client certificate authentication.
let mut cfg = rustls::ServerConfig::new(rustls::NoClientAuth::new());
// Select a certificate to use.
cfg.set_single_cert(certs, key)
.map_err(|e| error(format!("failed to set single cert: {}", e)))?;
// Configure ALPN to accept HTTP/2, HTTP/1.1 in that order.
cfg.set_protocols(&[b"h2".to_vec(), b"http/1.1".to_vec()]);
sync::Arc::new(cfg)
};
let tls_acceptor = TlsAcceptor::from(tls_cfg);
// Create a TCP listener via tokio.
let tcp_listener = TcpListener::bind(&config.addr)
.await
.map_err(|e| {
eprintln!("failed to create TCP Listener: {:?}", e);
e
})?;
// Prepare a long-running future stream to accept and serve clients.
let incoming_tls_stream = stream! {
loop {
let (socket, _) = tcp_listener.accept().await?;
let stream = tls_acceptor.accept(socket)
.map_err(|e| {
println!("[!] Voluntary server halt due to client-connection error...");
// Errors could be handled here, instead of server aborting.
// Ok(None)
error(format!("TLS Error: {:?}", e))
});
yield stream.await;
}
};
// Setup router
let service = make_service_fn(|_| async {
Ok::<_, io::Error>(service_fn(echo))
});
// Build server
let server = Server::builder(HyperAcceptor {
acceptor: Box::pin(incoming_tls_stream),
}).serve(service);
// Run the future, keep going until an error occurs.
println!("[https-server]: Starting to serve on https://{}.", &config.fqdn);
server
.await
.map_err(|e| {
eprintln!("[https-server]: error: {:?}", e);
HTTPSServerError::from(e)
})
}
}
The following is the main method to run the server in a separate thread
#[tokio::main]
async fn main() {
// Start the server asynchronously
let fqdn = "https://localhost:8080";
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), 8080);
let cert_path = "localhost.crt";
let key_path = "localhost.key";
let ca_path = "RootCA.crt";
let client_config = HTTPSClientConfig {
fqdn,
ca_path: Some(ca_path)
};
let server_config = HTTPSServerConfig {
fqdn,
addr,
cert_path,
key_path,
ca_path,
};
// Spawn server in a new thread!
tokio::spawn(start(server_config)).await.unwrap();
// A function to kick off a client call to the server.
client(client_config);
}
async fn start<'a>(server_config: HTTPSServerConfig<'a>) {
HTTPSServer::start(server_config)
.await
.map_err(|e| {
eprintln!("[https-server]: failed to start server: {:?}", e)
});
}
The Error
I am not exactly sure how to solve the following error as it doesn't seem trivial to ensure a dyn type implements Send. Not sure how to solve this.
error: future cannot be sent between threads safely
--> https/src/main.rs:61:5
|
61 | tokio::spawn(start(server_config)).await.unwrap();
| ^^^^^^^^^^^^ future returned by `start` is not `Send`
|
::: /Users/x/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.2.0/src/task/spawn.rs:129:21
|
129 | T: Future + Send + 'static,
| ---- required by this bound in `tokio::spawn`
|
= help: the trait `Send` is not implemented for `dyn futures_util::Stream<Item = Result<tokio_rustls::server::TlsStream<tokio::net::TcpStream>, std::io::Error>>`
note: future is not `Send` as it awaits another future which is not `Send`
--> https/src/server.rs:89:9
|
89 | server
| ^^^^^^ await occurs here on type `hyper::Server<HyperAcceptor<'_>, hyper::service::make::MakeServiceFn<[closure#https/src/server.rs:77:39: 79:10]>>`, which is not `Send`

Cannot use Stream::take_while on an mpsc::channel: bool: Future is not satisfied [duplicate]

This question already has answers here:
How to use take_while with futures::Stream?
(2 answers)
Closed 3 years ago.
I want to run an event loop in one thread and handle data from a UDP socket until another thread signals to stop work.
This is a difficult task for me, so I want to start from a simpler task:
one thread starting the event loop and waiting for another thread to signal the end:
use futures::{future, future::Future, stream::Stream, sync::mpsc};
use std::{io, io::BufRead, thread};
fn main() {
let (mut tx, rx) = mpsc::channel::<bool>(1);
let thr = thread::spawn(|| {
let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap();
runtime.spawn(
future::lazy(|| {
println!("event loop started");
Ok(())
})
.and_then(rx.take_while(|x| *x == true).into_future()),
);
runtime.run()
});
let stdin = io::stdin();
for line in stdin.lock().lines() {
let line = line.unwrap();
println!("{}", line);
if line == "exit" {
tx.try_send(false).unwrap();
break;
}
}
thr.join().unwrap().unwrap();
}
This code doesn't compile:
error[E0277]: the trait bound `bool: futures::future::Future` is not satisfied
--> src/main.rs:14:26
|
14 | .and_then(rx.take_while(|x| *x == true).into_future()),
| ^^^^^^^^^^ the trait `futures::future::Future` is not implemented for `bool`
|
= note: required because of the requirements on the impl of `futures::future::IntoFuture` for `bool`
error[E0599]: no method named `into_future` found for type `futures::stream::take_while::TakeWhile<futures::sync::mpsc::Receiver<bool>, [closure#src/main.rs:14:37: 14:51], bool>` in the current scope
--> src/main.rs:14:53
|
14 | .and_then(rx.take_while(|x| *x == true).into_future()),
| ^^^^^^^^^^^
|
= note: the method `into_future` exists but the following trait bounds were not satisfied:
`futures::stream::take_while::TakeWhile<futures::sync::mpsc::Receiver<bool>, [closure#src/main.rs:14:37: 14:51], bool> : futures::stream::Stream`
`&mut futures::stream::take_while::TakeWhile<futures::sync::mpsc::Receiver<bool>, [closure#src/main.rs:14:37: 14:51], bool> : futures::stream::Stream`
How do I fix the compilation error?
Read and understand the documentation and function signature of methods you attempt to use:
fn take_while<P, R>(self, pred: P) -> TakeWhile<Self, P, R>
where
P: FnMut(&Self::Item) -> R,
R: IntoFuture<Item = bool, Error = Self::Error>,
Self: Sized,
take_while takes a closure that returns some type that must be convertible into a future; a bool is not convertible into a future. The simplest way to do this is via future::ok:
let thr = thread::spawn(|| {
let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap();
runtime.spawn({
rx.take_while(|&x| future::ok(x))
.for_each(|x| {
println!("{}", x);
future::ok(())
})
});
runtime.run()
});
See also:
The trait bound `(): futures::Future` is not satisfied when using TcpConnectionNew
But my problem also in joining future::lazy and rx.take_while
That's an unrelated problem to what you asked about. Again, we look at the docs, this time for Future::and_then:
fn and_then<F, B>(self, f: F) -> AndThen<Self, B, F>
where
F: FnOnce(Self::Item) -> B,
B: IntoFuture<Error = Self::Error>,
Self: Sized,
Similarly to take_while, it takes a closure and the closure must return something that can be convertible into a future. Your code doesn't provide a closure.
Then look at Stream::into_future. This returns a type that implements Future and returns a tuple. The first item in the tuple is a single value from the stream, the second is the stream itself, to allow getting more values.
To get all the item and error types correct, I've make liberal use of map(drop) and map_err(drop) — you will want to do something better for your data and error handling.
runtime.spawn({
future::lazy(|| {
println!("event loop started");
Ok(())
})
.and_then(|_| {
rx.take_while(|&x| future::ok(x))
.into_future()
.map(drop)
.map_err(drop)
})
.map(drop)
});
Really, you should just use a oneshot channel; it's much simpler:
use futures::{
future::{self, Future},
sync::oneshot,
};
use std::thread;
fn main() {
let (tx, rx) = oneshot::channel();
let thr = thread::spawn(|| {
let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap();
runtime.spawn({
future::lazy(|| {
println!("event loop started");
Ok(())
})
.and_then(|_| rx.map_err(drop))
});
runtime.run()
});
let lines = ["hello", "goodbye", "exit"];
for &line in &lines {
if line == "exit" {
tx.send(()).unwrap();
break;
}
}
thr.join().unwrap().unwrap();
}

Avoiding closure - encapsulating thread variables in a struct

I am writing a simple websocket server named BoltServer based on Rust websocket crate(Code is incomplete, I just started). I am using the example program as the base. However the example programs are not modular (having very long methods). So I am trying to break them up into structs and methods. I want to spawn two threads for each client. One sends messages and the other one receives messages. So here, I want to capture all variables used by the thread in a struct and then call the run method in the impl.
extern crate websocket;
extern crate time;
extern crate rustc_serialize;
pub mod ws {
use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::net::ToSocketAddrs;
use websocket;
use websocket::{Server, Message, Sender, Receiver};
use websocket::server::Connection;
use websocket::stream::WebSocketStream;
use std::str::from_utf8;
struct BoltUser {
user_id: u32,
my_tx: mpsc::Sender<String>,
}
struct Broadcaster {
my_rx: mpsc::Receiver<String>,
}
impl Broadcaster {
fn new(receiver: mpsc::Receiver<String>) -> Broadcaster {
Broadcaster { my_rx: receiver }
}
fn run(self) {
while let Ok(msg) = self.my_rx.recv() {
println!("Broadcaster got message: {}", msg);
}
}
}
struct SocketReader {}
impl SocketReader {
fn run(self) {}
}
struct SocketWriter {
my_rx: mpsc::Receiver<String>,
sender: Sender,
}
impl SocketWriter {
fn run(self) {
while let Ok(message) = self.my_rx.recv() {
}
}
}
pub struct BoltServer {
address: String,
connected_users: Arc<Mutex<Vec<BoltUser>>>,
}
impl BoltServer {
pub fn new(address: &str) -> BoltServer {
BoltServer {
address: address.to_string(),
connected_users: Arc::new(Mutex::new(vec![])),
}
}
fn handshake(&mut self,
connection: Connection<WebSocketStream, WebSocketStream>)
-> (SocketWriter, SocketReader) {
let request = connection.read_request().unwrap();
// println!("thread-> Accepting request...");
let response = request.accept();
let (mut sender, mut receiver) = response.send().unwrap().split();
let (user_tx, user_rx) = mpsc::channel::<String>();//Create a channel for writer
let socket_writer = SocketWriter {
my_rx: user_rx,
sender: sender,
};
let socket_reader = SocketReader {};
(socket_writer, socket_reader)
}
pub fn start(&mut self) {
println!("Starting");
let (broadcaster_tx, broadcaster_rx) = mpsc::channel::<String>();
let broadcaster = Broadcaster::new(broadcaster_rx);
let handle = thread::Builder::new()
.name("Broadcaster".to_string())
.spawn(move || broadcaster.run());
let server = Server::bind(&*self.address).unwrap();
let mut user_id: u32 = 0;
// Block and process connection request from a new client
for connection in server {
user_id = user_id + 1;//Create a new user id
let (socket_writer, socket_reader) = self.handshake(connection);
thread::Builder::new()
.name("Socket writer".to_string())
.spawn(move || socket_writer.run());
thread::Builder::new()
.name("Socket reader".to_string())
.spawn(move || socket_reader.run());
}
handle.unwrap().join();
println!("Finished");
}
}
}
The following code gives an idea of what I want to achieve.
// Block and process connection request from a new client
for connection in server {
user_id = user_id + 1;//Create a new user id
let (socket_writer, socket_reader) = self.handshake(connection);
thread::Builder::new().name("Socket writer".to_string()).spawn(move || {
socket_writer.run()
});
thread::Builder::new().name("Socket reader".to_string()).spawn(move || {
socket_reader.run()
});
}
Here I am stuck in the handshake method. I am not able to initialize the SocketWriter struct with the sender that I am getting by calling the split method in the library. I am getting the following compilation error:
error[E0038]: the trait `websocket::Sender` cannot be made into an object
--> src/lib.rs:46:9
|
46 | sender:Sender,
| ^^^^^^^^^^^^^ the trait `websocket::Sender` cannot be made into an object
|
= note: method `send_dataframe` has generic type parameters
= note: method `send_message` has generic type parameters
The error is telling you the immediate problem:
46 | sender:Sender,
| ^^^^^^^^^^^^^ the trait `websocket::Sender` cannot be made into an object
First of all, a variable/field can't have a plain trait type (but &Trait can be possible), but also the websocket::Sender trait is not object safe; it has generic methods which can't work dynamically (ie vtable methods have to have a fixed type).
Instead, you have to have a concrete type (you could also make it a generic struct).
It's not obvious what the right type is, so I like to get the compiler to tell me. So first try the simplest possible:
sender: (),
The compiler replies with some information:
| ^^^^^^ expected (), found struct `websocket::client::Sender`
Ok, let's plug that in:
sender: websocket::client::Sender,
That gives:
46 | sender: websocket::client::Sender,
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 type arguments, found 0
Ok, that's a generic type. Next try:
sender: websocket::client::Sender<()>,
Finally it gives us the real type:
74 | sender:sender,
| ^^^^^^ expected (), found enum `websocket::WebSocketStream`
So finally we can finish SocketWriter:
struct SocketWriter {
my_rx: mpsc::Receiver<String>,
sender: websocket::client::Sender<websocket::WebSocketStream>,
}
There's a following compile error since the connection you get is a Result<> so you need to check for errors (it compiles if I change to self.handshake(connection.unwrap()), but that's obviously not the best practice.

Lifetime error when spawning a task

I can not understand this error regarding lifetimes. Proxy is a struct:
impl Proxy {
pub fn new<A: ip::ToSocketAddr>(address: A) -> Proxy {
Proxy {
address: address.to_socket_addr().unwrap()
}
}
pub fn listen_and_proxy(&self) {
println!("listening {}", self.address);
for stream in TcpListener::bind(self.address).listen().incoming() {
let stream = stream.unwrap();
spawn(move || { // <- this causes an error
let mut stream = stream;
let name = stream.peer_name();
println!("client: {} -> {}", name, self.handle(stream));
});
}
}
}
Output error:
cannot infer an appropriate lifetime due to conflicting requirements
What is wrong in the code?
You are trying to call self.handle(stream) which would require moving self into the newly spawned thread. This cannot be done.

Resources