use std::ops::Deref;
use crate::{ANNResult, error::ToRanked, graph::AdjacencyList, utils::VectorId};
pub trait ExecutionContext: Send + Sync + Clone + 'static {
fn wrap_spawn<F, T>(&self, f: F) -> impl std::future::Future<Output = T> + Send + 'static
where
F: std::future::Future<Output = T> + Send + 'static,
{
f
}
}
pub trait DataProvider: Sized + Send + Sync + 'static {
type Context: ExecutionContext;
type InternalId: VectorId;
type ExternalId: PartialEq + Send + Sync + 'static;
type Error: ToRanked + std::fmt::Debug + Send + Sync + 'static;
type Guard: Guard<Id = Self::InternalId> + 'static;
fn to_internal_id(
&self,
context: &Self::Context,
gid: &Self::ExternalId,
) -> Result<Self::InternalId, Self::Error>;
fn to_external_id(
&self,
context: &Self::Context,
id: Self::InternalId,
) -> Result<Self::ExternalId, Self::Error>;
}
pub trait Delete: DataProvider {
fn delete(
&self,
context: &Self::Context,
gid: &Self::ExternalId,
) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
fn release(
&self,
context: &Self::Context,
id: Self::InternalId,
) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send;
fn status_by_internal_id(
&self,
context: &Self::Context,
id: Self::InternalId,
) -> impl std::future::Future<Output = Result<ElementStatus, Self::Error>> + Send;
fn status_by_external_id(
&self,
context: &Self::Context,
gid: &Self::ExternalId,
) -> impl std::future::Future<Output = Result<ElementStatus, Self::Error>> + Send;
fn statuses_unordered<Itr, F>(
&self,
context: &Self::Context,
itr: Itr,
mut f: F,
) -> impl std::future::Future<Output = Result<(), Self::Error>> + Send
where
Itr: Iterator<Item = Self::InternalId> + Send,
F: FnMut(Result<ElementStatus, Self::Error>, Self::InternalId) + Send,
{
async move {
for i in itr {
f(self.status_by_internal_id(context, i).await, i);
}
Ok(())
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ElementStatus {
Valid,
Deleted,
}
impl ElementStatus {
pub fn is_valid(self) -> bool {
self == Self::Valid
}
pub fn is_deleted(self) -> bool {
self == Self::Deleted
}
}
pub trait HasId {
type Id: VectorId;
}
impl<T> HasId for &T
where
T: HasId,
{
type Id = T::Id;
}
impl<T> HasId for &mut T
where
T: HasId,
{
type Id = T::Id;
}
pub trait SetElement<T>: DataProvider {
type SetError: ToRanked + std::fmt::Debug + Send + Sync + 'static;
fn set_element(
&self,
context: &Self::Context,
id: &Self::ExternalId,
element: T,
) -> impl std::future::Future<Output = Result<Self::Guard, Self::SetError>> + Send;
}
pub trait Guard: Send + Sync + 'static {
type Id;
fn complete(self) -> impl std::future::Future<Output = ()> + Send;
fn id(&self) -> Self::Id;
}
#[derive(Debug, Default)]
pub struct NoopGuard<I>(I);
impl<I> NoopGuard<I> {
pub fn new(id: I) -> Self {
Self(id)
}
}
impl<I> Guard for NoopGuard<I>
where
I: Send + Sync + Copy + 'static,
{
type Id = I;
async fn complete(self) {}
fn id(&self) -> Self::Id {
self.0
}
}
pub trait NeighborAccessor: HasId + Send + Sync {
fn get_neighbors(
&mut self,
id: Self::Id,
neighbors: &mut AdjacencyList<Self::Id>,
) -> impl std::future::Future<Output = ANNResult<()>> + Send;
}
pub trait NeighborAccessorMut: NeighborAccessor {
fn set_neighbors(
&mut self,
id: Self::Id,
neighbors: &[Self::Id],
) -> impl std::future::Future<Output = ANNResult<()>> + Send;
fn append_vector(
&mut self,
id: Self::Id,
neighbors: &[Self::Id],
) -> impl std::future::Future<Output = ANNResult<()>> + Send;
fn set_neighbors_bulk<I, T>(
&mut self,
iter: I,
) -> impl std::future::Future<Output = ANNResult<()>> + Send
where
I: Iterator<Item = (Self::Id, T)> + Send,
T: Deref<Target = [Self::Id]> + Send,
{
async move {
for (vector_id, neighbors) in iter {
self.set_neighbors(vector_id, neighbors.deref()).await?;
}
Ok(())
}
}
}
pub struct Neighbors<'a, T>(pub &'a mut T);
impl<T> HasId for Neighbors<'_, T>
where
T: HasId,
{
type Id = T::Id;
}
impl<T> NeighborAccessor for Neighbors<'_, T>
where
T: NeighborAccessor,
{
fn get_neighbors(
&mut self,
id: Self::Id,
neighbors: &mut AdjacencyList<Self::Id>,
) -> impl std::future::Future<Output = ANNResult<()>> + Send {
self.0.get_neighbors(id, neighbors)
}
}
impl<T> NeighborAccessorMut for Neighbors<'_, T>
where
T: NeighborAccessorMut,
{
fn set_neighbors(
&mut self,
id: Self::Id,
neighbors: &[Self::Id],
) -> impl std::future::Future<Output = ANNResult<()>> + Send {
self.0.set_neighbors(id, neighbors)
}
fn append_vector(
&mut self,
id: Self::Id,
neighbors: &[Self::Id],
) -> impl std::future::Future<Output = ANNResult<()>> + Send {
self.0.append_vector(id, neighbors)
}
fn set_neighbors_bulk<I, U>(
&mut self,
iter: I,
) -> impl std::future::Future<Output = ANNResult<()>> + Send
where
I: Iterator<Item = (Self::Id, U)> + Send,
U: Deref<Target = [Self::Id]> + Send,
{
self.0.set_neighbors_bulk(iter)
}
}
pub trait DefaultAccessor: DataProvider {
type Accessor<'a>: HasId<Id = Self::InternalId>
where
Self: 'a;
fn default_accessor(&self) -> Self::Accessor<'_>;
}
#[derive(Default, Clone)]
pub struct DefaultContext;
impl std::fmt::Display for DefaultContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "default context")
}
}
impl ExecutionContext for DefaultContext {}
#[cfg(test)]
mod tests {
use std::{
future::Future,
pin::Pin,
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
task,
};
use pin_project::{pin_project, pinned_drop};
use super::*;
#[test]
fn test_default_context() {
let ctx = DefaultContext;
assert_eq!(ctx.to_string(), "default context");
assert_eq!(
std::mem::size_of::<DefaultContext>(),
0,
"expected DefaultContext to be an empty class"
);
}
#[derive(Debug)]
struct TestContextInner {
spawned: AtomicUsize,
dropped: AtomicUsize,
}
#[derive(Debug, Clone)]
struct TestContext {
inner: Arc<TestContextInner>,
}
impl Default for TestContext {
fn default() -> Self {
Self {
inner: Arc::new(TestContextInner {
spawned: AtomicUsize::new(0),
dropped: AtomicUsize::new(0),
}),
}
}
}
impl std::fmt::Display for TestContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "test context")
}
}
#[pin_project(PinnedDrop)]
pub struct SpawnCounter<F> {
#[pin]
inner: F,
parent: TestContext,
}
#[pinned_drop]
impl<F> PinnedDrop for SpawnCounter<F> {
fn drop(self: Pin<&mut Self>) {
self.parent.inner.dropped.fetch_add(1, Ordering::AcqRel);
}
}
impl<F> Future for SpawnCounter<F>
where
F: Future,
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
self.project().inner.poll(cx)
}
}
impl ExecutionContext for TestContext {
fn wrap_spawn<F, T>(&self, f: F) -> impl Future<Output = T> + Send + 'static
where
F: Future<Output = T> + Send + 'static,
{
self.inner.spawned.fetch_add(1, Ordering::AcqRel);
SpawnCounter {
inner: f,
parent: self.clone(),
}
}
}
#[allow(clippy::manual_async_fn)]
fn test_spawning<Context>(
context: Context,
width: usize,
depth: usize,
) -> impl Future<Output = ()> + Send + 'static
where
Context: ExecutionContext + 'static + std::fmt::Debug,
{
async move {
if depth == 0 {
return;
}
let handles: Box<[_]> = (0..width)
.map(|_| {
let clone = context.clone();
tokio::spawn(context.wrap_spawn(test_spawning(clone, width, depth - 1)))
})
.collect();
for h in handles {
h.await.unwrap();
}
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn test_task_spawning() {
let context = TestContext::default();
assert_eq!(context.inner.spawned.load(Ordering::Acquire), 0);
assert_eq!(context.inner.dropped.load(Ordering::Acquire), 0);
let width = 10;
let depth = 3;
test_spawning(context.clone(), width, depth).await;
let expected = (width.pow((depth + 1).try_into().unwrap()) - 1) / (width - 1) - 1;
assert_eq!(context.inner.spawned.load(Ordering::Acquire), expected);
assert_eq!(context.inner.dropped.load(Ordering::Acquire), expected);
}
#[tokio::test]
async fn test_noop_guard() {
{
let guard = NoopGuard::<usize>::new(10);
assert_eq!(guard.id(), 10);
guard.complete().await;
}
{
let guard = NoopGuard::<usize>::new(5);
assert_eq!(guard.id(), 5);
}
}
#[test]
fn simple_status_test() {
let valid = ElementStatus::Valid;
assert!(valid.is_valid());
assert!(!valid.is_deleted());
let deleted = ElementStatus::Deleted;
assert!(!deleted.is_valid());
assert!(deleted.is_deleted());
}
}