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
}
}
}
}
基本思路是把递归转成迭代,本来有一条 Some -> Arc -> Some -> Arc -> Some .... 这样的链,递归 drop 会 stackoverflow,我一个一个地把 Some 换成 None,断掉这个链,变成 [Arc->None, Arc->None ....] 这样的一个 vec,由 vec 迭代 drop 掉每个 Arc。
不过用了 unsafe,不知有没更好的方法。