I wrote this code, but when sending pings, the program cannot do anything else. How can I spawn another thread to do this work while I do something else in my program?
pub fn sending_ping(addr: Addr<MicroscopeClient>) -> Result<(), ()> {
info!("Pings started");
spawn(async move {
loop {
info!("Ping");
match addr.send(Ping {}).await {
Ok(_) => {
info!("Ping sended")
}
Err(e) => {
warn!("Ping error");
return;
}
}
std::thread::sleep(Duration::from_millis((4000) as u64));
}
});
return Ok(());
}
Assuming you are using tokio, calling tokio::spawn does not necessarily guarantee that the task will be executed on a separate thread (though it may be).
The problem here is that std::thread::sleep() completely blocks the thread that the task is running on, meaning your pings will run asynchronously (not blocking other tasks) but when you move on to the sleep nothing else on the current thread will execute for the next 4 seconds.
You can resolve this by using a non-blocking version of sleep, such as the one provided by tokio https://docs.rs/tokio/latest/tokio/time/fn.sleep.html
Thus your code would look like
pub fn sending_ping(addr: Addr<MicroscopeClient>) -> Result<(), ()> {
info!("Pings started");
spawn(async move {
loop {
info!("Ping");
match addr.send(Ping {}).await {
Ok(_) => {
info!("Ping sended")
}
Err(e) => {
warn!("Ping error");
return;
}
}
tokio::time::sleep(Duration::from_millis(4000)).await;
}
});
return Ok(());
}
If you truly want to ensure that the task spawns on a different thread you would have to use std::thread::spawn but you would then have to set up another async runtime. Instead you could use spawn_blocking which at least guarantees the task is run on a thread where tasks are expected to block
I solved this particular problem in the following way
fn send_heartbeat(&self, ctx: &mut <Self as Actor>::Context) {
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
info!("Websocket Client heartbeat failed, disconnecting!");
act.server_addr.do_send(Disconnect {
id: act.user_id.clone(),
});
ctx.stop();
return;
}
});
}
But I still don't know how to start some long process in a websocket in a separate thread, so that it does not block the websocket, with tokio="0.2.0" and actix="0.3.0".
Related
How can I pause an async task in Rust?
Swift has withCheckedContinuation(function:_:)
that pauses current task and returns saved context that can be resumed at desired time. (a.k.a. call/cc)
tokio has tokio::task::yield_now, but it can resume automatically, so that's not what I'm looking for. I mean "pause" that will never resume without explicit command.
Now I'm looking into tokio manual. It defines several synchronization features in tokio::sync module, but I couldn't find a function to pause a task directly. Am I supposed to use only the synchronization feature to simulate the suspend? Or am I missing something here?
tokio::sync::oneshot can be used for this purpose. It gives you two objects: one a future, and the other a handle you can use to cause the future to resolve. It also conveys a value, but that value can be just () if we don't need it.
In the following runnable example, main() is the task being paused and the task which resumes it is spawned, because that's the simplest thing to do; but in a real application you'd presumably pass on the sender to something else that already exists.
use std::time::Duration;
use tokio::spawn;
use tokio::time::sleep;
use tokio::sync::oneshot;
#[tokio::main]
async fn main() {
println!("one");
let (sender, receiver) = oneshot::channel::<()>();
spawn(async move {
sleep(Duration::from_millis(400));
println!("two");
if let Err(_) = sender.send(()) {
println!("oops, the receiver dropped");
}
});
println!("...wait...");
match receiver.await {
Ok(()) => println!("three"),
Err(_) => println!("oops, the sender dropped"),
}
}
Note that this is not a special feature of the oneshot channel: any future which you can control the resolution of can be used for this purpose. oneshot is appropriate when you want to hand out a specific handle to this one paused task. If you instead wanted many tasks to wake up on a single notification, you could use tokio::sync::watch instead.
I don't know anything built-in, but you can build your own.
You need access to the Waker to unpark a future. You also need to keep track of whether the futute has been manually unparked, because futures can be waked by the runtime even if nobody ordered them to.
There are various ways to write this code, here is one:
// You can get rid of this `Unpin` bound, if you really want
pub async fn park(callback: impl FnOnce(Parker) + Unpin) {
enum Park<F> {
FirstTime { callback: F },
SecondTime { unparked: Arc<AtomicBool> },
}
impl<F: FnOnce(Parker) + Unpin> Future for Park<F> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Self::SecondTime { unparked } = &*self {
return if unparked.load(Ordering::SeqCst) {
Poll::Ready(())
} else {
Poll::Pending
};
}
let unparked = Arc::new(AtomicBool::new(false));
let callback = match std::mem::replace(
&mut *self,
Self::SecondTime {
unparked: Arc::clone(&unparked),
},
) {
Self::FirstTime { callback } => callback,
Self::SecondTime { .. } => unreachable!(),
};
callback(Parker {
waker: cx.waker().clone(),
unparked,
});
Poll::Pending
}
}
Park::FirstTime { callback }.await
}
Then you call it like park(|p| { ... }).await.
Example.
I'm doing the final project of the rust book (ch 20 - Building a Multithreaded Web Server) and the final working code for a thread worker is :
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {
let thread = thread::spawn(move || loop {
let message = receiver.lock().unwrap().recv().unwrap();
match message {
Message::NewJob(job) => {
println!("Worker {} got a job; executing.", id);
job();
}
Message::Terminate => {
println!("Worker {} was told to terminate.", id);
break;
}
}
});
// ...
}
}
Then I decided to shorten the code by removing the message variable and apply the match operator directly on receiver.lock().unwrap().recv().unwrap()
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {
let thread = thread::spawn(move || loop {
match receiver.lock().unwrap().recv().unwrap() {
Message::NewJob(job) => {
println!("Worker {} got a job; executing.", id);
job();
}
Message::Terminate => {
println!("Worker {} was told to terminate.", id);
break;
}
}
});
// ...
}
}
but now there is only one worker (the first one) responding to the job queue.
What is happening here ?
The two code snippets are not equivalent. The difference lies in the way temporaries are borrowed and dropped.
In the first example, the temporary mutex guard is dropped at the end of the statement:
let message = receiver.lock().unwrap().recv().unwrap();
// the mutex guard is dropped here, and other threads can acquire the lock
while in the second snipped, the mutex guard remains alive and thus holds the lock until the end of the match statement:
match receiver.lock().unwrap().recv().unwrap() { // the mutex is locked here and will remain locked until the channel yields a result, thus blocking other threads
Message::NewJob(job) => {
println!("Worker {} got a job; executing.", id);
job();
}
Message::Terminate => {
println!("Worker {} was told to terminate.", id);
break;
}
}
// the mutex guard is dropped here
So in the second example other threads are locked out from acquiring access to the receiving end of the channel.
To quote the answer from this thread:
when a temporary is created inside an expression, it is (only) dropped at the end of the enscoping statement:
A let binding ends its statement before the binding ("variable") is usable;
A match and derivatives, end the statement after the scope in which the binding ("variable") is usable.
I ran into the kind of a problem described in this question: How can I create a Tokio runtime inside another Tokio runtime without getting the error "Cannot start a runtime from within a runtime"? .
Some good rust crates doesn't have asynchronous executor. I decided to put all such libraries calls in one thread which is tolerant of such operations. Another thread should be able to send non-blicking messages using tokio::channel.
I have programmed a demo stand to test implementation options. Call tokio::spawn inside of each runtime is made in order to understand a little more detail in tokio runtimes and handlers - it is a part of a question.
The question.
Please correct me if I misunderstand something further.
There are two tokio runtimes. Each is launched in its own thread. Call tokio::spawn inside first_runtime() spawns task on first runtime. Call tokio::spawn inside second_runtime() spawns task on second runtime. There is a tokio::channel between these two tasks. Call tx.send(...).await does not block sending thread if channel buffer is not full, even if receiving thread is blocked by thread::sleep() call.
Am I getting everything right? The output of this code tells me that I'm right, but I need confirmation of my reasoning.
use std::thread;
use std::time::Duration;
use tokio::sync::mpsc::{Sender, Receiver, channel}; // 1.12.0
#[tokio::main(worker_threads = 1)]
#[allow(unused_must_use)]
async fn first_runtime(tx: Sender<String>) {
thread::sleep(Duration::from_secs(1));
println!("first thread woke up");
tokio::spawn(async move {
for msg_id in 0..10 {
if let Err(e) = tx.send(format!("message {}", msg_id)).await {
eprintln!("[ERR]: {}", e);
} else {
println!("message {} send", msg_id);
}
}
}).await;
println!("first thread finished");
}
#[tokio::main(worker_threads = 1)]
#[allow(unused_must_use)]
async fn second_runtime(mut rx: Receiver<String>) {
thread::sleep(Duration::from_secs(3));
println!("second thread woke up");
tokio::spawn(async move {
while let Some(msg) = rx.recv().await {
println!("{} received", msg);
}
}).await;
println!("second thread finished");
}
fn main() {
let (tx, rx) = channel::<String>(5);
thread::spawn(move || { first_runtime(tx); });
second_runtime(rx);
}
I'm trying to write a simple library that has a background worker thread that processes commands when the library functions are called.
The way that I would normally do it in C is have a global semaphore handle that the worker would block on. The functions would give the semaphore after they sent a command, at which point the worker would unblock etc... There are other ways but this is just an example.
I have a few questions about how I can achieve something similar with Rust.
How do I prevent a thread from closing once the function that created it returns? e.g the thread would be created when I call init(), but would exit when init() returns, how to prevent this?
How to have a have a global synchronization method between the worker thread and function calls? I was looking at using channels but how do I access the rx from the thread and multiple tx's from different functions? e.g send_cmd_a(), send_cmd_b() to the same thread
Pseudo code of what I'm trying to accomplish:
static (tx, rx) = mpsc::channel(); //how to do something like this?
fn init() {
thread::spawn(|| {
loop {
let cmd = rx.recv().unwrap(); //blocks till there is data
//process data....
if cmd == "exit" {
return;
}
}
});
}
fn send_cmd_a() {
//Do a bunch of other stuff...
tx.send("cmd_a").unwrap();
}
fn exit() {
tx.send("exit").unwrap();
}
Do I have to create one big object that encapsulates all of this, thus owning the synchronization mechanism? (still doesn't answer question #1)
What would be the preferred way of doing something like this in Rust?
I think I figured out a way to implement what I wanted in Rust without needing to use global variables.
struct Device {
sender: Sender<u8>, //other stuff
}
trait New {
fn new() -> Self;
}
trait SendCommand {
fn send_command(&self, u8);
}
impl New for Device {
fn new() -> Device {
let (tx, rx) = channel();
let device = Device { sender: tx };
thread::spawn(move || {
loop {
let cmd = rx.recv().unwrap();
println!("Command: {}", cmd); //process commands here
}
});
return device;
}
}
impl SendCommand for Device {
fn send_command(&self, cmd: u8) {
self.sender.send(cmd).unwrap();
}
}
fn main() {
let dev = Device::new(); //create the device
for i in 0..10 {
dev.send_command(i); //send commands
sleep(Duration::from_millis(50));
}
loop {}
}
I'm writing a small test that starts a daemon process and tests it e.g:
let server = Command::new("target/debug/server").spawn();
// do some tests
server.kill();
The typical way to fail a test is to panic. Unfortunately this means that kill() never gets invoked and repeated runs of the test suite fail, because the port is taken by the old process that is still running.
Is there something like a TRAP function that I can use to ensure the Child gets killed?
You can use standard RAII patterns to ensure the child thread is killed if you leave a given scope. If you want to kill your child only if you are panicking, you can insert a check to std::thread::panicking.
use std::process::{Command,Child};
struct ChildGuard(Child);
impl Drop for ChildGuard {
fn drop(&mut self) {
// You can check std::thread::panicking() here
match self.0.kill() {
Err(e) => println!("Could not kill child process: {}", e),
Ok(_) => println!("Successfully killed child process"),
}
}
}
fn main() {
let child = Command::new("/bin/cat").spawn().unwrap();
let _guard = ChildGuard(child);
panic!("Main thread panicking");
}
You can put the possibly-panicking code into a closure and give that closure to catch_panic. catch_panic acts the same way a scoped or spawned thread does on joining. It returns a Result with either Ok(ClosureRetVal) or an Err(Box<Any>) if the closure panicked.
let res = std::thread::catch_panic(|| {
panic!("blub: {}", 35);
});
if let Err(err) = res {
let msg: String = *err.downcast().unwrap();
println!("{}", msg);
}
PlayPen