2
0
Fork 0
mirror of https://git.asonix.dog/asonix/pict-rs synced 2024-12-22 19:31:35 +00:00
pict-rs/src/concurrent_processor.rs

173 lines
4.3 KiB
Rust
Raw Normal View History

2021-10-21 00:28:40 +00:00
use crate::{
2022-03-24 22:09:15 +00:00
details::Details,
2021-10-21 00:28:40 +00:00
error::{Error, UploadError},
2023-08-14 19:25:19 +00:00
repo::Hash,
2021-10-21 00:28:40 +00:00
};
use actix_web::web;
use dashmap::{mapref::entry::Entry, DashMap};
2023-07-22 15:44:43 +00:00
use flume::{r#async::RecvFut, Receiver, Sender};
2021-10-21 00:28:40 +00:00
use std::{
future::Future,
path::PathBuf,
pin::Pin,
2023-07-22 16:15:30 +00:00
sync::Arc,
2021-10-21 00:28:40 +00:00
task::{Context, Poll},
};
use tracing::Span;
2023-07-22 15:44:43 +00:00
type OutcomeReceiver = Receiver<(Details, web::Bytes)>;
2021-10-21 00:28:40 +00:00
2023-08-14 19:25:19 +00:00
type ProcessMapKey = (Hash, PathBuf);
2023-07-22 16:15:30 +00:00
type ProcessMapInner = DashMap<ProcessMapKey, OutcomeReceiver>;
2021-10-21 00:28:40 +00:00
2023-07-22 16:15:30 +00:00
#[derive(Debug, Default, Clone)]
pub(crate) struct ProcessMap {
process_map: Arc<ProcessMapInner>,
2023-07-22 15:44:43 +00:00
}
2023-07-22 16:15:30 +00:00
impl ProcessMap {
pub(super) fn new() -> Self {
Self::default()
2023-07-22 15:44:43 +00:00
}
2021-10-21 00:28:40 +00:00
2023-07-22 16:15:30 +00:00
pub(super) async fn process<Fut>(
&self,
2023-08-14 19:25:19 +00:00
hash: Hash,
2023-07-22 16:15:30 +00:00
path: PathBuf,
fut: Fut,
) -> Result<(Details, web::Bytes), Error>
where
Fut: Future<Output = Result<(Details, web::Bytes), Error>>,
{
2023-08-14 19:25:19 +00:00
let key = (hash.clone(), path.clone());
2023-07-22 15:44:43 +00:00
let (sender, receiver) = flume::bounded(1);
2023-07-22 16:15:30 +00:00
let entry = self.process_map.entry(key.clone());
2021-10-21 00:28:40 +00:00
2023-07-22 15:44:43 +00:00
let (state, span) = match entry {
2021-10-21 00:28:40 +00:00
Entry::Vacant(vacant) => {
2023-07-22 15:44:43 +00:00
vacant.insert(receiver);
2023-07-22 16:15:30 +00:00
2021-10-21 00:28:40 +00:00
let span = tracing::info_span!(
"Processing image",
2023-08-14 19:25:19 +00:00
hash = ?hash,
path = ?path,
2021-10-21 00:28:40 +00:00
completed = &tracing::field::Empty,
);
2023-07-22 16:15:30 +00:00
metrics::counter!("pict-rs.process-map.inserted").increment(1);
2023-07-22 21:47:59 +00:00
2023-07-22 15:44:43 +00:00
(CancelState::Sender { sender }, span)
2021-10-21 00:28:40 +00:00
}
2023-07-22 15:44:43 +00:00
Entry::Occupied(receiver) => {
2021-10-21 00:28:40 +00:00
let span = tracing::info_span!(
"Waiting for processed image",
2023-08-14 19:25:19 +00:00
hash = ?hash,
path = ?path,
2021-10-21 00:28:40 +00:00
);
2023-07-22 16:15:30 +00:00
let receiver = receiver.get().clone().into_recv_async();
(CancelState::Receiver { receiver }, span)
2021-10-21 00:28:40 +00:00
}
};
CancelSafeProcessor {
2023-07-22 16:15:30 +00:00
cancel_token: CancelToken {
span,
key,
state,
process_map: self.clone(),
},
2021-10-21 00:28:40 +00:00
fut,
}
2023-07-22 16:15:30 +00:00
.await
}
fn remove(&self, key: &ProcessMapKey) -> Option<OutcomeReceiver> {
self.process_map.remove(key).map(|(_, v)| v)
}
}
struct CancelToken {
span: Span,
key: ProcessMapKey,
state: CancelState,
process_map: ProcessMap,
}
enum CancelState {
Sender {
sender: Sender<(Details, web::Bytes)>,
},
Receiver {
receiver: RecvFut<'static, (Details, web::Bytes)>,
},
}
impl CancelState {
const fn is_sender(&self) -> bool {
matches!(self, Self::Sender { .. })
}
}
pin_project_lite::pin_project! {
struct CancelSafeProcessor<F> {
cancel_token: CancelToken,
#[pin]
fut: F,
2021-10-21 00:28:40 +00:00
}
}
impl<F> Future for CancelSafeProcessor<F>
where
F: Future<Output = Result<(Details, web::Bytes), Error>>,
{
type Output = Result<(Details, web::Bytes), Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.as_mut().project();
let span = &this.cancel_token.span;
2023-07-22 16:15:30 +00:00
let process_map = &this.cancel_token.process_map;
2023-07-22 15:44:43 +00:00
let state = &mut this.cancel_token.state;
let key = &this.cancel_token.key;
2021-10-21 00:28:40 +00:00
let fut = this.fut;
2023-07-22 15:44:43 +00:00
span.in_scope(|| match state {
2023-07-22 16:15:30 +00:00
CancelState::Sender { sender } => {
let res = std::task::ready!(fut.poll(cx));
2023-07-22 21:47:59 +00:00
if process_map.remove(key).is_some() {
metrics::counter!("pict-rs.process-map.removed").increment(1);
2023-07-22 21:47:59 +00:00
}
2023-07-22 15:44:43 +00:00
if let Ok(tup) = &res {
let _ = sender.try_send(tup.clone());
}
2023-07-22 16:15:30 +00:00
Poll::Ready(res)
}
2023-07-22 15:44:43 +00:00
CancelState::Receiver { ref mut receiver } => Pin::new(receiver)
.poll(cx)
.map(|res| res.map_err(|_| UploadError::Canceled.into())),
2021-10-21 00:28:40 +00:00
})
}
}
impl Drop for CancelToken {
fn drop(&mut self) {
2023-07-22 15:44:43 +00:00
if self.state.is_sender() {
2023-07-22 16:15:30 +00:00
let completed = self.process_map.remove(&self.key).is_none();
2022-11-26 17:17:53 +00:00
self.span.record("completed", completed);
2023-07-22 21:47:59 +00:00
if !completed {
metrics::counter!("pict-rs.process-map.removed").increment(1);
2023-07-22 21:47:59 +00:00
}
2021-10-21 00:28:40 +00:00
}
}
}