2

I'm making a Python module, written in Rust, using pyo3, that will:

  • Run its own thread
  • Read an input pin on a Raspberry Pi, counting how many times the state changes
  • Let Python query the counter

So far, my code looks like this:

use std::thread;

use pyo3::prelude::*;

#[pyclass]
struct CountWatcher {
    // The number of the GPIO pin we'll be watching
    // Can't be read from Python
    pin: u8,

    // How many times the pin changed state
    // Can be read from Python
    #[pyo3(get)]
    count: u128,

    // Thread handle
    t: thread::JoinHandle<()>,
}

#[pymethods]
impl CountWatcher {
    #[new]
    fn new(pin: u8) -> Self {
        let t = thread::spawn(|| {
            loop {
                // This is where the code that reads the GPIO pin, and increments count will eventually go
                println!("Test");
                std::thread::sleep(std::time::Duration::from_secs(1));
            }
        });

        Self {
            pin,
            count: 0,
            t: t,
        }
    }
}

#[pymodule]
fn countwatcher(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<CountWatcher>()?;
    Ok(())
}

This code works, but the problem I'm having is somehow getting a reference to the instance inside the thread so I can update count, while still letting Python check count anytime it wants.

I thought something like this would work, but it doesn't:

fn new(pin: u8) -> Arc<Self> {
    let mut inst = Self {
        pin,
        count: 0,
        t: None,
    };

    let mut inst_1 = Arc::new(inst);
    let mut inst_2 = inst_1.clone();

    let t = thread::spawn(move || {
        loop {
            inst_1.count += 1;
            std::thread::sleep(std::time::Duration::from_secs(1));
        }
    });

    inst_2.t = Some(t);

    inst_2
}

Note that I had to convert the struct's t type to Option<thread::JoinHandle()>>, since here, I need to create the instance before creating the thread. Also, my new method is now returning an Arc<Self> instead of just Self, which I'm not entirely sure I'm allowed to do.

I also tried using Arc<Mutex<CountWatcher>>, but then I need to either return an Arc<Mutex<CountWatcher>> from new, or return inst_2.lock(), which would just permanently lock it.

John
  • 2,551
  • 3
  • 30
  • 55
  • I don't know pyo3, but could you instead make the `count` field have type `Mutex` ? – Jonas Berlin Feb 22 '21 at 08:49
  • I guess then you might have to create a `#[getter]` function for the field to extract the count value instead of having `#[pyo3(get)]` directly at the field. – Jonas Berlin Feb 22 '21 at 08:57
  • @JonasBerlin I ended up using a getter function, and having count as `Arc>`, just like you said. It's a little more code, but at least it works. I'm happy with the solution, but I wonder if it's possible to do this without the getter functions – John Feb 22 '21 at 15:28
  • If you don't want to deal with a mutex you can also use an atomic for just a number, though for 128-bit you need a nightly feature. Although if this is just an incrementing count a 128-bit number is overkill as it would take over 500 years to fill a 64 bit counter if the count was going up every nanosecond. – Buzzec Feb 25 '21 at 03:01

0 Answers0