Skip to content

Commit 9e256b0

Browse files
committed
[simple] Stack allocate the shadow-stack using a linked list
1 parent f5986a8 commit 9e256b0

File tree

2 files changed

+91
-74
lines changed

2 files changed

+91
-74
lines changed

libs/simple/src/context.rs

Lines changed: 90 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::fmt::{Debug, Formatter};
1212
use std::{fmt, mem};
1313
use std::collections::HashSet;
1414
use parking_lot::{Mutex, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard};
15+
use std::ptr::NonNull;
1516

1617
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1718
enum ContextState {
@@ -93,7 +94,7 @@ impl RawContext {
9394
"original_thread" => original_thread.clone()
9495
)),
9596
shadow_stack: UnsafeCell::new(ShadowStack {
96-
elements: Vec::with_capacity(4)
97+
last: std::ptr::null_mut(),
9798
}),
9899
state: Cell::new(ContextState::Active)
99100
}));
@@ -181,7 +182,7 @@ impl RawContext {
181182
self.logger, "Awaiting collection";
182183
"ptr" => ?ptr,
183184
"current_thread" => FnValue(|_| ThreadId::current()),
184-
"shadow_stack" => ?shadow_stack.elements,
185+
"shadow_stack" => FnValue(|_| format!("{:?}", shadow_stack.as_vec())),
185186
"total_contexts" => pending.total_contexts,
186187
"waiting_contexts" => pending.waiting_contexts,
187188
"state" => ?pending.state,
@@ -260,7 +261,7 @@ impl RawContext {
260261
* inside of RawContext that is not thread-safe.
261262
*/
262263
pub struct SimpleCollectorContext {
263-
raw: ManuallyDrop<Box<RawContext>>,
264+
raw: *mut RawContext,
264265
/// Whether we are the root context
265266
///
266267
/// Only the root actually owns the `Arc`
@@ -270,13 +271,44 @@ pub struct SimpleCollectorContext {
270271
impl SimpleCollectorContext {
271272
pub(crate) unsafe fn register_root(collector: &SimpleCollector) -> Self {
272273
SimpleCollectorContext {
273-
raw: RawContext::register_new(&collector.0),
274+
raw: Box::into_raw(ManuallyDrop::into_inner(
275+
RawContext::register_new(&collector.0)
276+
)),
274277
root: true, // We are responsible for unregistering
275278
}
276279
}
277280
#[inline]
278281
pub(crate) fn collector(&self) -> &RawSimpleCollector {
279-
&*self.raw.collector
282+
unsafe { &(*self.raw).collector }
283+
}
284+
#[inline(always)]
285+
unsafe fn with_shadow_stack<R, T: Trace>(
286+
&self, value: *mut &mut T, func: impl FnOnce() -> R
287+
) -> R {
288+
let old_link = (*(*self.raw).shadow_stack.get()).last;
289+
let new_link = ShadowStackLink {
290+
element: NonNull::new_unchecked(
291+
std::mem::transmute::<
292+
*mut dyn DynTrace,
293+
*mut (dyn DynTrace + 'static)
294+
>(value as *mut dyn DynTrace)
295+
),
296+
prev: old_link
297+
};
298+
(*(*self.raw).shadow_stack.get()).last = &new_link;
299+
let result = func();
300+
debug_assert_eq!(
301+
(*(*self.raw).shadow_stack.get()).last,
302+
&new_link
303+
);
304+
(*(*self.raw).shadow_stack.get()).last = new_link.prev;
305+
result
306+
}
307+
#[cold]
308+
unsafe fn trigger_basic_safepoint<T: Trace>(&self, element: &mut &mut T) {
309+
self.with_shadow_stack(element, || {
310+
(*self.raw).trigger_safepoint();
311+
})
280312
}
281313
}
282314
impl Drop for SimpleCollectorContext {
@@ -287,13 +319,13 @@ impl Drop for SimpleCollectorContext {
287319
unsafe {
288320
trace!(
289321
collector.logger, "Freeing context";
290-
"ptr" => format_args!("{:p}", &**self.raw),
322+
"ptr" => format_args!("{:p}", self.raw),
291323
"current_thread" => FnValue(|_| ThreadId::current()),
292-
"state" => ?self.raw.state.get()
324+
"state" => ?(*self.raw).state.get()
293325
);
294-
collector.free_context(
295-
std::ptr::read(&self.raw)
296-
)
326+
collector.free_context(ManuallyDrop::new(
327+
Box::from_raw(self.raw)
328+
));
297329
}
298330
}
299331
}
@@ -303,28 +335,23 @@ unsafe impl GcContext for SimpleCollectorContext {
303335

304336
#[inline]
305337
unsafe fn basic_safepoint<T: Trace>(&mut self, value: &mut &mut T) {
306-
debug_assert_eq!(self.raw.state.get(), ContextState::Active);
307-
let dyn_ptr = (*self.raw.shadow_stack.get())
308-
.push(value);
309-
if self.raw.collector.should_collect_relaxed() {
310-
self.raw.trigger_safepoint();
338+
debug_assert_eq!((*self.raw).state.get(), ContextState::Active);
339+
if (*self.raw).collector.should_collect_relaxed() {
340+
self.trigger_basic_safepoint(value);
311341
}
312-
debug_assert_eq!(self.raw.state.get(), ContextState::Active);
313-
let popped = (*self.raw.shadow_stack.get())
314-
.pop_unchecked();
315-
debug_assert_eq!(popped, dyn_ptr);
342+
debug_assert_eq!((*self.raw).state.get(), ContextState::Active);
316343
}
317344

318345
unsafe fn freeze(&mut self) {
319-
assert_eq!(self.raw.state.get(), ContextState::Active);
346+
assert_eq!((*self.raw).state.get(), ContextState::Active);
320347
// TODO: Isn't this state read concurrently?
321-
self.raw.state.set(ContextState::Frozen);
348+
(*self.raw).state.set(ContextState::Frozen);
322349
/*
323350
* We may need to notify others that we are frozen
324351
* This means we are now "valid" for the purposes of
325352
* collection ^_^
326353
*/
327-
self.raw.collector.valid_contexts_wait.notify_all();
354+
(*self.raw).collector.valid_contexts_wait.notify_all();
328355
}
329356

330357
unsafe fn unfreeze(&mut self) {
@@ -334,35 +361,32 @@ unsafe impl GcContext for SimpleCollectorContext {
334361
* could trigger undefined behavior!!!!!
335362
*/
336363
self.collector().prevent_collection(|_| {
337-
assert_eq!(self.raw.state.get(), ContextState::Frozen);
338-
self.raw.state.set(ContextState::Active);
364+
assert_eq!((*self.raw).state.get(), ContextState::Frozen);
365+
(*self.raw).state.set(ContextState::Active);
339366
})
340367
}
341368

342369
#[inline]
343370
unsafe fn recurse_context<T, F, R>(&self, value: &mut &mut T, func: F) -> R
344371
where T: Trace, F: for<'gc> FnOnce(&'gc mut Self, &'gc mut T) -> R {
345-
debug_assert_eq!(self.raw.state.get(), ContextState::Active);
346-
let dyn_ptr = (*self.raw.shadow_stack.get())
347-
.push(value);
348-
let mut sub_context = SimpleCollectorContext {
349-
/*
350-
* safe to copy because we wont drop it
351-
* Lifetime is guarenteed to be restricted to
352-
* the closure.
353-
*/
354-
raw: std::ptr::read(&self.raw),
355-
root: false /* don't drop our pointer!!! */
356-
};
357-
let result = func(&mut sub_context, value);
358-
debug_assert!(!sub_context.root);
359-
// Since we're not the root, no need to run drop code!
360-
std::mem::forget(sub_context);
361-
debug_assert_eq!(self.raw.state.get(), ContextState::Active);
362-
let actual_ptr = (*self.raw.shadow_stack.get())
363-
.pop_unchecked();
364-
debug_assert_eq!(actual_ptr, dyn_ptr);
365-
result
372+
debug_assert_eq!((*self.raw).state.get(), ContextState::Active);
373+
self.with_shadow_stack(value, || {
374+
let mut sub_context = ManuallyDrop::new(SimpleCollectorContext {
375+
/*
376+
* safe to copy because we wont drop it
377+
* Lifetime is guarenteed to be restricted to
378+
* the closure.
379+
*/
380+
raw: self.raw,
381+
root: false /* don't drop our pointer!!! */
382+
});
383+
let result = func(&mut *sub_context, value);
384+
debug_assert!(!sub_context.root);
385+
// No need to run drop code on context.....
386+
std::mem::forget(sub_context);
387+
debug_assert_eq!((*self.raw).state.get(), ContextState::Active);
388+
result
389+
})
366390
}
367391
}
368392

@@ -379,40 +403,33 @@ impl !Send for SimpleCollectorContext {}
379403
// Root tracking
380404
//
381405

406+
#[repr(C)]
407+
#[derive(Debug)]
408+
pub(crate) struct ShadowStackLink {
409+
pub element: NonNull<dyn DynTrace>,
410+
/// The previous link in the chain,
411+
/// or NULL if there isn't any
412+
pub prev: *const ShadowStackLink
413+
}
414+
382415
#[derive(Clone, Debug)]
383416
pub struct ShadowStack {
384-
pub(crate) elements: Vec<*mut dyn DynTrace>
417+
/// The last element in the shadow stack,
418+
/// or NULL if it's empty
419+
pub(crate) last: *const ShadowStackLink
385420
}
386421
impl ShadowStack {
387-
#[inline]
388-
pub(crate) unsafe fn push<'a, T: Trace + 'a>(&mut self, value: &'a mut T) -> *mut dyn DynTrace {
389-
let short_ptr = value as &mut (dyn DynTrace + 'a)
390-
as *mut (dyn DynTrace + 'a);
391-
let long_ptr = std::mem::transmute::<
392-
*mut (dyn DynTrace + 'a),
393-
*mut (dyn DynTrace + 'static)
394-
>(short_ptr);
395-
self.elements.push(long_ptr);
396-
long_ptr
422+
unsafe fn as_vec(&self) -> Vec<*mut dyn DynTrace> {
423+
let mut result: Vec<_> = self.reverse_iter().collect();
424+
result.reverse();
425+
result
397426
}
398427
#[inline]
399-
pub(crate) unsafe fn pop_unchecked(&mut self) -> *mut dyn DynTrace {
400-
match self.elements.pop() {
401-
Some(value) => value,
402-
None => {
403-
#[cfg(debug_assertions)] {
404-
/*
405-
* In debug mode we'd rather panic than
406-
* trigger undefined behavior.
407-
*/
408-
unreachable!()
409-
}
410-
#[cfg(not(debug_assertions))] {
411-
// Hint to optimizer we consider this impossible
412-
std::hint::unreachable_unchecked()
413-
}
414-
}
415-
}
428+
pub(crate) unsafe fn reverse_iter(&self) -> impl Iterator<Item=*mut dyn DynTrace> + '_ {
429+
std::iter::successors(
430+
self.last.as_ref(),
431+
|link| link.prev.as_ref()
432+
).map(|link| link.element.as_ptr())
416433
}
417434
}
418435

libs/simple/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ impl RawSimpleCollector {
615615
let roots: Vec<*mut dyn DynTrace> = contexts.iter()
616616
.flat_map(|ctx| {
617617
(**ctx).assume_valid_shadow_stack()
618-
.elements.iter().cloned()
618+
.reverse_iter()
619619
})
620620
.chain(std::iter::once(&self.handle_list
621621
as *const GcHandleList as *const dyn DynTrace

0 commit comments

Comments
 (0)