Marius
Marius

Reputation: 428

Background worker thread and synchronization in Rust

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.

  1. 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?

  2. 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?

Upvotes: 4

Views: 3370

Answers (1)

Marius
Marius

Reputation: 428

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 {}
}

Upvotes: 3

Related Questions