Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I reliably clean up Rust threads performing blocking IO?

It seems to be a common idiom in Rust to spawn off a thread for blocking IO so you can use non-blocking channels:

use std::sync::mpsc::channel;
use std::thread;
use std::net::TcpListener;

fn main() {
    let (accept_tx, accept_rx) = channel();

    let listener_thread = thread::spawn(move || {
        let listener = TcpListener::bind(":::0").unwrap();
        for client in listener.incoming() {
            if let Err(_) = accept_tx.send(client.unwrap()) {
                break;
            }
        }
    });
}

The problem is, rejoining threads like this depends on the spawned thread "realizing" that the receiving end of the channel has been dropped (i.e., calling send(..) returns Err(_)):

drop(accept_rx);
listener_thread.join(); // blocks until listener thread reaches accept_tx.send(..)

You can make dummy connections for TcpListeners, and shutdown TcpStreams via a clone, but these seem like really hacky ways to clean up such threads, and as it stands, I don't even know of a hack to trigger a thread blocking on a read from stdin to join.

How can I clean up threads like these, or is my architecture just wrong?

like image 295
sleeparrow Avatar asked Jun 18 '15 04:06

sleeparrow


People also ask

Is Rust good for multithreading?

Rust attempts to mitigate the negative effects of using threads, but programming in a multithreaded context still takes careful thought and requires a code structure that is different from that in programs running in a single thread.

How many threads should I use Rust?

The threads provided by the Rust standard library are "OS threads", that is, they use the facilities of your operating system. Therefore, a Rust program has no limit imposed by Rust itself, but rather, this limit would result from whatever your OS lets you do.

How do you block a thread in Rust?

park and unparkThe thread::park function blocks the current thread unless or until the token is available for its thread handle, at which point it atomically consumes the token. It may also return spuriously, without consuming the token.

How do you make threads in Rust?

To create a new thread in Rust, we call the thread::spawn function and then pass it a closure, which in turn contains the code that we want to run in the new thread.


1 Answers

One simply cannot safely cancel a thread reliably in Windows or Linux/Unix/POSIX, so it isn't available in the Rust standard library.

Here is an internals discussion about it.

There are a lot of unknowns that come from cancelling threads forcibly. It can get really messy. Beyond that, the combination of threads and blocking I/O will always face this issue: you need every blocking I/O call to have timeouts for it to even have a chance of being interruptible reliably. If one can't write async code, one needs to either use processes (which have a defined boundary and can be ended by the OS forcibly, but obviously come with heavier weight and data sharing challenges) or non-blocking I/O which will land your thread back in an event loop that is interruptible.

mio is available for async code. Tokio is a higher level crate based on mio which makes writing non-blocking async code even more straight forward.

like image 137
SpamapS Avatar answered Nov 06 '22 23:11

SpamapS