goscript_vm/
gc.rs

1use super::objects::*;
2use super::value::{GosValue, RCQueue, RCount, IRC};
3use std::cell::Ref;
4use std::cell::RefCell;
5use std::convert::TryFrom;
6use std::rc::{Rc, Weak};
7
8pub struct GcoVec {
9    inner: Rc<RefCell<Vec<GcWeak>>>,
10}
11
12impl GcoVec {
13    pub fn new() -> GcoVec {
14        GcoVec {
15            inner: Rc::new(RefCell::new(Vec::new())),
16        }
17    }
18
19    #[inline]
20    pub fn add(&self, v: &GosValue) {
21        let weak = GcWeak::from_gosv(v);
22        self.add_weak(weak);
23    }
24
25    #[inline]
26    pub fn add_weak(&self, w: GcWeak) {
27        self.inner.borrow_mut().push(w);
28    }
29
30    fn borrow_data(&self) -> Ref<Vec<GcWeak>> {
31        self.inner.borrow()
32    }
33}
34
35#[derive(Debug, Clone)]
36pub enum GcWeak {
37    Array(Weak<(ArrayObj, RCount)>),
38    Closure(Weak<(RefCell<ClosureObj>, RCount)>),
39    Slice(Weak<(SliceObj, RCount)>),
40    Map(Weak<(MapObj, RCount)>),
41    Struct(Weak<(RefCell<StructObj>, RCount)>),
42    // todo:
43    // GC doesn't support channel for now, because we can't access the
44    // underlying objects (unless we don't use smol::channel and write
45    // it ourself).
46    // but memory leaking circles that involve channels should be very
47    // rare, so in practice we may not need it at all.
48    //Channel(Weak<(RefCell<ChannelObj>, RCount)>),
49}
50
51impl GcWeak {
52    pub fn from_gosv(v: &GosValue) -> GcWeak {
53        match v {
54            GosValue::Array(a) => GcWeak::Array(Rc::downgrade(a)),
55            GosValue::Closure(c) => GcWeak::Closure(Rc::downgrade(c)),
56            GosValue::Slice(s) => GcWeak::Slice(Rc::downgrade(s)),
57            GosValue::Map(m) => GcWeak::Map(Rc::downgrade(m)),
58            GosValue::Struct(s) => GcWeak::Struct(Rc::downgrade(s)),
59            _ => unreachable!(),
60        }
61    }
62
63    fn to_gosv(&self) -> Option<GosValue> {
64        match &self {
65            GcWeak::Array(w) => w.upgrade().map(|v| {
66                v.1.set(i32::try_from(w.strong_count()).unwrap() - 1);
67                GosValue::Array(v)
68            }),
69            GcWeak::Closure(w) => w.upgrade().map(|v| {
70                v.1.set(i32::try_from(w.strong_count()).unwrap() - 1);
71                GosValue::Closure(v)
72            }),
73            GcWeak::Slice(w) => w.upgrade().map(|v| {
74                v.1.set(i32::try_from(w.strong_count()).unwrap() - 1);
75                GosValue::Slice(v)
76            }),
77            GcWeak::Map(w) => w.upgrade().map(|v| {
78                v.1.set(i32::try_from(w.strong_count()).unwrap() - 1);
79                GosValue::Map(v)
80            }),
81            GcWeak::Struct(w) => w.upgrade().map(|v| {
82                v.1.set(i32::try_from(w.strong_count()).unwrap() - 1);
83                GosValue::Struct(v)
84            }),
85        }
86    }
87}
88
89fn children_ref_sub_one(val: &GosValue) {
90    match val {
91        GosValue::Array(arr) => arr
92            .0
93            .borrow_data_mut()
94            .iter()
95            .for_each(|obj| obj.borrow().ref_sub_one()),
96        GosValue::Closure(c) => c.0.borrow().ref_sub_one(),
97        GosValue::Slice(s) => {
98            let sdata = &s.0;
99            if !sdata.is_nil() {
100                sdata
101                    .borrow_data()
102                    .iter()
103                    .for_each(|obj| obj.borrow().ref_sub_one())
104            }
105        }
106        GosValue::Map(m) => {
107            let mdata = &m.0;
108            if !mdata.is_nil() {
109                mdata.borrow_data().iter().for_each(|(k, v)| {
110                    k.ref_sub_one();
111                    v.borrow().ref_sub_one();
112                })
113            }
114        }
115        GosValue::Struct(s) => s.0.borrow().fields.iter().for_each(|obj| obj.ref_sub_one()),
116        _ => unreachable!(),
117    };
118}
119
120fn children_mark_dirty(val: &GosValue, queue: &mut RCQueue) {
121    match val {
122        GosValue::Array(arr) => arr
123            .0
124            .borrow_data_mut()
125            .iter()
126            .for_each(|obj| obj.borrow().mark_dirty(queue)),
127        GosValue::Closure(c) => c.0.borrow().mark_dirty(queue),
128        GosValue::Slice(s) => {
129            let sdata = &s.0;
130            if !sdata.is_nil() {
131                sdata
132                    .borrow_data()
133                    .iter()
134                    .for_each(|obj| obj.borrow().mark_dirty(queue))
135            }
136        }
137        GosValue::Map(m) => {
138            let mdata = &m.0;
139            if !mdata.is_nil() {
140                mdata.borrow_data().iter().for_each(|(k, v)| {
141                    k.mark_dirty(queue);
142                    v.borrow().mark_dirty(queue);
143                })
144            }
145        }
146        GosValue::Struct(s) => {
147            s.0.borrow()
148                .fields
149                .iter()
150                .for_each(|obj| obj.mark_dirty(queue))
151        }
152        _ => unreachable!(),
153    };
154}
155
156fn break_cycle(obj: &mut GosValue) {
157    match obj {
158        GosValue::Array(arr) => arr.0.borrow_data_mut().clear(),
159        GosValue::Closure(c) => {
160            let r: &mut ClosureObj = &mut RefCell::borrow_mut(&c.0);
161            if let Some(uvs) = &mut r.uvs {
162                uvs.clear();
163            }
164            r.recv = None;
165            r.ffi = None;
166        }
167        GosValue::Slice(s) => {
168            let sdata = &s.0;
169            if !sdata.is_nil() {
170                // slices share data, we cannot clear the data here
171            }
172        }
173        GosValue::Map(m) => {
174            let mdata = &m.0;
175            if !mdata.is_nil() {
176                mdata.borrow_data_mut().clear();
177            }
178        }
179        GosValue::Struct(s) => RefCell::borrow_mut(&s.0).fields.clear(),
180        _ => unreachable!(),
181    };
182}
183
184/// put the non-zero-rc on the left, and the others on the right
185fn partition_to_scan(to_scan: &mut Vec<GosValue>) -> usize {
186    let len = to_scan.len();
187    if len == 0 {
188        return 0;
189    }
190    let mut p0 = 0;
191    let mut p1 = len - 1;
192    loop {
193        while p0 < len - 1 && to_scan[p0].rc() > 0 {
194            p0 += 1;
195        }
196        while p1 > 1 && to_scan[p1].rc() <= 0 {
197            p1 -= 1;
198        }
199        if p0 >= p1 {
200            break;
201        }
202        to_scan.swap(p0, p1);
203    }
204    p0
205}
206
207pub fn gc(objs: &GcoVec) {
208    let mut to_scan: Vec<GosValue> = objs
209        .borrow_data()
210        .iter()
211        .filter_map(|o| o.to_gosv())
212        .collect();
213    //print!("objs before GC: {}\n", to_scan.len());
214    for v in to_scan.iter() {
215        children_ref_sub_one(v);
216    }
217
218    let boundary = partition_to_scan(&mut to_scan);
219    for i in boundary..to_scan.len() {
220        to_scan[i].set_rc(-(i as IRC));
221    }
222
223    let mut queue: RCQueue = RCQueue::new();
224    for i in 0..boundary {
225        children_mark_dirty(&to_scan[i], &mut queue);
226    }
227
228    loop {
229        if let Some(i) = queue.pop_front() {
230            let obj = &to_scan[(-i) as usize];
231            obj.set_rc(666);
232            children_mark_dirty(&obj, &mut queue);
233        } else {
234            break;
235        }
236    }
237
238    for mut obj in to_scan.into_iter() {
239        if obj.rc() <= 0 {
240            break_cycle(&mut obj);
241        }
242    }
243
244    let _result: Vec<GosValue> = objs
245        .borrow_data()
246        .iter()
247        .filter_map(|o| o.to_gosv())
248        .collect();
249    //print!("objs left after GC: {}\n", result.len());
250}