On-the-fly substitution of `Option<Arc<Mutex<Box<dyn T>>>>`

Suppose I have an object video_source: Option<Arc<Mutex<Box<dyn GetVideo>>>> and I pass it to a thread:

std::thread::spawn(||{
    loop {
        if let Some(video_source) = video_source {
            let video_frame = video_source.lock().unwrap().get();
        }
    }
})

where

trait GetVideo {
    fn get() -> Vec<u8>
}

What if I want to change the video source on the fly? Well, I'd do this on another thread:

video_frame.unwrap().lock().unwrap() = Box::new(other_source);

I want to make this idea more generic. I want a type that permits such thing. Here's my sketch:

use std::sync::{Arc, Mutex};

pub type OnTheFlyInner<T> = Box<T + Send + Sync>;
pub type OnTheFly<T> = Arc<Mutex<OnTheFlyInner<T>>>;

//I'd like this to be a method of `OnTheFly`
pub fn on_the_fly_substitute(on_the_fly: &mut Option<OnTheFly>, substitute_by: Option<OnTheFlyInner>) {
    if let Some(substitute_by) = substitute_by {
        if let Some(on_the_fly) = on_the_fly {
            *on_the_fly.lock().unwrap() = substitute_by;
        }
    } else {
        on_the_fly.take();
    }
}

However, I cannot make something generic over T where T is a trait, it should be a type.

Any ideas?

This is because you've used + Send + Sync syntax which is a shorthand for traits only. If you delete these, and use where T: Send + Sync in method definition, it should work.

pub type OnTheFlyInner<T> = Box<T>;
pub type OnTheFly<T> = Arc<Mutex<OnTheFlyInner<T>>>;

//I'd like this to be a method of `OnTheFly`
pub fn on_the_fly_substitute<T: Send + Sync>(on_the_fly: &mut Option<OnTheFly<T>>, substitute_by: Option<OnTheFlyInner<T>>) {

BTW, there's arc-swap crate.

1 Like

Thanks, I've come up with this:

use std::ops::{Deref, DerefMut};
use std::sync::{Arc, LockResult, Mutex, MutexGuard, PoisonError};

pub type OnTheFlyInner<T> = Box<T>;

#[derive(Clone)]
pub struct OnTheFly<T> {
    inner: Arc<Mutex<Option<OnTheFlyInner<T>>>>,
}

pub struct MutexGuardRef<'a, T> {
    mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}

impl<'a, T> MutexGuardRef<'a, T> {
    pub fn inner(&self) -> Option<&T> {
        match self.mutex_guard.deref() {
            Some(b) => Some(&*b),
            None => None,
        }
    }

    pub fn inner_mut(&mut self) -> Option<&mut T> {
        match self.mutex_guard.deref_mut() {
            Some(b) => Some(&mut *b),
            None => None,
        }
    }
}

impl<T> OnTheFly<T>
where
    T: Sized + Send + Sync,
{
    pub fn new(b: Box<T>) -> OnTheFly<T> {
        OnTheFly {
            inner: Arc::new(Mutex::new(Some(b))),
        }
    }

    pub fn new_empty() -> OnTheFly<T> {
        OnTheFly {
            inner: Arc::new(Mutex::new(None)),
        }
    }

    pub fn replace(&mut self, replace_by: Option<OnTheFlyInner<T>>) {
        if let Some(on_the_fly_inner) = replace_by {
            self.inner.lock().unwrap().replace(on_the_fly_inner);
        } else {
            self.inner.lock().unwrap().take();
        }
    }

    pub fn lock(&self) -> LockResult<MutexGuardRef<'_, T>> {
        match self.inner.lock() {
            Ok(mut m) => match m.as_deref_mut() {
                _ => Ok(MutexGuardRef { mutex_guard: m }),
            },
            Err(e) => {
                Err(PoisonError::new(MutexGuardRef{
                    mutex_guard: e.into_inner()
                }))
            }
        }
    }
}

fn main() {
    let mut o = OnTheFly::new(Box::new(0));
    let oo = o.clone();
    std::thread::spawn(move || loop {
        if let Some(oo) = oo.lock().unwrap().inner_mut() {
            *oo += 1;
            println!("value: {}", *oo);
        }
        std::thread::sleep(std::time::Duration::from_secs(1));
    });
    //Waits a little before substituting the inner object on-the-fly
    std::thread::sleep(std::time::Duration::from_secs(5));
    o.replace(Some(Box::new(12345)));
    std::thread::sleep(std::time::Duration::from_secs(100))
}

Playground

As you see, replacing is very easy:

o.replace(Some(Box::new(12345)));

however, calling some method on OnTheFly requires me calling inner_mut:

 if let Some(oo) = oo.lock().unwrap().inner_mut() {
    oo.do_something();
 }

because I couldn't make MutexGuardRef implement Deref and DerefMut. I tried, but I couldn't:

use std::ops::{Deref};
use std::sync::{MutexGuard};

pub struct MutexGuardRef<'a, T> {
    mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}

impl<'a, T> Deref for MutexGuardRef<'a, T> {
    type Target = Option<T>;

    fn deref(&'a self) -> &'a Self::Target {
        &self.mutex_guard.deref().as_ref().map(|x|*x)
    }
}

this gives:

   = note: expected fn pointer `fn(&MutexGuardRef<'a, T>) -> &Option<T>`
              found fn pointer `fn(&'a MutexGuardRef<'a, T>) -> &'a Option<T>`

What do you think? It's almost perfect except for the inner_mut unnecessary call.

Yeah, that's tricky. MutexGuard doesn't have a map like Ref has, so you can't simply get a new MutexGuard for the inner value. parking_lot has a map tho.

The Deref trait wants to start a new borrow, and you can't tell it to do something else, so you have to stick to the method exactly like in the trait.

Option<Box<T>> is not compatible with Option<T>. You can convert &Option<T> to Option<&T>, but there's no method to do it other way. I suppose you could transmute them?

use std::ops::{Deref};
use std::sync::{MutexGuard};

pub struct MutexGuardRef<'a, T> {
    mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}

impl<'a, T> Deref for MutexGuardRef<'a, T> {
    type Target = Option<T>;

    fn deref(&self) -> &Self::Target {
        let tmp = self.mutex_guard.deref().as_deref();
        unsafe { 
            std::mem::transmute::<Option<&T>, &Option<T>>(tmp)
        }
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.