fix deadlock

This commit is contained in:
Edward Shen 2021-04-19 00:16:13 -04:00
parent 1ac7b619cf
commit 74966678bd
Signed by: edward
GPG key ID: 19182661E818369F
7 changed files with 30 additions and 40 deletions

11
Cargo.lock generated
View file

@ -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",

View file

@ -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
View file

@ -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
View file

@ -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 {

View file

@ -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;
} }
}); });

View file

@ -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),
}, },

View file

@ -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);