Serde for AtomicPtr

Could someone please point out what I am doing wrong? I want to implement serde capability for a struct with an AtomicPtr. I get the following error when I test it (I use bincode).

panicked at 'called Result::unwrap() on an Err value: Custom("invalid u8 while decoding bool, expected 0 or 1, found 48")'

    #[derive(Serialize, Deserialize)]
    struct A {
        #[serde(serialize_with = "freeze", deserialize_with = "defreeze")]
        datum: AtomicPtr<bool>,
    }

    impl A {
        fn new() -> Self {
            let v = Box::into_raw(Box::new(true));
            return Self {
                datum: AtomicPtr::new(v),
            };
        }

        fn value(&self) -> bool {
            let value = self.datum.load(Ordering::SeqCst);
            let value = unsafe { Box::from_raw(value) };
            return *value;
        }
    }

    fn freeze<S>(v: &AtomicPtr<bool>, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let value = v.load(Ordering::SeqCst);
        let value = unsafe { Box::from_raw(value) };
        (*value).serialize(serializer)
    }

    pub fn defreeze<'de, D>(deserializer: D) -> Result<AtomicPtr<bool>, D::Error>
    where
        D: Deserializer<'de>,
    {
        use serde::de::Error;
        let res = bool::deserialize(deserializer)
            .and_then(|value| {
                let v = Box::into_raw(Box::new(value));
                Ok(AtomicPtr::new(v))
            })
            .map_err(|err| Error::custom(err.to_string()));
        return res;
    }



This code has a serious problem, even without the serde part -- you're creating and dropping the Box more than once.

A needs to look something like

struct A {
    datum: AtomicPtr<bool>,
}

impl A {
    fn new() -> Self {
        let v = Box::into_raw(Box::new(true));
        Self { datum: AtomicPtr::new(v) }
    }

    fn value(&self) -> bool {
        let value = self.datum.load(SeqCst);
        // no Box::from_raw, just access the pointer
        unsafe { *value }
    }
}

impl Drop for A {
    fn drop(&mut self) {
        let p = self.datum.load(SeqCst);
        // only ever call Box::from_raw once, to drop the box
        drop(unsafe { Box::from_raw(p) });
        // otherwise it'll get implicitly dropped and make your pointer invalid
    }
}

With this it should be relatively clear what the problem is. defreeze looks correct to me, but freeze needs to not drop the Box, e.g.

    fn freeze<S>(v: &AtomicPtr<bool>, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let value = v.load(Ordering::SeqCst);
        unsafe { *value }.serialize(serializer)
    }

However, note that this is still quite problematic in a larger context. If you're ever doing atomic swaps on the AtomicPtr, then access is dereferencing into a box that some other thread could be accessing and/or deallocating. You probably don't want to use AtomicPtr to store a Box. If you do, you should probably use the atomicbox crate to avoid problems like this. More likely useful is arc-swap.

1 Like

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.