Arc 递归指针 销毁栈溢出 有没有解决方法

Demo

use std::sync::{Arc, Weak};
trait TestTrait {}
struct Test(Option<Arc<dyn TestTrait>>);

impl TestTrait for Test {}

#[test]
fn arc_test(){
    let mut ptr:Arc<dyn TestTrait> = Arc::new(Test(None));
    for _ in 1..1000000{
        ptr = Arc::new(Test(Some(Arc::clone(&ptr))))
    }
}

来源

#[test]
#[allow(unused_variables)]
fn test() {
    use self::gostd::Context;
    use ::std::sync::Arc;
    let basectx = Context::CancelContext::new(None);
    // cancel();
    let mut pctx = Arc::clone(&basectx.ctx);

    for i in 0..1000000 {
        let ctx = Context::CancelContext::new(Some(Arc::clone(&pctx)));
        pctx = Arc::clone(&ctx.ctx);
    }

    // cancel();
    let a = basectx.ctx.deadline();
    let b = pctx.deadline();
    println!("time: {:?}\ntime: {:?}", a, b)
}


// #![feature(dropck_eyepatch)]
#![allow(unused_imports)]
use std::sync::{Arc, Weak};
trait TestTrait {}
struct Test(Option<Arc<dyn TestTrait>>);

impl TestTrait for Test {}

#[test]
fn arc_test(){
    let mut ptr:Arc<dyn TestTrait> = Arc::new(Test(None));
    for _ in 1..1000000{
        ptr = Arc::new(Test(Some(Arc::clone(&ptr))))
    }
}


pub mod gostd {
    #[allow(non_snake_case)]
    pub mod Context {

        // use std::slice::Concat;
        use std::cell::UnsafeCell;
        use std::ptr;
        use std::sync::atomic::{AtomicBool, Ordering};
        use std::sync::{Arc, Once};
        // use std::marker::PhantomData;
        use chrono::{DateTime, Utc};
        pub trait Context {
            fn done(&self) -> bool;
            fn deadline(&self) -> Option<DateTime<Utc>>;
            fn parent(&self) -> &Option<Arc<dyn Context>>;
            fn ref_done(&self) -> &AtomicBool;
        }

        pub struct Ctx<'a> {
            pub ctx: Arc<dyn Context + 'a>,
            pub cancel: Box<dyn Fn() + 'a>,
        }

        #[allow(dead_code)]
        pub struct CancelContext {
            deadline: UnsafeCell<Option<DateTime<Utc>>>,
            done: Arc<AtomicBool>,
            parent: Option<Arc<dyn Context>>,
            call: Once,
        }
        #[allow(dead_code)]
        impl CancelContext {
            #[allow(dead_code)]
            pub fn new<'a>(parent: Option<Arc<dyn Context>>) -> Ctx<'a> {
                let ctx = Arc::new(Self {
                    parent, //: parent.map(|p| p.parent()),
                    done: Arc::new(AtomicBool::new(false)),
                    deadline: UnsafeCell::new(None),
                    call: Once::new(),
                    // _marker: PhantomData
                });
                let ret = Arc::clone(&ctx);
                return Ctx {
                    ctx: ret,
                    cancel: Box::new(move || {
                        ctx.cancel();
                    }),
                };
            }

            #[allow(unused_variables, unused_must_use)]
            pub fn cancel(&self) {
                self.done
                    .compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::SeqCst);
                self.set_deadline(Some(Utc::now()))
            }

            fn set_deadline(&self, t: Option<DateTime<Utc>>) {
                self.call.call_once(|| unsafe {
                    let p = self.deadline.get();
                    *p = t
                })
            }
        }



        impl Context for CancelContext {

            #[allow(unused_must_use)]
            fn done(&self) -> bool {
                if !self.done.load(Ordering::Relaxed) {
                    let mut parent: &Option<Arc<dyn Context>> = self.parent();
                    let mut stack = Vec::new();
                    while let Some(p) = parent {
                        stack.push(p);
                        if p.ref_done().load(Ordering::Relaxed) {
                            stack.into_iter().rev().for_each(|p| {
                                p.ref_done().compare_exchange_weak(
                                    false,
                                    true,
                                    Ordering::SeqCst,
                                    Ordering::SeqCst,
                                );
                            });
                            self.set_deadline(p.deadline());
                            self.cancel();
                            break;
                        };
                        parent = p.parent();
                    }
                };
                return self.done.load(Ordering::Relaxed);
            }

            fn deadline(&self) -> Option<DateTime<Utc>> {
                unsafe {
                    let p = self.deadline.get();
                    *p.clone()
                }
            }

            fn parent(&self) -> &Option<Arc<dyn Context>> {
                &self.parent
            }

            fn ref_done(&self) -> &AtomicBool {
                &self.done
            }
        }
    }
}
謎麟
讨论数量: 2
Oraoto
impl Drop for Test {
    fn drop(&mut self) {
        let mut test = self;
        let mut arcs = vec![];
        loop {
            // base case
            if test.0.is_none() {
                return;
            }

            // test = Test(None), option = Arc<dyn TestTrait>
            let mut option = None;
            std::mem::swap(&mut test.0, &mut option);

            // get a raw pointer, to go to next element
            let arc = option.unwrap();
            let raw = Arc::into_raw(arc);
            test = unsafe {&mut *(raw as *mut Test)};

            // convert back to Arc, to avoid memory leak
            let arc = unsafe {Arc::from_raw(raw)};
            arcs.push(arc);
        }
    }
}

基本思路是把递归转成迭代,本来有一条 Some -> Arc -> Some -> Arc -> Some .... 这样的链,递归 drop 会 stackoverflow,我一个一个地把 Some 换成 None,断掉这个链,变成 [Arc->None, Arc->None ....] 这样的一个 vec,由 vec 迭代 drop 掉每个 Arc。

不过用了 unsafe,不知有没更好的方法。

3年前 评论
MaxKing (楼主) 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!