Tries to acquire a lock for specific task. Returns Some(path) to the lock if succeeds. The task path must not contain any extension and have file stem. To release a lock you need either manually rename or remove it, or wait until it expires and cleanup task removes it. Note: this function is racy. Main idea is: be fault tolerant and never block some task. The price is that we rarely do some tas
(
task_path: &Path,
timeout: Duration,
allowed_future_drift: Duration,
)
| 781 | /// never block some task. The price is that we rarely do some task |
| 782 | /// more than once. |
| 783 | fn acquire_task_fs_lock( |
| 784 | task_path: &Path, |
| 785 | timeout: Duration, |
| 786 | allowed_future_drift: Duration, |
| 787 | ) -> Option<PathBuf> { |
| 788 | assert!(task_path.extension().is_none()); |
| 789 | assert!(task_path.file_stem().is_some()); |
| 790 | |
| 791 | // list directory |
| 792 | let dir_path = task_path.parent()?; |
| 793 | let it = fs::read_dir(dir_path) |
| 794 | .map_err(|err| { |
| 795 | warn!( |
| 796 | "Failed to list cache directory, path: {}, err: {}", |
| 797 | dir_path.display(), |
| 798 | err |
| 799 | ) |
| 800 | }) |
| 801 | .ok()?; |
| 802 | |
| 803 | // look for existing locks |
| 804 | for entry in it { |
| 805 | let entry = entry |
| 806 | .map_err(|err| { |
| 807 | warn!( |
| 808 | "Failed to list cache directory, path: {}, err: {}", |
| 809 | dir_path.display(), |
| 810 | err |
| 811 | ) |
| 812 | }) |
| 813 | .ok()?; |
| 814 | |
| 815 | let path = entry.path(); |
| 816 | if path.is_dir() || path.file_stem() != task_path.file_stem() { |
| 817 | continue; |
| 818 | } |
| 819 | |
| 820 | // check extension and mtime |
| 821 | match path.extension() { |
| 822 | None => continue, |
| 823 | Some(ext) => { |
| 824 | if let Some(ext_str) = ext.to_str() { |
| 825 | // if it's None, i.e. not valid UTF-8 string, then that's not our lock for sure |
| 826 | if ext_str.starts_with("wip-") |
| 827 | && !is_fs_lock_expired(Some(&entry), &path, timeout, allowed_future_drift) |
| 828 | { |
| 829 | return None; |
| 830 | } |
| 831 | } |
| 832 | } |
| 833 | } |
| 834 | } |
| 835 | |
| 836 | // create the lock |
| 837 | let lock_path = task_path.with_extension(format!("wip-{}", std::process::id())); |
| 838 | let _file = fs::OpenOptions::new() |
| 839 | .create_new(true) |
| 840 | .write(true) |