mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 06:28:11 +08:00
Forward serialize through node wrappers and free abandoned frame table reservations
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
use crate::gpoll::Finality;
|
use crate::gpoll::Finality;
|
||||||
use std::cell::UnsafeCell;
|
use std::cell::UnsafeCell;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::{ManuallyDrop, MaybeUninit};
|
||||||
use std::sync::atomic::{AtomicU8, AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU8, AtomicU64, Ordering};
|
||||||
|
|
||||||
const SLOT_EMPTY: u8 = 0;
|
const SLOT_EMPTY: u8 = 0;
|
||||||
@@ -81,18 +81,27 @@ impl<T, const CAP: usize> Drop for FrameTable<T, CAP> {
|
|||||||
|
|
||||||
impl<'t, T> VacantSlot<'t, T> {
|
impl<'t, T> VacantSlot<'t, T> {
|
||||||
pub fn publish(self, value: T, finality: Finality) -> &'t T {
|
pub fn publish(self, value: T, finality: Finality) -> &'t T {
|
||||||
|
let slot = ManuallyDrop::new(self).slot;
|
||||||
// SAFETY: the CAS in `lookup` reserved this slot exclusively for us and
|
// SAFETY: the CAS in `lookup` reserved this slot exclusively for us and
|
||||||
// its state is still SLOT_EMPTY, so nobody reads the value yet.
|
// its state is still SLOT_EMPTY, so nobody reads the value yet.
|
||||||
let lent = unsafe { &*(*self.slot.value.get()).write(value) };
|
let lent = unsafe { &*(*slot.value.get()).write(value) };
|
||||||
let state = match finality {
|
let state = match finality {
|
||||||
Finality::AllFinal => SLOT_FINAL,
|
Finality::AllFinal => SLOT_FINAL,
|
||||||
Finality::Partial => SLOT_PARTIAL,
|
Finality::Partial => SLOT_PARTIAL,
|
||||||
};
|
};
|
||||||
self.slot.state.store(state, Ordering::Release);
|
slot.state.store(state, Ordering::Release);
|
||||||
lent
|
lent
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release(self) {
|
pub fn release(self) {
|
||||||
|
drop(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees the reservation, so an early return or panic between `lookup` and
|
||||||
|
/// `publish` cannot retire the slot for the rest of the table's life.
|
||||||
|
impl<T> Drop for VacantSlot<'_, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
self.slot.key.store(0, Ordering::Release);
|
self.slot.key.store(0, Ordering::Release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,6 +132,14 @@ mod tests {
|
|||||||
assert!(matches!(table.lookup(7), Lookup::Vacant(_)));
|
assert!(matches!(table.lookup(7), Lookup::Vacant(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_dropped_reservation_frees_the_slot() {
|
||||||
|
let table = FrameTable::<u32, 8>::new();
|
||||||
|
let Lookup::Vacant(slot) = table.lookup(7) else { unreachable!() };
|
||||||
|
drop(slot);
|
||||||
|
assert!(matches!(table.lookup(7), Lookup::Vacant(_)), "an abandoned reservation must not retire the slot");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn neighboring_hashes_do_not_share_an_entry() {
|
fn neighboring_hashes_do_not_share_an_entry() {
|
||||||
let table = FrameTable::<u32, 8>::new();
|
let table = FrameTable::<u32, 8>::new();
|
||||||
|
|||||||
@@ -94,6 +94,10 @@ where
|
|||||||
(**self).extent(input)
|
(**self).extent(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
|
(**self).serialize()
|
||||||
|
}
|
||||||
|
|
||||||
fn eval_batch<'a>(&self, input: &'a Input, range: Range<u64>, scratch: Option<&'a mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'a, Self::Output>
|
fn eval_batch<'a>(&self, input: &'a Input, range: Range<u64>, scratch: Option<&'a mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'a, Self::Output>
|
||||||
where
|
where
|
||||||
Input: InjectIndex + Copy,
|
Input: InjectIndex + Copy,
|
||||||
@@ -116,6 +120,10 @@ where
|
|||||||
(**self).extent(input)
|
(**self).extent(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
|
(**self).serialize()
|
||||||
|
}
|
||||||
|
|
||||||
fn eval_batch<'a>(&self, input: &'a Input, range: Range<u64>, scratch: Option<&'a mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'a, Self::Output>
|
fn eval_batch<'a>(&self, input: &'a Input, range: Range<u64>, scratch: Option<&'a mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'a, Self::Output>
|
||||||
where
|
where
|
||||||
Input: InjectIndex + Copy,
|
Input: InjectIndex + Copy,
|
||||||
@@ -138,6 +146,10 @@ where
|
|||||||
(**self).extent(input)
|
(**self).extent(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
|
(**self).serialize()
|
||||||
|
}
|
||||||
|
|
||||||
fn eval_batch<'a>(&self, input: &'a Input, range: Range<u64>, scratch: Option<&'a mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'a, Self::Output>
|
fn eval_batch<'a>(&self, input: &'a Input, range: Range<u64>, scratch: Option<&'a mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'a, Self::Output>
|
||||||
where
|
where
|
||||||
Input: InjectIndex + Copy,
|
Input: InjectIndex + Copy,
|
||||||
@@ -239,28 +251,6 @@ impl<'a, N> LazyInput<'a, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Input, N> Node<Input> for LazyInput<'a, N>
|
|
||||||
where
|
|
||||||
N: Node<Input>,
|
|
||||||
{
|
|
||||||
type Output = N::Output;
|
|
||||||
|
|
||||||
fn eval(&self, input: &Input) -> GPoll<Self::Output> {
|
|
||||||
self.node.eval(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extent(&self, input: &Input) -> GPoll<Extent> {
|
|
||||||
self.node.extent(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval_batch<'b>(&self, input: &'b Input, range: Range<u64>, scratch: Option<&'b mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'b, Self::Output>
|
|
||||||
where
|
|
||||||
Input: InjectIndex + Copy,
|
|
||||||
{
|
|
||||||
self.node.eval_batch(input, range, scratch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user