Use a mutable borrow in a locked mutex closure

Hi! I'm working on an embedded system using rtic, and I need to update some shared settings in a loop and pass it into the lock closure of another shared resource. Clearly, the struct needs to be mutable for updating in the loop, but a closure tries to borrow it immutably. What would be the right way of implementing this?

My code looks something like this:

#[derive(Copy, Clone)]
struct DeviceConfig {
  channels: [ChannelConfig; 2],
  // ...
}

#[rtic::app(...)]
mod app {
    #[shared]
    struct Shared {
        settings: DeviceConfig,
        device: Device,
        //...
    }

    #[task(..., shared=[settings, device])]
    fn process(c: process:Context) {
        c.shared.settings.lock(|settings| {
            // Mutable borrow of `settings` occurs here
            for ch in settings.channels.iter_mut() {
                 for data in buffer {
                     // Update some settings field based on input data
                     ch.foo = calculate_foo(data);
                     // Immutable borrow of `settings` occurs here -- throws compiler error
                     c.shared.device.lock(|dvc| dvc.update(settings));
                 }
            }
        });
    }
}

where Device::update(&mut self, config: DeviceConfig) and ChannelConfig is some nested data structure.

I've tried playing around with a few things like locking the device along with the settings above so everything is in the closure, but that didn't help either. I'd ideally like to avoid an expensive clone here since this is the main data processing loop of the application.

Thanks in advance!

Edit: typo -- inconsistent naming

Judging from the method names, ch holds an exclusive (&mut) borrow of settings inside this loop:

            for ch in settings.channel.iter_mut() {

which means you can't use settings itself inside that loop. You need some way of obtaining ch in the inner loop...

            for ch_idx in settings.channel.something() {
                 for data in buffer {
                     let ch = settings.channel.some_way(ch_idx);
                     ch.foo = calculate_foo(data);
                     c.shared.device.lock(|dvc| dvc.update(settings));

...so that the exclusive reborrow of settings can expire before the closure captures it, each time through the inner loop.

Okay that makes sense, that worked.

Also apologies, I realised that I was erroneous with my naming in the process here, (settings.channel vs settings.channels), but the answer still holds.

Thank you so much!

1 Like