use std::{fmt, ops::Deref, sync::OnceLock};
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
use rspack_cacheable::{
Error,
rkyv::{
Archive, Deserialize, Place, Serialize,
rancor::Fallible,
rc::{ArchivedRc, Flavor},
},
};
use triomphe::{Arc, UniqueArc};
pub struct FreezeLock<T> {
frozen: OnceLock<Arc<T>>,
building: RwLock<Option<UniqueArc<T>>>,
}
impl<T> FreezeLock<T> {
pub fn new(value: T) -> Self {
Self {
frozen: OnceLock::new(),
building: RwLock::new(Some(UniqueArc::new(value))),
}
}
pub fn read(&self) -> FreezeReadGuard<'_, T> {
if let Some(value) = self.frozen.get() {
return FreezeReadGuard(FreezeRead::Frozen(value));
}
let building = self.building.read();
if let Some(value) = self.frozen.get() {
FreezeReadGuard(FreezeRead::Frozen(value))
} else {
FreezeReadGuard(FreezeRead::Building(RwLockReadGuard::map(
building,
|value| &**value.as_ref().expect("metadata must be initialized"),
)))
}
}
pub fn frozen(&self) -> Option<&T> {
self.frozen.get().map(|value| &**value)
}
pub fn get_mut(&mut self) -> &mut T {
self
.building
.get_mut()
.as_mut()
.expect("metadata is already frozen")
}
pub(crate) fn update(&self, update: impl FnOnce(&mut T)) {
let mut building = self.building.write();
update(building.as_mut().expect("metadata is already frozen"));
}
pub fn freeze(&self) -> &Arc<T> {
if let Some(value) = self.frozen.get() {
return value;
}
let mut building = self.building.write();
self.frozen.get_or_init(|| {
building
.take()
.expect("metadata must be initialized")
.shareable()
})
}
pub(crate) fn freeze_with(&self, value: Arc<T>) {
let mut building = self.building.write();
assert!(self.frozen.set(value).is_ok(), "metadata is already frozen");
building.take();
}
}
impl<T: Default> Default for FreezeLock<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> From<T> for FreezeLock<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T> From<Arc<T>> for FreezeLock<T> {
fn from(value: Arc<T>) -> Self {
Self {
frozen: OnceLock::from(value),
building: RwLock::new(None),
}
}
}
impl<T: fmt::Debug> fmt::Debug for FreezeLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.read().fmt(f)
}
}
pub struct FreezeReadGuard<'a, T: ?Sized>(FreezeRead<'a, T>);
enum FreezeRead<'a, T: ?Sized> {
Building(MappedRwLockReadGuard<'a, T>),
Frozen(&'a T),
}
impl<'a, T: ?Sized> FreezeReadGuard<'a, T> {
pub fn map<U: ?Sized>(self, map: impl FnOnce(&T) -> &U) -> FreezeReadGuard<'a, U> {
FreezeReadGuard(match self.0 {
FreezeRead::Building(value) => FreezeRead::Building(MappedRwLockReadGuard::map(value, map)),
FreezeRead::Frozen(value) => FreezeRead::Frozen(map(value)),
})
}
pub fn try_map<U: ?Sized>(
self,
map: impl FnOnce(&T) -> Option<&U>,
) -> Option<FreezeReadGuard<'a, U>> {
Some(FreezeReadGuard(match self.0 {
FreezeRead::Building(value) => {
FreezeRead::Building(MappedRwLockReadGuard::try_map(value, map).ok()?)
}
FreezeRead::Frozen(value) => FreezeRead::Frozen(map(value)?),
}))
}
}
impl<T: ?Sized> Deref for FreezeReadGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match &self.0 {
FreezeRead::Building(value) => value,
FreezeRead::Frozen(value) => value,
}
}
}
impl<T: fmt::Debug + ?Sized> fmt::Debug for FreezeReadGuard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.deref().fmt(f)
}
}
impl<T: Archive> Archive for FreezeLock<T> {
type Archived = <Arc<T> as Archive>::Archived;
type Resolver = <Arc<T> as Archive>::Resolver;
fn resolve(&self, resolver: Self::Resolver, out: Place<Self::Archived>) {
self
.frozen
.get()
.expect("metadata must be frozen before caching")
.resolve(resolver, out);
}
}
impl<T: Archive, S> Serialize<S> for FreezeLock<T>
where
Arc<T>: Serialize<S>,
S: Fallible<Error = Error> + ?Sized,
{
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
self
.frozen
.get()
.ok_or(Error::MessageError(
"metadata must be frozen before caching",
))?
.serialize(serializer)
}
}
impl<T: Archive, F: Flavor, D> Deserialize<FreezeLock<T>, D> for ArchivedRc<T::Archived, F>
where
Self: Deserialize<Arc<T>, D>,
D: Fallible + ?Sized,
{
fn deserialize(&self, deserializer: &mut D) -> Result<FreezeLock<T>, D::Error> {
Ok(<Self as Deserialize<Arc<T>, D>>::deserialize(self, deserializer)?.into())
}
}