#[allow(unused)]
mod inner {
use crate as pagable;
#[derive(crate::Pagable, Eq, PartialEq, Debug)]
pub(super) struct Test {
xxx: String,
yyy: String,
#[pagable(flatten_serde)]
flatten_serde: Serde,
}
#[derive(crate::Pagable, Eq, PartialEq, Debug)]
enum TestEnum {
Unit,
Named { name1: String, name2: String },
Unnamed(String, String),
}
#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq, Debug)]
struct Serde(String);
#[derive(crate::Pagable, Eq, PartialEq, Debug)]
struct WithDiscard {
#[pagable(discard = "Default::default()")]
discard: String,
keep: String,
}
#[derive(crate::PagablePanic)]
pub(super) struct TestPanic<T>(T);
#[derive(crate::Pagable, Clone, Eq, PartialEq, Debug)]
pub(super) struct WithStaticStr {
name: crate::StaticStr,
value: i32,
}
trait HasItem {
type Item;
}
struct StringItem;
impl HasItem for StringItem {
type Item = String;
}
#[derive(crate::Pagable)]
#[pagable(bound = "T::Item: crate::Pagable")]
struct WithAssociatedBound<T: HasItem> {
item: T::Item,
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use dashmap::DashMap;
use dupe::Dupe;
use either::Either;
use super::*;
use crate::Pagable;
use crate::arc_erase::ArcEraseDyn;
use crate::pagable_arc::PagableArc;
use crate::pagable_arc::PinnedPagableArc;
use crate::storage::data::DataKey;
use crate::storage::data::PagableData;
use crate::storage::handle::PagableStorageHandle;
use crate::storage::in_memory::InMemoryPagableStorage;
use crate::storage::support::SerializerForPaging;
use crate::storage::traits::PagableStorage;
use crate::testing::EmptyPagableStorage;
use crate::testing::TestingDeserializer;
use crate::testing::TestingSerializer;
use crate::traits::PagableDeserialize;
use crate::traits::PagableSerialize;
#[test]
fn test_roundtrip() -> crate::Result<()> {
let t1 = Test {
xxx: "xxx".to_owned(),
yyy: "yyy".to_owned(),
flatten_serde: Serde("flatten".to_owned()),
};
let mut serializer = TestingSerializer::new();
t1.pagable_serialize(&mut serializer)?;
let bytes = serializer.finish();
let mut deserializer = TestingDeserializer::new(&bytes);
let t2 = Test::pagable_deserialize(&mut deserializer)?;
assert_eq!(t1, t2);
Ok(())
}
#[test]
fn test_discard() -> crate::Result<()> {
let t1 = WithDiscard {
discard: "discard".to_owned(),
keep: "keep".to_owned(),
};
let mut serializer = TestingSerializer::new();
t1.pagable_serialize(&mut serializer)?;
let bytes = serializer.finish();
let mut deserializer = TestingDeserializer::new(&bytes);
let t2 = WithDiscard::pagable_deserialize(&mut deserializer)?;
assert_eq!(t2.discard, "");
assert_eq!(t2.keep, "keep");
Ok(())
}
#[test]
fn test_static_str_in_struct() -> crate::Result<()> {
crate::static_str!(TEST_NAME = "test_static_str_value");
let original = WithStaticStr {
name: TEST_NAME,
value: 42,
};
let mut serializer = TestingSerializer::new();
original.pagable_serialize(&mut serializer)?;
let bytes = serializer.finish();
let mut deserializer = TestingDeserializer::new(&bytes);
let restored = WithStaticStr::pagable_deserialize(&mut deserializer)?;
assert_eq!(original, restored);
Ok(())
}
#[test]
fn test_associated_type_bound() -> crate::Result<()> {
let t1 = WithAssociatedBound::<StringItem> {
item: "associated".to_owned(),
};
let mut serializer = TestingSerializer::new();
t1.pagable_serialize(&mut serializer)?;
let bytes = serializer.finish();
let mut deserializer = TestingDeserializer::new(&bytes);
let t2 = WithAssociatedBound::<StringItem>::pagable_deserialize(&mut deserializer)?;
assert_eq!(t2.item, "associated");
Ok(())
}
#[test]
fn test_rt2() -> crate::Result<()> {
static_assertions::assert_impl_all!(std::sync::Arc<String>: PagableSerialize, PagableDeserialize<'static>);
static_assertions::assert_impl_all!(std::sync::Arc<std::sync::Arc<String>>: PagableSerialize, PagableDeserialize<'static>);
let t1: std::sync::Arc<std::sync::Arc<String>> =
std::sync::Arc::new(std::sync::Arc::new("string".to_owned()));
let mut serializer = TestingSerializer::new();
t1.pagable_serialize(&mut serializer)?;
let bytes = serializer.finish();
let mut deserializer = TestingDeserializer::new(&bytes);
let t2 =
<std::sync::Arc<std::sync::Arc<String>>>::pagable_deserialize(&mut deserializer)?;
assert_eq!(t1, t2);
Ok(())
}
#[test]
fn test_arc_identity_preserved() -> crate::Result<()> {
use std::sync::Arc;
let shared: Arc<String> = Arc::new("shared".to_owned());
let vec: Vec<Arc<String>> = vec![shared.clone(), shared.clone()];
assert!(Arc::ptr_eq(&vec[0], &vec[1]));
let mut serializer = TestingSerializer::new();
vec.pagable_serialize(&mut serializer)?;
let bytes = serializer.finish();
let mut deserializer = TestingDeserializer::new(&bytes);
let restored: Vec<Arc<String>> = Vec::pagable_deserialize(&mut deserializer)?;
assert_eq!(restored.len(), 2);
assert!(
Arc::ptr_eq(&restored[0], &restored[1]),
"Arc identity should be preserved across serialization"
);
assert_eq!(*restored[0], "shared");
Ok(())
}
#[tokio::test]
async fn test_pagable_arc_refcounts() -> anyhow::Result<()> {
let storage =
PagableStorageHandle::new(std::sync::Arc::new(EmptyPagableStorage::new()));
let arc1 = PinnedPagableArc::new("hello world".to_owned(), storage.clone() as _);
let weak1 = PinnedPagableArc::into_pagable(arc1.clone());
assert_eq!(weak1.pinned_count(), 2);
weak1.unpin();
assert_eq!(weak1.pinned_count(), 1);
drop(arc1);
assert_eq!(weak1.pinned_count(), 0);
assert!(!weak1.is_paged_out());
{
let arc2 = weak1.clone();
assert_eq!(weak1.pinned_count(), 0);
let pin1 = arc2.pin().await?;
assert_eq!(weak1.pinned_count(), 2);
drop(pin1);
assert_eq!(weak1.pinned_count(), 1);
drop(arc2);
assert_eq!(weak1.pinned_count(), 0);
}
let pinned = weak1.pin().await?;
assert_eq!(weak1.pinned_count(), 2);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pagable_arc_paging() -> anyhow::Result<()> {
let mut backing_storage = InMemoryPagableStorage::new();
let storage = PagableStorageHandle::new(backing_storage.handle());
let pagable = PagableArc::new("hello world".to_owned(), storage.clone());
pagable.unpin();
assert_eq!(backing_storage.pending_paging_count(), 1);
assert!(!pagable.is_paged_out());
backing_storage.page_out_pending();
assert!(pagable.is_paged_out());
assert!(pagable.get_data_key().is_some());
let key = pagable.get_data_key().unwrap();
let deserialized: String = storage.deserialize_pagable_data(&key).await.unwrap();
let arc2 = PinnedPagableArc::new(deserialized, storage.clone());
let arc1 = pagable.pin().await?;
assert!(pagable.is_pinned());
assert!(!pagable.is_paged_out());
assert_eq!(*arc1, "hello world");
assert_eq!(&arc1, &arc2);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pagable_arc_shared_arc() -> anyhow::Result<()> {
let mut backing_storage = InMemoryPagableStorage::new();
let storage = PagableStorageHandle::new(backing_storage.handle());
let inner_std_arc = std::sync::Arc::new("hello world".to_owned());
let pagable1 = PagableArc::new(inner_std_arc.dupe(), storage.clone());
let pagable2 = PagableArc::new(inner_std_arc.dupe(), storage.clone());
pagable1.unpin();
pagable2.unpin();
backing_storage.page_out_pending();
assert!(pagable1.is_paged_out());
assert!(pagable2.is_paged_out());
let strong1 = pagable1.pin().await?;
let strong2 = pagable2.pin().await?;
assert!(!PinnedPagableArc::ptr_eq(&strong1, &strong2));
assert!(std::sync::Arc::ptr_eq(&*strong1, &*strong2));
Ok(())
}
#[test]
#[should_panic]
fn test_panic_on_serialize() {
let t = TestPanic(());
let mut serializer = TestingSerializer::new();
t.pagable_serialize(&mut serializer).unwrap();
}
#[test]
#[should_panic]
fn test_panic_on_deserialize() {
let mut deserializer = TestingDeserializer::new(&[]);
let _ = TestPanic::<()>::pagable_deserialize(&mut deserializer).unwrap();
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_pagable_arc() -> anyhow::Result<()> {
let mut backing_storage = InMemoryPagableStorage::new();
let storage = PagableStorageHandle::new(backing_storage.handle());
#[derive(Pagable, Eq, PartialEq, Debug)]
struct UNodeData(usize);
#[derive(Pagable, Debug, Clone, Dupe)]
struct UNode(PagableArc<UNodeData>);
impl UNode {
fn new(x: usize, storage: PagableStorageHandle) -> Self {
Self(PagableArc::new(UNodeData(x), storage))
}
}
#[derive(Pagable, Debug, Clone, Dupe)]
struct EvalResult(PagableArc<Vec<UNode>>);
impl EvalResult {
fn new(x: Vec<UNode>, storage: PagableStorageHandle) -> Self {
Self(PagableArc::new(x, storage))
}
}
#[derive(Pagable, Debug)]
struct CfgNodeData {
node: PinnedPagableArc<UNodeData>,
deps: Vec<CfgNode>,
}
#[derive(Pagable, Debug, Clone, Dupe)]
struct CfgNode(PagableArc<CfgNodeData>);
impl CfgNode {
fn new(node: UNode, deps: Vec<CfgNode>, storage: PagableStorageHandle) -> Self {
Self(PagableArc::new(
CfgNodeData {
node: node.0.pin_sync().unwrap(),
deps,
},
storage,
))
}
}
let node1 = UNode::new(1, storage.dupe());
let node2 = UNode::new(2, storage.dupe());
let node3 = UNode::new(3, storage.dupe());
let weak_node1 = node1.0.dupe();
let weak_node2 = node2.0.dupe();
let weak_node3 = node3.0.dupe();
weak_node1.unpin();
weak_node2.unpin();
weak_node3.unpin();
let eval_result = EvalResult::new(
vec![node1.dupe(), node2.dupe(), node3.dupe()],
storage.dupe(),
);
let cfgnode1 = CfgNode::new(node1.dupe(), vec![], storage.dupe());
let cfgnode2 = CfgNode::new(node2.dupe(), vec![cfgnode1.dupe()], storage.dupe());
let cfgnode3 = CfgNode::new(
node3.dupe(),
vec![cfgnode1.dupe(), cfgnode2.dupe()],
storage.dupe(),
);
eprintln!("node1: {}", node1.0.identity());
eprintln!("node2: {}", node2.0.identity());
eprintln!("node3: {}", node3.0.identity());
eprintln!("eval_result: {}", eval_result.0.identity());
eprintln!("cfgnode1: {}", cfgnode1.0.identity());
eprintln!("cfgnode2: {}", cfgnode2.0.identity());
eprintln!("cfgnode3: {}", cfgnode3.0.identity());
drop(node1);
drop(node2);
drop(node3);
backing_storage.page_out_pending();
eval_result.0.unpin();
cfgnode1.0.unpin();
cfgnode2.0.unpin();
cfgnode3.0.unpin();
backing_storage.page_out_pending();
assert!(cfgnode1.0.get_data_key().is_some());
assert!(cfgnode2.0.get_data_key().is_some());
assert!(cfgnode3.0.get_data_key().is_some());
assert!(eval_result.0.get_data_key().is_some());
assert!(weak_node1.get_data_key().is_some());
assert!(weak_node2.get_data_key().is_some());
assert!(weak_node3.get_data_key().is_some());
assert!(cfgnode1.0.is_paged_out());
assert!(cfgnode2.0.is_paged_out());
assert!(cfgnode3.0.is_paged_out());
assert!(eval_result.0.is_paged_out());
assert!(weak_node1.is_paged_out());
assert!(weak_node2.is_paged_out());
assert!(weak_node3.is_paged_out());
eprintln!("upgrading cfgnode1");
assert!(!cfgnode1.0.is_pinned());
let strong_cfgnode1 = cfgnode1.0.pin().await?;
assert!(cfgnode1.0.is_pinned());
eprintln!("upgrading eval_result");
let strong_eval_result = eval_result.0.pin().await?;
assert!(!eval_result.0.is_paged_out());
assert_eq!((*strong_eval_result).len(), 3);
assert!(!strong_eval_result[0].0.is_paged_out());
assert!(strong_eval_result[0].0.is_pinned());
let strong_node2 = strong_eval_result[1].0.pin().await?;
assert!(strong_eval_result[1].0.is_pinned());
let strong_cfgnode3 = cfgnode3.0.pin().await?;
assert!(cfgnode3.0.is_pinned());
Ok(())
}
}
}
mod asserts {
use crate::traits::Pagable;
use crate::traits::PagableDeserializeOwned;
use crate::traits::PagableSerialize;
static_assertions::assert_impl_all!(super::inner::Test: PagableSerialize, PagableDeserializeOwned);
static_assertions::assert_impl_all!(super::inner::Test: Pagable);
struct NotPagable;
static_assertions::assert_impl_all!(super::inner::TestPanic<()>: PagableSerialize, PagableDeserializeOwned);
static_assertions::assert_impl_all!(super::inner::TestPanic<NotPagable>: PagableSerialize, PagableDeserializeOwned);
}