Rust Multitasking API on PC

One of the obstacles when switching to a new RTOS is to get familiar with the API and programming workflow. To flatten that learning curve we can use a similar syntax a programmer is already used to. So, let us look at threads and IPC in Rust for computer systems.

Threads

use std::thread;

fn process_large_text(text: Arc<TextMap>) ->  {
    /*...*/
} 

fn main() {
    /*...*/
    let handle = thread::spawn1(move || 
                    process_large_text(text.clone()));2
    handle.join().unwrap();3
}

Listing: Multitasking using std::thread

A new thread is created and executed immediately with the thread::spawn() call 1 in the listing. The parameter of thread::spawn() is a capture which is similar to lambda function in other languages. This capture simply calls the process_large_text() function and passes a reference to the text 2. In this case clone() only increments the reference counter of the atomic reference counted type (Arc<>). At 3 the program waits until the thread exits. If a thread panics, the join() function will return an error. [1, pp. 457]

Inter-Process Communication (IPC)

The Rust standard library offers some support to communicate between threads. There are other solutions from the community as well, i.e. tokio. This subsection covers mutual exclusion (Mutex) and communication channels (channel) from the Multi-producer single-consumer (mpsc) library.

Channel

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

let (tx, rx) = channel();1

let handle = thread::spawn(move || {
    tx.send(slow_operation()).unwrap();2
});
let result = rx.recv().unwrap();3
handle.join().unwrap();

Listing: Message passing using a channel

A channel consists of one or more senders and a single receiver. At 1 in the listing a new channel is created. In the capture 2 the result of slow_operation() is sent through the channel. The main program receives the result at 3 and the joins the thread. [1, pp. 470]

Mutex

use std::sync::Mutex;
use std::thread;

let data = Mutex::new(42i32);1
let handle = thread::spawn(move || {
    let mut data_value = data.lock().unwrap();2
    *data_value += 1;3
    // mutex automatically unlocked when `data_value` goes out of scope
});
handle.join();

Listing: Mutual Exclusion (mutex)

In comparison to C/C++ a mutex in Rust is bound to a resource. In the listing at 1 a new mutex containing an integer variable is created. To access the data protected by the mutex a thread must first lock the resource 2. The value can then be modified 3 and the mutex is automatically unlocked as soon as data_value goes out of scope. If a thread locks a resource and then panics, the mutex is marked as poisoned. When other threads try to lock a poisoned mutex, an error is returned. [1, pp. 484]