From 1b910ddc969b4cce65a5a0f4d0f5c3d84e803fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Fri, 5 Apr 2024 17:47:17 +0800 Subject: [PATCH] perf: re-implement file watcher in an async way (#877) --- .github/workflows/lock.yml | 6 +- Cargo.lock | 393 +----------------- yazi-adaptor/Cargo.toml | 2 +- yazi-core/src/lib.rs | 7 +- .../src/manager/commands/update_files.rs | 4 +- .../src/manager/commands/update_mimetype.rs | 4 +- yazi-core/src/manager/watcher.rs | 153 +++---- 7 files changed, 100 insertions(+), 469 deletions(-) diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index c76f7ba0..557f29b2 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -14,14 +14,18 @@ jobs: permissions: issues: write pull-requests: write + discussions: write steps: - uses: dessant/lock-threads@v4 with: issue-inactive-days: "30" issue-comment: > I'm going to lock this issue because it has been closed for _30 days_. ⏳ + This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. - process-only: "issues" + pr-inactive-days: "30" + discussion-inactive-days: "30" + process-only: "issues,prs,discussions" diff --git a/Cargo.lock b/Cargo.lock index b376e14e..06d81cef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,12 +38,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "aligned-vec" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" - [[package]] name = "allocator-api2" version = "0.2.16" @@ -130,35 +124,12 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" - [[package]] name = "arc-swap" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.57", -] - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - [[package]] name = "async-priority-channel" version = "0.2.0" @@ -174,29 +145,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" -[[package]] -name = "av1-grain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" -dependencies = [ - "arrayvec", -] - [[package]] name = "backtrace" version = "0.3.71" @@ -261,12 +209,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" -[[package]] -name = "bitstream-io" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c9989a51171e2e81038ab168b6ae22886fe9ded214430dbb4f41c28cf176da" - [[package]] name = "block-buffer" version = "0.10.4" @@ -286,12 +228,6 @@ dependencies = [ "serde", ] -[[package]] -name = "built" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d17f4d6e4dc36d1a02fbedc2753a096848e7c1b0772f7654eab8e2c927dd53" - [[package]] name = "bumpalo" version = "3.15.4" @@ -336,20 +272,6 @@ name = "cc" version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "cfg-expr" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" -dependencies = [ - "smallvec", - "target-lexicon", -] [[package]] name = "cfg-if" @@ -1023,35 +945,20 @@ dependencies = [ [[package]] name = "image" -version = "0.25.1" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", "color_quant", "exr", "gif", - "image-webp", + "jpeg-decoder", "num-traits", "png", "qoi", - "ravif", - "rayon", - "rgb", "tiff", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a84a25dcae3ac487bc24ef280f9e20c79c9b1a3e5e32cbed3041d1c514aa87c" -dependencies = [ - "byteorder", - "thiserror", ] [[package]] @@ -1060,12 +967,6 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" -[[package]] -name = "imgref" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" - [[package]] name = "indexmap" version = "2.2.6" @@ -1102,17 +1003,6 @@ dependencies = [ "libc", ] -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.57", -] - [[package]] name = "itertools" version = "0.12.1" @@ -1128,20 +1018,14 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "jobserver" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" -dependencies = [ - "libc", -] - [[package]] name = "jpeg-decoder" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +dependencies = [ + "rayon", +] [[package]] name = "js-sys" @@ -1199,17 +1083,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "libfuzzer-sys" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" -dependencies = [ - "arbitrary", - "cc", - "once_cell", -] - [[package]] name = "libredox" version = "0.1.3" @@ -1248,15 +1121,6 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - [[package]] name = "lru" version = "0.12.3" @@ -1294,16 +1158,6 @@ dependencies = [ "libc", ] -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - [[package]] name = "md-5" version = "0.10.6" @@ -1400,12 +1254,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - [[package]] name = "nom" version = "7.1.3" @@ -1416,12 +1264,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - [[package]] name = "notify" version = "6.1.1" @@ -1450,55 +1292,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.57", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.18" @@ -1686,12 +1485,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1725,25 +1518,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "profiling" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" -dependencies = [ - "quote", - "syn 2.0.57", -] - [[package]] name = "qoi" version = "0.4.1" @@ -1753,12 +1527,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quick-xml" version = "0.31.0" @@ -1777,36 +1545,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - [[package]] name = "ratatui" version = "0.26.1" @@ -1827,56 +1565,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "rav1e" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" -dependencies = [ - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "once_cell", - "paste", - "profiling", - "rand", - "rand_chacha", - "simd_helpers", - "system-deps", - "thiserror", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error", - "rav1e", - "rayon", - "rgb", -] - [[package]] name = "rayon" version = "1.10.0" @@ -1946,15 +1634,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" -[[package]] -name = "rgb" -version = "0.8.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" -dependencies = [ - "bytemuck", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -2126,15 +1805,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - [[package]] name = "slab" version = "0.4.9" @@ -2262,25 +1932,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "system-deps" -version = "6.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" -dependencies = [ - "cfg-expr", - "heck 0.5.0", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "target-lexicon" -version = "0.12.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" - [[package]] name = "thiserror" version = "1.0.58" @@ -2630,17 +2281,6 @@ dependencies = [ "log", ] -[[package]] -name = "v_frame" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - [[package]] name = "validator" version = "0.17.0" @@ -2689,12 +2329,6 @@ dependencies = [ "time", ] -[[package]] -name = "version-compare" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" - [[package]] name = "version_check" version = "0.9.4" @@ -3289,12 +2923,6 @@ dependencies = [ "syn 2.0.57", ] -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - [[package]] name = "zune-inflate" version = "0.2.54" @@ -3303,12 +2931,3 @@ checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" dependencies = [ "simd-adler32", ] - -[[package]] -name = "zune-jpeg" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" -dependencies = [ - "zune-core", -] diff --git a/yazi-adaptor/Cargo.toml b/yazi-adaptor/Cargo.toml index 2d3d7373..4e1e2e1a 100644 --- a/yazi-adaptor/Cargo.toml +++ b/yazi-adaptor/Cargo.toml @@ -19,7 +19,7 @@ base64 = "0.22.0" color_quant = "1.1.0" crossterm = "0.27.0" futures = "0.3.30" -image = "0.25.1" +image = "0.24.9" imagesize = "0.12.0" kamadak-exif = "0.5.5" ratatui = "0.26.1" diff --git a/yazi-core/src/lib.rs b/yazi-core/src/lib.rs index e7ff1a7e..43ab4f08 100644 --- a/yazi-core/src/lib.rs +++ b/yazi-core/src/lib.rs @@ -22,4 +22,9 @@ pub mod which; pub use clipboard::*; pub use step::*; -pub fn init() { CLIPBOARD.with(Default::default); } +pub fn init() { + CLIPBOARD.with(Default::default); + + manager::WATCHED.with(Default::default); + manager::LINKED.with(Default::default); +} diff --git a/yazi-core/src/manager/commands/update_files.rs b/yazi-core/src/manager/commands/update_files.rs index a7fc1adb..690d3e74 100644 --- a/yazi-core/src/manager/commands/update_files.rs +++ b/yazi-core/src/manager/commands/update_files.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use yazi_proxy::ManagerProxy; use yazi_shared::{event::Cmd, fs::FilesOp, render}; -use crate::{folder::Folder, manager::Manager, tab::Tab, tasks::Tasks}; +use crate::{folder::Folder, manager::{Manager, LINKED}, tab::Tab, tasks::Tasks}; pub struct Opt { op: FilesOp, @@ -22,7 +22,7 @@ impl Manager { }; let mut ops = vec![opt.op]; - for u in self.watcher.linked.read().from_dir(ops[0].url()) { + for u in LINKED.read().from_dir(ops[0].url()) { ops.push(ops[0].chroot(u)); } diff --git a/yazi-core/src/manager/commands/update_mimetype.rs b/yazi-core/src/manager/commands/update_mimetype.rs index e069fcf5..28f3f523 100644 --- a/yazi-core/src/manager/commands/update_mimetype.rs +++ b/yazi-core/src/manager/commands/update_mimetype.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use yazi_dds::ValueSendable; use yazi_shared::{event::Cmd, fs::Url, render}; -use crate::{manager::Manager, tasks::Tasks}; +use crate::{manager::{Manager, LINKED}, tasks::Tasks}; pub struct Opt { data: ValueSendable, @@ -23,7 +23,7 @@ impl Manager { return; }; - let linked = self.watcher.linked.read(); + let linked = LINKED.read(); let updates = opt .data .into_table_string() diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index d2f8837f..eea2e80a 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -1,86 +1,66 @@ -use std::{collections::{HashMap, HashSet}, sync::Arc, time::{Duration, SystemTime}}; +use std::{collections::{HashMap, HashSet}, time::{Duration, SystemTime}}; use anyhow::Result; use notify::{event::{MetadataKind, ModifyKind}, EventKind, RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; use parking_lot::RwLock; -use tokio::{fs, pin, sync::mpsc::{self, UnboundedReceiver}}; +use tokio::{fs, pin, sync::{mpsc::{self, UnboundedReceiver}, watch}}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use tracing::error; use yazi_plugin::isolate; use yazi_proxy::WATCHER; -use yazi_shared::fs::{File, FilesOp, Url}; +use yazi_shared::{fs::{File, FilesOp, Url}, RoCell}; use super::Linked; use crate::folder::{Files, Folder}; +pub(crate) static WATCHED: RoCell>> = RoCell::new(); +pub static LINKED: RoCell> = RoCell::new(); + pub struct Watcher { - watcher: RecommendedWatcher, - watched: Arc>>, - pub linked: Arc>, + tx: watch::Sender>, } impl Watcher { pub(super) fn serve() -> Self { - let (tx, rx) = mpsc::unbounded_channel(); - let watcher = RecommendedWatcher::new( - { - let tx = tx.clone(); - move |res: Result| { - let Ok(event) = res else { return }; + let (in_tx, in_rx) = watch::channel(Default::default()); + let (out_tx, out_rx) = mpsc::unbounded_channel(); - match event.kind { - EventKind::Create(_) => {} - EventKind::Modify(kind) => match kind { - ModifyKind::Data(_) => {} - ModifyKind::Metadata(md) => match md { - MetadataKind::WriteTime => {} - MetadataKind::Permissions => {} - MetadataKind::Ownership => {} - _ => return, - }, - ModifyKind::Name(_) => {} + let watcher = RecommendedWatcher::new( + move |res: Result| { + let Ok(event) = res else { return }; + + match event.kind { + EventKind::Create(_) => {} + EventKind::Modify(kind) => match kind { + ModifyKind::Data(_) => {} + ModifyKind::Metadata(md) => match md { + MetadataKind::WriteTime => {} + MetadataKind::Permissions => {} + MetadataKind::Ownership => {} _ => return, }, - EventKind::Remove(_) => {} + ModifyKind::Name(_) => {} _ => return, - } + }, + EventKind::Remove(_) => {} + _ => return, + } - for path in event.paths { - tx.send(Url::from(path)).ok(); - } + for path in event.paths { + out_tx.send(Url::from(path)).ok(); } }, Default::default(), ); - let instance = - Self { watcher: watcher.unwrap(), watched: Default::default(), linked: Default::default() }; - tokio::spawn(Self::on_changed(rx)); - instance + tokio::spawn(Self::on_in(in_rx, watcher.unwrap())); + tokio::spawn(Self::on_out(out_rx)); + Self { tx: in_tx } } pub(super) fn watch(&mut self, mut new: HashSet<&Url>) { new.retain(|&u| u.is_regular()); - let (to_unwatch, to_watch): (HashSet<_>, HashSet<_>) = { - let guard = self.watched.read(); - let old: HashSet<_> = guard.iter().collect(); - ( - old.difference(&new).map(|&x| x.clone()).collect(), - new.difference(&old).map(|&x| x.clone()).collect(), - ) - }; - - for u in to_unwatch { - self.watcher.unwatch(&u).ok(); - } - for u in to_watch { - if self.watcher.watch(&u, RecursiveMode::NonRecursive).is_err() { - new.remove(&u); - } - } - - *self.watched.write() = new.into_iter().cloned().collect(); - self.sync_linked(); + self.tx.send(new.into_iter().cloned().collect()).ok(); } pub(super) fn trigger_dirs(&self, folders: &[&Folder]) { @@ -114,33 +94,33 @@ impl Watcher { }); } - fn sync_linked(&self) { - let mut new = self.watched.read().clone(); - self.linked.write().retain(|k, _| new.remove(k)); - - let watched = self.watched.clone(); - let linked = self.linked.clone(); - macro_rules! go { - ($todo:expr) => { - for from in $todo { - match fs::canonicalize(&from).await { - Ok(to) if to != *from && watched.read().contains(&from) => { - linked.write().insert(from, Url::from(to)); - } - _ => {} - } - } + async fn on_in(mut rx: watch::Receiver>, mut watcher: RecommendedWatcher) { + loop { + let (mut to_unwatch, mut to_watch): (HashSet<_>, HashSet<_>) = { + let (new, old) = (&*rx.borrow_and_update(), &*WATCHED.read()); + (old.difference(new).cloned().collect(), new.difference(old).cloned().collect()) }; - } - tokio::spawn(async move { - let old: Vec<_> = linked.read().keys().cloned().collect(); - go!(new); - go!(old); - }); + to_unwatch.retain(|u| watcher.unwatch(u).is_ok()); + to_watch.retain(|u| watcher.watch(u, RecursiveMode::NonRecursive).is_ok()); + + { + let mut watched = WATCHED.write(); + watched.retain(|u| !to_unwatch.contains(u)); + watched.extend(to_watch); + } + + if !rx.has_changed().unwrap_or(false) { + Self::sync_linked().await; + } + + if rx.changed().await.is_err() { + break; + } + } } - async fn on_changed(rx: UnboundedReceiver) { + async fn on_out(rx: UnboundedReceiver) { // TODO: revert this once a new notification is implemented let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(50)); pin!(rx); @@ -171,4 +151,27 @@ impl Watcher { } } } + + async fn sync_linked() { + let mut new = WATCHED.read().clone(); + + let old = { + let mut linked = LINKED.write(); + linked.retain(|k, _| new.remove(k)); + linked.keys().cloned().collect() + }; + + async fn go(todo: HashSet) { + for from in todo { + let Ok(to) = fs::canonicalize(&from).await else { continue }; + + if to != *from && WATCHED.read().contains(&from) { + LINKED.write().insert(from, Url::from(to)); + } + } + } + + go(new).await; + go(old).await; + } }