diff options
Diffstat (limited to 'base/src')
-rw-r--r-- | base/src/lib.rs | 48 |
1 files changed, 45 insertions, 3 deletions
diff --git a/base/src/lib.rs b/base/src/lib.rs index 46ab576..b97f924 100644 --- a/base/src/lib.rs +++ b/base/src/lib.rs @@ -6,7 +6,8 @@ #![feature(lazy_cell)] use base64::Engine; use jellycommon::{config::GlobalConfig, AssetLocation}; -use std::{fs::File, path::PathBuf, sync::LazyLock}; +use std::{fs::File, future::Future, path::PathBuf, sync::LazyLock}; +use tokio::sync::Mutex; pub static CONF: LazyLock<GlobalConfig> = LazyLock::new(|| { serde_json::from_reader( @@ -20,7 +21,7 @@ pub static CONF: LazyLock<GlobalConfig> = LazyLock::new(|| { .unwrap() }); -pub fn cache_file(seed: &[&str]) -> AssetLocation { +pub fn cache_location(seed: &[&str]) -> (usize, AssetLocation) { use sha2::Digest; let mut d = sha2::Sha512::new(); for s in seed { @@ -28,9 +29,50 @@ pub fn cache_file(seed: &[&str]) -> AssetLocation { d.update(b"\0"); } let d = d.finalize(); + let n = d[0] as usize | (d[1] as usize) << 8 | (d[2] as usize) << 16 | (d[3] as usize) << 24; let fname = base64::engine::general_purpose::URL_SAFE.encode(d); let fname = &fname[..22]; // about 128 bits - AssetLocation::Cache(fname.into()) + (n, AssetLocation::Cache(fname.into())) +} + +const CACHE_GENERATION_BUCKET_COUNT: usize = 1024; +pub static CACHE_GENERATION_LOCKS: LazyLock<[Mutex<()>; CACHE_GENERATION_BUCKET_COUNT]> = + LazyLock::new(|| [(); CACHE_GENERATION_BUCKET_COUNT].map(|_| Mutex::new(()))); + +pub async fn async_cache_file<Fun, Fut>( + seed: &[&str], + generate: Fun, +) -> Result<AssetLocation, anyhow::Error> +where + Fun: FnOnce(tokio::fs::File) -> Fut, + Fut: Future<Output = Result<(), anyhow::Error>>, +{ + let (bucket, location) = cache_location(seed); + // we need a lock even if it exists since somebody might be still in the process of writing. + let _guard = CACHE_GENERATION_LOCKS[bucket % CACHE_GENERATION_BUCKET_COUNT].lock(); + let exists = tokio::fs::try_exists(location.path()).await?; + if !exists { + let f = tokio::fs::File::create(location.path()).await?; + generate(f).await?; + } + drop(_guard); + Ok(location) +} + +pub fn cache_file<Fun>(seed: &[&str], mut generate: Fun) -> Result<AssetLocation, anyhow::Error> +where + Fun: FnMut(std::fs::File) -> Result<(), anyhow::Error>, +{ + let (bucket, location) = cache_location(seed); + // we need a lock even if it exists since somebody might be still in the process of writing. + let _guard = CACHE_GENERATION_LOCKS[bucket % CACHE_GENERATION_BUCKET_COUNT].lock(); + let exists = location.path().exists(); + if !exists { + let f = std::fs::File::create(location.path())?; + generate(f)?; + } + drop(_guard); + Ok(location) } pub trait AssetLocationExt { |