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},
|
|
|
|
};
|
|
|
|
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 once_cell::sync::Lazy;
|
|
|
|
use std::{
|
|
|
|
future::Future,
|
|
|
|
path::PathBuf,
|
|
|
|
pin::Pin,
|
|
|
|
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
|
|
|
|
2022-03-28 20:34:36 +00:00
|
|
|
type ProcessMapKey = (Vec<u8>, PathBuf);
|
|
|
|
|
2023-07-22 15:44:43 +00:00
|
|
|
type ProcessMap = DashMap<ProcessMapKey, OutcomeReceiver>;
|
2021-10-21 00:28:40 +00:00
|
|
|
|
|
|
|
static PROCESS_MAP: Lazy<ProcessMap> = Lazy::new(DashMap::new);
|
|
|
|
|
|
|
|
struct CancelToken {
|
|
|
|
span: Span,
|
2022-03-28 20:34:36 +00:00
|
|
|
key: ProcessMapKey,
|
2023-07-22 15:44:43 +00:00
|
|
|
state: CancelState,
|
|
|
|
}
|
|
|
|
|
|
|
|
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 { .. })
|
|
|
|
}
|
2021-10-21 00:28:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pin_project_lite::pin_project! {
|
|
|
|
pub(super) struct CancelSafeProcessor<F> {
|
|
|
|
cancel_token: CancelToken,
|
|
|
|
|
|
|
|
#[pin]
|
|
|
|
fut: F,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<F> CancelSafeProcessor<F>
|
|
|
|
where
|
|
|
|
F: Future<Output = Result<(Details, web::Bytes), Error>>,
|
|
|
|
{
|
2022-04-02 22:40:04 +00:00
|
|
|
pub(super) fn new(hash: &[u8], path: PathBuf, fut: F) -> Self {
|
|
|
|
let key = (hash.to_vec(), path.clone());
|
2022-03-28 20:34:36 +00:00
|
|
|
|
2023-07-22 15:44:43 +00:00
|
|
|
let (sender, receiver) = flume::bounded(1);
|
|
|
|
|
2022-03-28 20:34:36 +00:00
|
|
|
let entry = 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);
|
2021-10-21 00:28:40 +00:00
|
|
|
let span = tracing::info_span!(
|
|
|
|
"Processing image",
|
2022-11-26 17:17:53 +00:00
|
|
|
hash = &tracing::field::debug(&hex::encode(hash)),
|
2021-10-21 00:28:40 +00:00
|
|
|
path = &tracing::field::debug(&path),
|
|
|
|
completed = &tracing::field::Empty,
|
|
|
|
);
|
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",
|
2022-11-26 17:17:53 +00:00
|
|
|
hash = &tracing::field::debug(&hex::encode(hash)),
|
2021-10-21 00:28:40 +00:00
|
|
|
path = &tracing::field::debug(&path),
|
|
|
|
);
|
2023-07-22 15:44:43 +00:00
|
|
|
(
|
|
|
|
CancelState::Receiver {
|
|
|
|
receiver: receiver.get().clone().into_recv_async(),
|
|
|
|
},
|
|
|
|
span,
|
|
|
|
)
|
2021-10-21 00:28:40 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-04-02 22:40:04 +00:00
|
|
|
CancelSafeProcessor {
|
2023-07-22 15:44:43 +00:00
|
|
|
cancel_token: CancelToken { span, key, state },
|
2021-10-21 00:28:40 +00:00
|
|
|
fut,
|
2022-04-02 22:40:04 +00:00
|
|
|
}
|
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 15:44:43 +00:00
|
|
|
let state = &mut this.cancel_token.state;
|
2022-03-28 20:34:36 +00:00
|
|
|
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 {
|
|
|
|
CancelState::Sender { sender } => fut.poll(cx).map(|res| {
|
|
|
|
PROCESS_MAP.remove(key);
|
|
|
|
|
|
|
|
if let Ok(tup) = &res {
|
|
|
|
let _ = sender.try_send(tup.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
res
|
|
|
|
}),
|
|
|
|
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() {
|
2022-03-28 20:34:36 +00:00
|
|
|
let completed = PROCESS_MAP.remove(&self.key).is_none();
|
2022-11-26 17:17:53 +00:00
|
|
|
self.span.record("completed", completed);
|
2021-10-21 00:28:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|