1use std::sync::atomic::{AtomicBool, Ordering};
10
11use catallaxy_gc::{Header, ObjState};
12
13use crate::class::{heap_class, ObjKind};
14use crate::dict::DictTable;
15use crate::mutator::{Mutator, RtResult, Trap};
16use crate::object::*;
17use crate::value::Value;
18
19pub static PROMOTE_ON_ACCESS: AtomicBool = AtomicBool::new(false);
22
23static PROMOTION_HOOK: std::sync::OnceLock<fn(&str)> = std::sync::OnceLock::new();
25
26pub fn set_promotion_hook(f: fn(&str)) {
27 let _ = PROMOTION_HOOK.set(f);
28}
29
30#[derive(Copy, Clone, PartialEq, Eq, Debug)]
32pub enum Access {
33 Local,
35 Shared,
37}
38
39#[inline(always)]
41pub fn check_read(m: &Mutator, h: &Header) -> RtResult<Access> {
42 let meta = h.meta();
43 match Header::state_of(meta) {
44 ObjState::Owned if Header::owner_of(meta) == m.tid => Ok(Access::Local),
45 ObjState::Frozen => Ok(Access::Local),
46 ObjState::Shared => Ok(Access::Shared),
47 ObjState::Owned => foreign(m, h),
48 }
49}
50
51#[inline(always)]
54pub fn check_write(m: &Mutator, h: &Header) -> RtResult<Access> {
55 let meta = h.meta();
56 match Header::state_of(meta) {
57 ObjState::Owned if Header::owner_of(meta) == m.tid => {
58 if Header::needs_remember(meta) {
59 remember(m, h);
60 }
61 Ok(Access::Local)
62 }
63 ObjState::Shared => Ok(Access::Shared),
64 ObjState::Frozen => Err(frozen_error(h)),
65 ObjState::Owned => foreign(m, h),
66 }
67}
68
69pub fn frozen_error(h: &Header) -> Trap {
70 let cls = unsafe { heap_class(h) };
71 Trap::Panic(format!("FrozenError: cannot modify frozen {}", cls.name))
72}
73
74#[cold]
75fn foreign(m: &Mutator, h: &Header) -> RtResult<Access> {
76 let cls = unsafe { heap_class(h) };
77 if PROMOTE_ON_ACCESS.load(Ordering::Relaxed) {
78 share_deep(m, Value::from_ptr(h), true)?;
79 if let Some(hook) = PROMOTION_HOOK.get() {
80 hook(&cls.name);
81 } else {
82 eprintln!("warning: promoted {} to shared on access from thread {}", cls.name, m.tid);
83 }
84 return Ok(Access::Shared);
85 }
86 Err(Trap::Panic(format!(
87 "OwnershipError: {} owned by thread {} accessed from thread {}",
88 cls.name,
89 h.owner(),
90 m.tid
91 )))
92}
93
94#[cold]
96pub fn remember(m: &Mutator, h: &Header) {
97 unsafe { (*m.heap_ptr()).remember(h) };
99}
100
101pub fn escape(v: Value, tid: u16) {
106 let Some(h) = v.header() else { return };
107 let meta = h.meta();
108 let owned_old = Header::state_of(meta) == ObjState::Owned && Header::owner_of(meta) == tid && h.is_remembered();
109 if !h.is_young() && !owned_old {
110 return; }
112 let mut work = vec![v];
113 let mut seen_old = rustc_hash::FxHashSet::default();
114 while let Some(x) = work.pop() {
115 let Some(h) = x.header() else { continue };
116 if h.is_young() {
117 h.tenure();
118 for_each_child(x, |c| work.push(c));
119 continue;
120 }
121 let meta = h.meta();
122 if Header::state_of(meta) == ObjState::Owned
123 && Header::owner_of(meta) == tid
124 && h.is_remembered()
125 && seen_old.insert(x.0)
126 {
127 for_each_child(x, |c| work.push(c));
128 }
129 }
130}
131
132pub fn for_each_child(v: Value, mut f: impl FnMut(Value)) {
134 let Some(h) = v.header() else { return };
135 let cls = unsafe { heap_class(h) };
136 unsafe {
137 match cls.kind {
138 ObjKind::Instance => {
139 let o = as_instance(v);
140 for i in 0..cls.nfields() {
141 f(o.get(i));
142 }
143 }
144 ObjKind::List => {
145 for x in as_list(v).to_vec() {
146 f(x)
147 }
148 }
149 ObjKind::Dict | ObjKind::Set => {
150 for e in as_dict(v).table().iter() {
151 f(e.key);
152 f(e.value);
153 }
154 }
155 ObjKind::Tuple => {
156 for &x in as_tuple(v).elems() {
157 f(x)
158 }
159 }
160 ObjKind::Closure => {
161 for &x in as_closure(v).caps() {
162 f(x)
163 }
164 }
165 ObjKind::Cell => f(Value(as_cell(v).value.load(Ordering::Relaxed))),
166 ObjKind::Mutex | ObjKind::Atomic => {
167 }
169 _ => {}
170 }
171 }
172}
173
174pub fn freeze_deep(m: &Mutator, v: Value) -> RtResult<()> {
177 escape(v, m.tid);
179 let mut work = vec![v];
180 while let Some(x) = work.pop() {
181 let Some(h) = x.header() else { continue };
182 let meta = h.meta();
183 match Header::state_of(meta) {
184 ObjState::Frozen | ObjState::Shared => continue,
185 ObjState::Owned if Header::owner_of(meta) != m.tid => {
186 let cls = unsafe { heap_class(h) };
187 return Err(Trap::Panic(format!(
188 "OwnershipError: cannot freeze {} owned by thread {}",
189 cls.name,
190 h.owner()
191 )));
192 }
193 ObjState::Owned => {
194 let kind = unsafe { heap_class(h).kind };
195 if matches!(kind, ObjKind::StringBuilder | ObjKind::Native | ObjKind::Thread) {
196 continue;
198 }
199 h.set_state(ObjState::Frozen);
200 for_each_child(x, |c| work.push(c));
201 }
202 }
203 }
204 Ok(())
205}
206
207pub fn share_deep(m: &Mutator, v: Value, steal: bool) -> RtResult<()> {
210 escape(v, m.tid);
211 let mut work = vec![v];
212 while let Some(x) = work.pop() {
213 let Some(h) = x.header() else { continue };
214 let meta = h.meta();
215 match Header::state_of(meta) {
216 ObjState::Frozen | ObjState::Shared => continue,
217 ObjState::Owned if !steal && Header::owner_of(meta) != m.tid => {
218 let cls = unsafe { heap_class(h) };
219 return Err(Trap::Panic(format!(
220 "OwnershipError: cannot share {} owned by thread {}",
221 cls.name,
222 h.owner()
223 )));
224 }
225 ObjState::Owned => {
226 h.set_state(ObjState::Shared);
227 for_each_child(x, |c| work.push(c));
228 }
229 }
230 }
231 Ok(())
232}
233
234pub fn transfer_deep(v: Value, from: u16, to: u16) {
237 if from == to {
238 return;
239 }
240 escape(v, from);
243 let mut work = vec![v];
244 while let Some(x) = work.pop() {
245 let Some(h) = x.header() else { continue };
246 let meta = h.meta();
247 if Header::state_of(meta) == ObjState::Owned && Header::owner_of(meta) == from {
248 h.set_owned_by(to);
249 for_each_child(x, |c| work.push(c));
250 }
251 }
252}
253
254pub const TRANSIT_TID: u16 = 0;
256
257#[inline]
260pub fn publish_into_shared(m: &Mutator, v: Value) -> RtResult<()> {
261 escape(v, m.tid);
262 if let Some(h) = v.header() {
263 if h.state() == ObjState::Owned {
264 share_deep(m, v, false)?;
265 }
266 }
267 Ok(())
268}
269
270pub fn lock_obj<'a>(m: &Mutator, h: &'a Header) -> catallaxy_gc::ObjLockGuard<'a> {
273 if !h.try_lock(m.tid) {
274 catallaxy_gc::enter_native(&m.rec);
275 h.lock(m.tid);
276 catallaxy_gc::leave_native(&m.rec);
277 }
278 catallaxy_gc::ObjLockGuard::adopt(h)
279}
280
281#[inline(never)]
287pub fn read_list<'a>(m: &Mutator, v: Value) -> RtResult<&'a ListObj> {
288 let l = list_of(v)?;
289 check_read(m, unsafe { &*v.as_ptr() })?;
290 Ok(l)
291}
292
293#[inline]
297pub fn write_list<R>(m: &Mutator, v: Value, f: impl FnOnce(&ListObj, Access) -> R) -> RtResult<R> {
298 let l = list_of(v)?;
299 with_write(m, v, |access| f(l, access))
300}
301
302#[inline]
306pub fn read_dict<R>(m: &Mutator, v: Value, f: impl Fn(&DictTable) -> R) -> RtResult<R> {
307 dict_of(v)?;
308 unsafe { read_dict_unchecked(m, v, f) }
310}
311
312#[inline]
317pub unsafe fn read_dict_unchecked<R>(m: &Mutator, v: Value, f: impl Fn(&DictTable) -> R) -> RtResult<R> {
318 let t = unsafe { as_dict(v) }.table();
319 let h = unsafe { &*v.as_ptr() };
320 match check_read(m, h)? {
321 Access::Local => Ok(f(t)),
322 Access::Shared => match t.read_optimistic(&f) {
323 Some(r) => Ok(r),
324 None => {
325 let _g = lock_obj(m, h);
326 Ok(f(t))
327 }
328 },
329 }
330}
331
332#[inline]
335pub fn write_dict<R>(m: &Mutator, v: Value, f: impl FnOnce(&DictTable, Access) -> R) -> RtResult<R> {
336 dict_of(v)?;
337 unsafe { write_dict_unchecked(m, v, f) }
339}
340
341#[inline]
346pub unsafe fn write_dict_unchecked<R>(m: &Mutator, v: Value, f: impl FnOnce(&DictTable, Access) -> R) -> RtResult<R> {
347 with_write(m, v, |access| f(unsafe { as_dict(v) }.table(), access))
348}
349
350#[inline]
353pub fn with_read<R>(m: &Mutator, v: Value, f: impl FnOnce() -> R) -> RtResult<R> {
354 debug_assert!(v.is_ptr(), "with_read on an immediate");
355 let h = unsafe { &*v.as_ptr() };
356 match check_read(m, h)? {
357 Access::Local => Ok(f()),
358 Access::Shared => {
359 let _g = lock_obj(m, h);
360 Ok(f())
361 }
362 }
363}
364
365#[inline]
369pub fn with_write<R>(m: &Mutator, v: Value, f: impl FnOnce(Access) -> R) -> RtResult<R> {
370 debug_assert!(v.is_ptr(), "with_write on an immediate");
371 let h = unsafe { &*v.as_ptr() };
372 match check_write(m, h)? {
373 Access::Local => Ok(f(Access::Local)),
374 Access::Shared => {
375 let _g = lock_obj(m, h);
376 Ok(f(Access::Shared))
377 }
378 }
379}