Rahix / shared-bus

Crate for sharing buses between multiple devices
Apache License 2.0
129 stars 34 forks source link

How to embed this (resolving lifetime constraints) #13

Closed ryankurte closed 3 years ago

ryankurte commented 4 years ago

hey so, i'm trying to embed a BusManager and a set of drivers consuming i2c::Write etc. into an object but, finding it extremely difficult to resolve the lifetime requirements to do so as the reference to the BusManager must outlive the BusProxy objects.

it appears this is a reflection of the use of Mutex<RefCell<_>> rather than the more common Arc<Mutex<_>>, and i see why this approach has been taken, just wondering if you have any ideas about how to make it possible?

Rahix commented 4 years ago

This isn't really possible as the proxies must hold references to the actual bus object. Thus, the bus manager must not be moved, else you'll have dangling references. That's why you can't resolve the lifetime issues; rust can't guarantee that the struct containing the manager and proxies can't move.

Instead, you probably want to either allocate the BusManager on the heap to ensure it stays in one place, or put it into a static mut where you can get the same guarantee. Essentially, you need to get a &'static reference to it some way or another.

Maybe take a look at what shared-bus-rtic does. I think the new!() macro it provides goes into the right direction. Maybe we should even include something like it here?

ryankurte commented 4 years ago

Ahh yeah, this design is to suit cortex-m::Mutex / use without a heap right? I ended up writing a quick completely concrete std proxy that just holds the whole object in the mutex instead of a reference which solves my problem, will look at the other approaches to embedding it later on.

#[derive(Clone)]
pub struct I2cProxy(Arc<Mutex<I2cdev>>);

impl i2c::Write for I2cProxy {
    type Error = LinuxI2CError;

    fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
        let mut i2c = self.0.lock().unwrap();
        i2c.write(addr, bytes)
    }
}

impl i2c::Read for I2cProxy {
    type Error = LinuxI2CError;

    fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
        let mut i2c = self.0.lock().unwrap();
        i2c.read(addr, buffer)
    }
}

impl i2c::WriteRead for I2cProxy {
    type Error = linux_embedded_hal::i2cdev::linux::LinuxI2CError;

    fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
        let mut i2c = self.0.lock().unwrap();
        i2c.write_read(addr, bytes, buffer)
    }
}
Rahix commented 3 years ago

@ryankurte does the new version of shared-bus solve this issue for you?

ryankurte commented 3 years ago

ahhh i'm not sure when i will be able to get back to that project to check... happy to close and re-open later or leave till then as suits you?

Rahix commented 3 years ago

let's close it for now and reopen if it turns out to still be an issue!