fix deadlock
This commit is contained in:
parent
1ac7b619cf
commit
74966678bd
7 changed files with 30 additions and 40 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -499,16 +499,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dashmap"
|
|
||||||
version = "4.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"num_cpus",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.13"
|
version = "0.99.13"
|
||||||
|
@ -984,7 +974,6 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"dashmap",
|
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"futures",
|
"futures",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
@ -17,7 +17,6 @@ bytes = "1"
|
||||||
chrono = { version = "0.4", features = [ "serde" ] }
|
chrono = { version = "0.4", features = [ "serde" ] }
|
||||||
clap = { version = "3.0.0-beta.2", features = [ "wrap_help" ] }
|
clap = { version = "3.0.0-beta.2", features = [ "wrap_help" ] }
|
||||||
ctrlc = "3"
|
ctrlc = "3"
|
||||||
dashmap = "4"
|
|
||||||
dotenv = "0.15"
|
dotenv = "0.15"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
|
|
3
src/cache/fs.rs
vendored
3
src/cache/fs.rs
vendored
|
@ -132,7 +132,7 @@ impl ConcurrentFsStream {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
file: Box::pin(File::open(path).await?),
|
file: Box::pin(File::open(path).await?),
|
||||||
// 0.5ms
|
// 0.5ms
|
||||||
sleep: Box::pin(tokio::time::interval(Duration::from_micros(500))),
|
sleep: Box::pin(tokio::time::interval(Duration::from_micros(250))),
|
||||||
is_file_done_writing: is_done,
|
is_file_done_writing: is_done,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,6 @@ impl Stream for ConcurrentFsStream {
|
||||||
bytes.truncate(filled);
|
bytes.truncate(filled);
|
||||||
polled_result.map(|_| {
|
polled_result.map(|_| {
|
||||||
if bytes.is_empty() {
|
if bytes.is_empty() {
|
||||||
dbg!(line!());
|
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(Ok(bytes.into()))
|
Some(Ok(bytes.into()))
|
||||||
|
|
3
src/cache/mod.rs
vendored
3
src/cache/mod.rs
vendored
|
@ -199,7 +199,8 @@ impl Stream for MemStream {
|
||||||
type Item = CacheStreamItem;
|
type Item = CacheStreamItem;
|
||||||
|
|
||||||
fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
let new_bytes = self.0.split_to(1460);
|
let mut new_bytes = Bytes::new();
|
||||||
|
std::mem::swap(&mut self.0, &mut new_bytes);
|
||||||
if new_bytes.is_empty() {
|
if new_bytes.is_empty() {
|
||||||
Poll::Ready(None)
|
Poll::Ready(None)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,12 +16,13 @@ use cache::{Cache, GenerationalCache, LowMemCache};
|
||||||
use clap::Clap;
|
use clap::Clap;
|
||||||
use config::CliArgs;
|
use config::CliArgs;
|
||||||
use log::{debug, error, warn, LevelFilter};
|
use log::{debug, error, warn, LevelFilter};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::RwLock;
|
||||||
use rustls::{NoClientAuth, ServerConfig};
|
use rustls::{NoClientAuth, ServerConfig};
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
use state::{RwLockServerState, ServerState};
|
use state::{RwLockServerState, ServerState};
|
||||||
use stop::send_stop;
|
use stop::send_stop;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use tokio::sync::RwLock as TokioRwLock;
|
||||||
|
|
||||||
mod cache;
|
mod cache;
|
||||||
mod config;
|
mod config;
|
||||||
|
@ -128,7 +129,7 @@ async fn main() -> Result<(), std::io::Error> {
|
||||||
cache_path.clone(),
|
cache_path.clone(),
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
let cache = Arc::new(Mutex::new(cache));
|
let cache = Arc::new(TokioRwLock::new(cache));
|
||||||
let cache1 = Arc::clone(&cache);
|
let cache1 = Arc::clone(&cache);
|
||||||
|
|
||||||
// Spawn periodic cache trimming
|
// Spawn periodic cache trimming
|
||||||
|
@ -136,7 +137,7 @@ async fn main() -> Result<(), std::io::Error> {
|
||||||
let mut interval = time::interval(Duration::from_secs(3 * 60));
|
let mut interval = time::interval(Duration::from_secs(3 * 60));
|
||||||
loop {
|
loop {
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
cache.lock().prune().await;
|
cache.write().await.prune().await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
25
src/ping.rs
25
src/ping.rs
|
@ -1,19 +1,13 @@
|
||||||
|
use std::num::{NonZeroU16, NonZeroU64};
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
use std::{io::BufReader, sync::Arc};
|
use std::{io::BufReader, sync::Arc};
|
||||||
use std::{
|
|
||||||
num::{NonZeroU16, NonZeroU64},
|
|
||||||
sync::atomic::Ordering,
|
|
||||||
};
|
|
||||||
|
|
||||||
use log::{error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use rustls::{
|
use rustls::internal::pemfile::{certs, rsa_private_keys};
|
||||||
internal::pemfile::{certs, rsa_private_keys},
|
use rustls::sign::{RSASigningKey, SigningKey};
|
||||||
sign::RSASigningKey,
|
use rustls::Certificate;
|
||||||
};
|
use serde::de::{MapAccess, Visitor};
|
||||||
use rustls::{sign::SigningKey, Certificate};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde::{
|
|
||||||
de::{MapAccess, Visitor},
|
|
||||||
Deserialize, Serialize,
|
|
||||||
};
|
|
||||||
use sodiumoxide::crypto::box_::PrecomputedKey;
|
use sodiumoxide::crypto::box_::PrecomputedKey;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -152,6 +146,7 @@ pub async fn update_server_state(secret: &str, cli: &CliArgs, data: &mut Arc<RwL
|
||||||
match resp {
|
match resp {
|
||||||
Ok(resp) => match resp.json::<Response>().await {
|
Ok(resp) => match resp.json::<Response>().await {
|
||||||
Ok(resp) => {
|
Ok(resp) => {
|
||||||
|
debug!("got write guard for server state");
|
||||||
let mut write_guard = data.0.write();
|
let mut write_guard = data.0.write();
|
||||||
|
|
||||||
if !write_guard.url_overridden && write_guard.image_server != resp.image_server {
|
if !write_guard.url_overridden && write_guard.image_server != resp.image_server {
|
||||||
|
@ -201,6 +196,8 @@ pub async fn update_server_state(secret: &str, cli: &CliArgs, data: &mut Arc<RwL
|
||||||
if resp.url != write_guard.url {
|
if resp.url != write_guard.url {
|
||||||
info!("This client's URL has been updated to {}", resp.url);
|
info!("This client's URL has been updated to {}", resp.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("dropping write guard for server state");
|
||||||
}
|
}
|
||||||
Err(e) => warn!("Got malformed response: {}", e),
|
Err(e) => warn!("Got malformed response: {}", e),
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,10 +12,10 @@ use bytes::Bytes;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use futures::{Stream, TryStreamExt};
|
use futures::{Stream, TryStreamExt};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use parking_lot::Mutex;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sodiumoxide::crypto::box_::{open_precomputed, Nonce, PrecomputedKey, NONCEBYTES};
|
use sodiumoxide::crypto::box_::{open_precomputed, Nonce, PrecomputedKey, NONCEBYTES};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use crate::cache::{Cache, CacheKey, ImageMetadata, UpstreamError};
|
use crate::cache::{Cache, CacheKey, ImageMetadata, UpstreamError};
|
||||||
use crate::client_api_version;
|
use crate::client_api_version;
|
||||||
|
@ -51,7 +51,7 @@ impl Responder for ServerResponse {
|
||||||
#[get("/{token}/data/{chapter_hash}/{file_name}")]
|
#[get("/{token}/data/{chapter_hash}/{file_name}")]
|
||||||
async fn token_data(
|
async fn token_data(
|
||||||
state: Data<RwLockServerState>,
|
state: Data<RwLockServerState>,
|
||||||
cache: Data<Mutex<Box<dyn Cache>>>,
|
cache: Data<RwLock<Box<dyn Cache>>>,
|
||||||
path: Path<(String, String, String)>,
|
path: Path<(String, String, String)>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let (token, chapter_hash, file_name) = path.into_inner();
|
let (token, chapter_hash, file_name) = path.into_inner();
|
||||||
|
@ -67,7 +67,7 @@ async fn token_data(
|
||||||
#[get("/{token}/data-saver/{chapter_hash}/{file_name}")]
|
#[get("/{token}/data-saver/{chapter_hash}/{file_name}")]
|
||||||
async fn token_data_saver(
|
async fn token_data_saver(
|
||||||
state: Data<RwLockServerState>,
|
state: Data<RwLockServerState>,
|
||||||
cache: Data<Mutex<Box<dyn Cache>>>,
|
cache: Data<RwLock<Box<dyn Cache>>>,
|
||||||
path: Path<(String, String, String)>,
|
path: Path<(String, String, String)>,
|
||||||
) -> impl Responder {
|
) -> impl Responder {
|
||||||
let (token, chapter_hash, file_name) = path.into_inner();
|
let (token, chapter_hash, file_name) = path.into_inner();
|
||||||
|
@ -183,16 +183,20 @@ fn push_headers(builder: &mut HttpResponseBuilder) -> &mut HttpResponseBuilder {
|
||||||
|
|
||||||
async fn fetch_image(
|
async fn fetch_image(
|
||||||
state: Data<RwLockServerState>,
|
state: Data<RwLockServerState>,
|
||||||
cache: Data<Mutex<Box<dyn Cache>>>,
|
cache: Data<RwLock<Box<dyn Cache>>>,
|
||||||
chapter_hash: String,
|
chapter_hash: String,
|
||||||
file_name: String,
|
file_name: String,
|
||||||
is_data_saver: bool,
|
is_data_saver: bool,
|
||||||
) -> ServerResponse {
|
) -> ServerResponse {
|
||||||
let key = CacheKey(chapter_hash, file_name, is_data_saver);
|
let key = CacheKey(chapter_hash, file_name, is_data_saver);
|
||||||
|
|
||||||
match cache.lock().get(&key).await {
|
match cache.write().await.get(&key).await {
|
||||||
Some(Ok((image, metadata))) => return construct_response(image, metadata),
|
Some(Ok((image, metadata))) => {
|
||||||
Some(Err(_)) => return ServerResponse::HttpResponse(HttpResponse::BadGateway().finish()),
|
return construct_response(image, metadata);
|
||||||
|
}
|
||||||
|
Some(Err(_)) => {
|
||||||
|
return ServerResponse::HttpResponse(HttpResponse::BadGateway().finish());
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +260,7 @@ async fn fetch_image(
|
||||||
|
|
||||||
let metadata = ImageMetadata::new(content_type, length, last_mod).unwrap();
|
let metadata = ImageMetadata::new(content_type, length, last_mod).unwrap();
|
||||||
let (stream, metadata) = {
|
let (stream, metadata) = {
|
||||||
match cache.lock().put(key, Box::new(body), metadata).await {
|
match cache.write().await.put(key, Box::new(body), metadata).await {
|
||||||
Ok((stream, metadata)) => (stream, *metadata),
|
Ok((stream, metadata)) => (stream, *metadata),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Failed to insert into cache: {}", e);
|
warn!("Failed to insert into cache: {}", e);
|
||||||
|
|
Loading…
Reference in a new issue