Skip to content

Commit

Permalink
Merge #363
Browse files Browse the repository at this point in the history
363: async/i2c: fix lifetimes on transaction() r=ryankurte a=Dirbaio

Trying to implement i2c for a shared i2c bus behind an async mutex yielded lots of cursed lifetime errors, because `&'a mut [Operation<'b>]` is invariant on `'b`, not covariant as one would expect...

To fix this, the GAT future needs two lifetimes. Also counterintuitively, the future must be `+ 'a`, but NOT `+ 'b`. Then `AddressMode: 'static` is needed because Rust wants annoying `where A: 'a` bounds otherwise.

The async SPI PR has the same issue, will fix later. #347 

With these fixes, implementing i2c on a mutex works nicely now:

```rust

struct SharedI2c<T>(tokio::sync::Mutex<T>);

impl<T: ErrorType> ErrorType for SharedI2c<T> {
    type Error = T::Error;
}

impl<A: AddressMode, T: I2c<A>> I2c<A> for SharedI2c<T> {
    type ReadFuture<'a>
    where
        Self: 'a,
    = impl Future<Output = Result<(), Self::Error>> + 'a;

    fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Self::ReadFuture<'a> {
        async move { self.0.lock().await.read(address, read).await }
    }

    type WriteFuture<'a>
    where
        Self: 'a,
    = impl Future<Output = Result<(), Self::Error>> + 'a;

    fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> {
        async move { self.0.lock().await.write(address, write).await }
    }

    type WriteReadFuture<'a>
    where
        Self: 'a,
    = impl Future<Output = Result<(), Self::Error>> + 'a;

    fn write_read<'a>(
        &'a mut self,
        address: A,
        write: &'a [u8],
        read: &'a mut [u8],
    ) -> Self::WriteReadFuture<'a> {
        async move { self.0.lock().await.write_read(address, write, read).await }
    }

    type TransactionFuture<'a, 'b>
    where
        Self: 'a,
        'b: 'a,
    = impl Future<Output = Result<(), Self::Error>> + 'a;

    fn transaction<'a, 'b>(
        &'a mut self,
        address: A,
        operations: &'a mut [Operation<'b>],
    ) -> Self::TransactionFuture<'a, 'b> {
        async move { self.0.lock().await.transaction(address, operations).await }
    }
}
```

cc `@matoushybl`

Co-authored-by: Dario Nieuwenhuis <[email protected]>
  • Loading branch information
bors[bot] and Dirbaio authored Feb 15, 2022
2 parents 173750c + 244d383 commit ddf4375
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 11 deletions.
22 changes: 12 additions & 10 deletions embedded-hal-async/src/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,10 @@ pub trait I2c<A: AddressMode = SevenBitAddress>: ErrorType {
) -> Self::WriteReadFuture<'a>;

/// Future returned by the `transaction` method.
type TransactionFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
type TransactionFuture<'a, 'b>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;
Self: 'a,
'b: 'a;

/// Execute the provided operations on the I2C bus as a single transaction.
///
Expand All @@ -124,11 +125,11 @@ pub trait I2c<A: AddressMode = SevenBitAddress>: ErrorType {
/// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing
/// - `SR` = repeated start condition
/// - `SP` = stop condition
fn transaction<'a>(
fn transaction<'a, 'b>(
&'a mut self,
address: A,
operations: &mut [Operation<'a>],
) -> Self::TransactionFuture<'a>;
operations: &'a mut [Operation<'b>],
) -> Self::TransactionFuture<'a, 'b>;
}

impl<A: AddressMode, T: I2c<A>> I2c<A> for &mut T {
Expand Down Expand Up @@ -164,16 +165,17 @@ impl<A: AddressMode, T: I2c<A>> I2c<A> for &mut T {
T::write_read(self, address, bytes, buffer)
}

type TransactionFuture<'a>
type TransactionFuture<'a, 'b>
where
Self: 'a,
= T::TransactionFuture<'a>;
'b: 'a,
= T::TransactionFuture<'a, 'b>;

fn transaction<'a>(
fn transaction<'a, 'b>(
&'a mut self,
address: A,
operations: &mut [Operation<'a>],
) -> Self::TransactionFuture<'a> {
operations: &'a mut [Operation<'b>],
) -> Self::TransactionFuture<'a, 'b> {
T::transaction(self, address, operations)
}
}
2 changes: 1 addition & 1 deletion src/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ impl<T: ErrorType> ErrorType for &mut T {
/// Address mode (7-bit / 10-bit)
///
/// Note: This trait is sealed and should not be implemented outside of this crate.
pub trait AddressMode: private::Sealed {}
pub trait AddressMode: private::Sealed + 'static {}

/// 7-bit address mode type
pub type SevenBitAddress = u8;
Expand Down

0 comments on commit ddf4375

Please sign in to comment.