diff --git a/packages/cultcache-rs/examples/cultcache_interop.rs b/packages/cultcache-rs/examples/cultcache_interop.rs index e625db9e..c9bac14d 100644 --- a/packages/cultcache-rs/examples/cultcache_interop.rs +++ b/packages/cultcache-rs/examples/cultcache_interop.rs @@ -30,18 +30,35 @@ fn main() -> Result<()> { let mut args = std::env::args().skip(1); let mode = args .next() - .ok_or_else(|| anyhow!("expected mode: write | read"))?; + .ok_or_else(|| anyhow!("expected mode: write | read | hold-lock"))?; let options = parse_args(args.collect()); let file = require_arg(&options, "file")?; match mode.as_str() { "write" => write_note(file, require_arg(&options, "runtime-id")?)?, "read" => read_note(file)?, + "hold-lock" => hold_lock(file, require_arg(&options, "ms")?.parse()?)?, _ => return Err(anyhow!("unknown mode {mode}")), } Ok(()) } +/// Holds the store lock as the library opens it (`.lock`, fs2 exclusive), for `ms`. +fn hold_lock(file: &str, ms: u64) -> Result<()> { + let lock = std::fs::OpenOptions::new() + .create(true) + .read(true) + .truncate(false) + .write(true) + .open(format!("{file}.lock"))?; + fs2::FileExt::lock_exclusive(&lock)?; + println!("{}", json!({ "held": true })); + std::io::Write::flush(&mut std::io::stdout())?; + std::thread::sleep(std::time::Duration::from_millis(ms)); + fs2::FileExt::unlock(&lock)?; + Ok(()) +} + fn write_note(file: &str, runtime_id: &str) -> Result<()> { let mut cache = build_cache(file)?; cache.pull_all_backing_stores()?; diff --git a/tests/GameCult.Caching.InteropPeer/Program.cs b/tests/GameCult.Caching.InteropPeer/Program.cs index 11dadaad..20e2c62c 100644 --- a/tests/GameCult.Caching.InteropPeer/Program.cs +++ b/tests/GameCult.Caching.InteropPeer/Program.cs @@ -16,7 +16,7 @@ static async Task ProgramMainAsync(string[] args) { if (args.Length == 0) { - throw new InvalidOperationException("Expected mode: write | read | write-routed "); + throw new InvalidOperationException("Expected mode: write | read | hold-lock | write-routed "); } var mode = args[0]; @@ -38,6 +38,9 @@ static async Task ProgramMainAsync(string[] args) case "read": await ReadAsync(file); return 0; + case "hold-lock": + HoldLock(file, int.Parse(RequireArg(options, "ms"), CultureInfo.InvariantCulture)); + return 0; default: throw new InvalidOperationException($"Unknown mode {mode}."); } @@ -49,6 +52,20 @@ static async Task ProgramMainAsync(string[] args) } } +// Holds the store lock exactly as CultCache.AcquireLock opens it, for --ms, then releases it. +static void HoldLock(string file, int milliseconds) +{ + using var held = new System.IO.FileStream( + file + ".lock", + System.IO.FileMode.OpenOrCreate, + System.IO.FileAccess.ReadWrite, + System.IO.FileShare.None, + bufferSize: 1); + Console.WriteLine("{\"held\":true}"); + Console.Out.Flush(); + System.Threading.Thread.Sleep(milliseconds); +} + static async Task WriteAsync(string file, string runtimeId) { var cache = BuildCache(file);