mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-31 19:32:13 +08:00
Refactor timer management and remove delay_timer dependency (#7075)
* Refactor timer management and remove delay_timer dependency
The delay_timer spawned tokio threads of cores, which not controled by expected.
* refactor: improve logging messages in Timer implementation
* fix(timer): prevent potential panic by using saturating_mul for interval calculations
* refactor timer scheduler
* chore: translate timer logs to english
* fix timer clippy warnings
* fix profile auto update refresh
* fix duplicate profile update notification
* fix: prevent potential panic in lightweight timer setup
* fix profile update listener cleanup
* Revert "fix duplicate profile update notification"
This reverts commit 4aafa94d3e.
This commit is contained in:
Generated
+34
-316
@@ -186,23 +186,12 @@ version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
|
||||
dependencies = [
|
||||
"event-listener 5.3.0",
|
||||
"event-listener",
|
||||
"event-listener-strategy",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener 2.5.3",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "2.5.0"
|
||||
@@ -223,44 +212,12 @@ checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand 2.4.1",
|
||||
"futures-lite 2.6.1",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-fs"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
|
||||
dependencies = [
|
||||
"async-lock 2.8.0",
|
||||
"autocfg",
|
||||
"blocking",
|
||||
"futures-lite 1.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
|
||||
dependencies = [
|
||||
"async-lock 2.8.0",
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"futures-lite 1.13.0",
|
||||
"log",
|
||||
"parking",
|
||||
"polling 2.8.0",
|
||||
"rustix 0.37.28",
|
||||
"slab",
|
||||
"socket2 0.4.10",
|
||||
"waker-fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "2.6.0"
|
||||
@@ -271,77 +228,40 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"futures-io",
|
||||
"futures-lite 2.6.1",
|
||||
"futures-lite",
|
||||
"parking",
|
||||
"polling 3.11.0",
|
||||
"polling",
|
||||
"rustix 1.1.4",
|
||||
"slab",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
|
||||
dependencies = [
|
||||
"event-listener 2.5.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "3.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
|
||||
dependencies = [
|
||||
"event-listener 5.3.0",
|
||||
"event-listener",
|
||||
"event-listener-strategy",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-net"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0434b1ed18ce1cf5769b8ac540e33f01fa9471058b5e89da9e06f3c882a8c12f"
|
||||
dependencies = [
|
||||
"async-io 1.13.0",
|
||||
"blocking",
|
||||
"futures-lite 1.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88"
|
||||
dependencies = [
|
||||
"async-io 1.13.0",
|
||||
"async-lock 2.8.0",
|
||||
"async-signal",
|
||||
"blocking",
|
||||
"cfg-if",
|
||||
"event-listener 3.1.0",
|
||||
"futures-lite 1.13.0",
|
||||
"rustix 0.38.44",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
|
||||
dependencies = [
|
||||
"async-channel 2.5.0",
|
||||
"async-io 2.6.0",
|
||||
"async-lock 3.4.2",
|
||||
"async-channel",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-signal",
|
||||
"async-task",
|
||||
"blocking",
|
||||
"cfg-if",
|
||||
"event-listener 5.3.0",
|
||||
"futures-lite 2.6.1",
|
||||
"event-listener",
|
||||
"futures-lite",
|
||||
"rustix 1.1.4",
|
||||
]
|
||||
|
||||
@@ -362,8 +282,8 @@ version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
|
||||
dependencies = [
|
||||
"async-io 2.6.0",
|
||||
"async-lock 3.4.2",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"atomic-waker",
|
||||
"cfg-if",
|
||||
"futures-core",
|
||||
@@ -575,7 +495,7 @@ version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef"
|
||||
dependencies = [
|
||||
"fastrand 2.4.1",
|
||||
"fastrand",
|
||||
"gloo-timers",
|
||||
"tokio",
|
||||
]
|
||||
@@ -683,10 +603,10 @@ version = "1.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
|
||||
dependencies = [
|
||||
"async-channel 2.5.0",
|
||||
"async-channel",
|
||||
"async-task",
|
||||
"futures-io",
|
||||
"futures-lite 2.6.1",
|
||||
"futures-lite",
|
||||
"piper",
|
||||
]
|
||||
|
||||
@@ -723,13 +643,13 @@ dependencies = [
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"cow-utils",
|
||||
"dashmap 6.1.0",
|
||||
"dashmap",
|
||||
"dynify",
|
||||
"fast-float2",
|
||||
"float16",
|
||||
"futures-channel",
|
||||
"futures-concurrency",
|
||||
"futures-lite 2.6.1",
|
||||
"futures-lite",
|
||||
"hashbrown 0.16.1",
|
||||
"icu_normalizer",
|
||||
"indexmap 2.14.0",
|
||||
@@ -1174,7 +1094,6 @@ dependencies = [
|
||||
"criterion",
|
||||
"dark-light",
|
||||
"deelevate",
|
||||
"delay_timer",
|
||||
"dunce",
|
||||
"flexi_logger",
|
||||
"futures",
|
||||
@@ -1222,6 +1141,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"warp",
|
||||
"webpki-roots",
|
||||
"windows 0.62.2",
|
||||
@@ -1390,16 +1310,6 @@ dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concat-idents"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
@@ -1654,17 +1564,6 @@ dependencies = [
|
||||
"itertools 0.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cron_clock"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a8699d8ed16e3db689f8ae04d8dc3c6666a4ba7e724e5a157884b7cc385d16b"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"nom 7.1.3",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.8.4"
|
||||
@@ -1888,19 +1787,6 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown 0.14.5",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "6.1.0"
|
||||
@@ -1958,31 +1844,6 @@ version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2"
|
||||
|
||||
[[package]]
|
||||
name = "delay_timer"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20eba879b7ee5f1d255d5666e16646fe384f899f71a4c0b4b0644e2e074964fe"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"autocfg",
|
||||
"concat-idents",
|
||||
"cron_clock",
|
||||
"dashmap 5.5.3",
|
||||
"event-listener 5.3.0",
|
||||
"futures",
|
||||
"log",
|
||||
"lru",
|
||||
"once_cell",
|
||||
"rs-snowflake",
|
||||
"rustc_version 0.2.3",
|
||||
"smol",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.8"
|
||||
@@ -2470,23 +2331,6 @@ version = "3.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.3.0"
|
||||
@@ -2504,7 +2348,7 @@ version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
|
||||
dependencies = [
|
||||
"event-listener 5.3.0",
|
||||
"event-listener",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
@@ -2514,15 +2358,6 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.4.1"
|
||||
@@ -2725,7 +2560,7 @@ checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"futures-core",
|
||||
"futures-lite 2.6.1",
|
||||
"futures-lite",
|
||||
"pin-project",
|
||||
"smallvec",
|
||||
]
|
||||
@@ -2753,28 +2588,13 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
|
||||
dependencies = [
|
||||
"fastrand 1.9.0",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"memchr",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
"waker-fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
|
||||
dependencies = [
|
||||
"fastrand 2.4.1",
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"parking",
|
||||
@@ -3282,8 +3102,6 @@ version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.1.5",
|
||||
]
|
||||
|
||||
@@ -3353,12 +3171,6 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.2"
|
||||
@@ -3853,15 +3665,6 @@ dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "interprocess"
|
||||
version = "2.4.2"
|
||||
@@ -3886,17 +3689,6 @@ dependencies = [
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.12.0"
|
||||
@@ -3941,7 +3733,7 @@ version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
||||
dependencies = [
|
||||
"hermit-abi 0.5.2",
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
@@ -4292,12 +4084,6 @@ dependencies = [
|
||||
"redox_syscall 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
@@ -4349,15 +4135,6 @@ version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
@@ -4721,7 +4498,7 @@ version = "4.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bdaf6120b9df005d37e58f6b75329be6255450453fbeba9ce4192324f921fb9"
|
||||
dependencies = [
|
||||
"futures-lite 2.6.1",
|
||||
"futures-lite",
|
||||
"log",
|
||||
"mac-notification-sys",
|
||||
"serde",
|
||||
@@ -5421,7 +5198,7 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
|
||||
dependencies = [
|
||||
"fastrand 2.4.1",
|
||||
"fastrand",
|
||||
"phf_shared 0.13.1",
|
||||
]
|
||||
|
||||
@@ -5534,7 +5311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"fastrand 2.4.1",
|
||||
"fastrand",
|
||||
"futures-io",
|
||||
]
|
||||
|
||||
@@ -5617,22 +5394,6 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"libc",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "3.11.0"
|
||||
@@ -5641,7 +5402,7 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"hermit-abi 0.5.2",
|
||||
"hermit-abi",
|
||||
"pin-project-lite",
|
||||
"rustix 1.1.4",
|
||||
"windows-sys 0.61.2",
|
||||
@@ -6463,12 +6224,6 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rs-snowflake"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e60ef3b82994702bbe4e134d98aadca4b49ed04440148985678d415c68127666"
|
||||
|
||||
[[package]]
|
||||
name = "runas"
|
||||
version = "1.2.0"
|
||||
@@ -6578,20 +6333,6 @@ dependencies = [
|
||||
"semver 1.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys 0.3.8",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
@@ -7304,23 +7045,6 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smol"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1"
|
||||
dependencies = [
|
||||
"async-channel 1.9.0",
|
||||
"async-executor",
|
||||
"async-fs",
|
||||
"async-io 1.13.0",
|
||||
"async-lock 2.8.0",
|
||||
"async-net",
|
||||
"async-process 1.8.1",
|
||||
"blocking",
|
||||
"futures-lite 1.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.10"
|
||||
@@ -8245,7 +7969,7 @@ version = "3.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand 2.4.1",
|
||||
"fastrand",
|
||||
"getrandom 0.4.2",
|
||||
"once_cell",
|
||||
"rustix 1.1.4",
|
||||
@@ -8504,7 +8228,6 @@ dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2 0.6.3",
|
||||
@@ -8577,6 +8300,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -9298,12 +9022,6 @@ dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "waker-fn"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
@@ -10598,17 +10316,17 @@ checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-executor",
|
||||
"async-io 2.6.0",
|
||||
"async-lock 3.4.2",
|
||||
"async-process 2.5.0",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-process",
|
||||
"async-recursion",
|
||||
"async-task",
|
||||
"async-trait",
|
||||
"blocking",
|
||||
"enumflags2",
|
||||
"event-listener 5.3.0",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
"futures-lite 2.6.1",
|
||||
"futures-lite",
|
||||
"hex",
|
||||
"libc",
|
||||
"ordered-stream",
|
||||
|
||||
@@ -62,7 +62,6 @@ nanoid = "0.5"
|
||||
chrono = "0.4.44"
|
||||
boa_engine = "0.21.1"
|
||||
once_cell = { version = "1.21.4", features = ["parking_lot"] }
|
||||
delay_timer = "0.11.6"
|
||||
percent-encoding = "2.3.2"
|
||||
reqwest = { version = "0.13.3", features = [
|
||||
"json",
|
||||
@@ -109,6 +108,7 @@ rust_iso3166 = "0.1.14"
|
||||
# Use the git repo until the next release after v2.0.0.
|
||||
dark-light = { git = "https://github.com/rust-dark-light/dark-light" }
|
||||
bytes = "1.11.1"
|
||||
tokio-util = { version = "0.7.18", features = ["time"] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc2 = "0.6.4"
|
||||
|
||||
@@ -349,9 +349,9 @@ pub async fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
|
||||
// 如果更新间隔或允许自动更新变更,异步刷新定时器
|
||||
if should_refresh_timer {
|
||||
crate::process::AsyncHandler::spawn(move || async move {
|
||||
logging!(info, Type::Timer, "定时器更新间隔已变更,正在刷新定时器...");
|
||||
logging!(info, Type::Timer, "Timer update settings changed, refreshing timer...");
|
||||
if let Err(e) = crate::core::Timer::global().refresh().await {
|
||||
logging!(error, Type::Timer, "刷新定时器失败: {}", e);
|
||||
logging!(error, Type::Timer, "Failed to refresh timer: {}", e);
|
||||
} else {
|
||||
// 刷新成功后发送自定义事件,不触发配置重载
|
||||
crate::core::handle::Handle::notify_timer_updated(&index);
|
||||
|
||||
+271
-331
@@ -1,60 +1,66 @@
|
||||
use crate::{config::Config, feat, singleton, utils::resolve::is_resolve_done};
|
||||
use anyhow::{Context as _, Result};
|
||||
use crate::{config::Config, core::handle, feat, process::AsyncHandler, singleton, utils::resolve::is_resolve_done};
|
||||
use anyhow::Result;
|
||||
use clash_verge_logging::{Type, logging, logging_error};
|
||||
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
|
||||
use parking_lot::RwLock;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use smartstring::alias::String;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
pin::Pin,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, AtomicU64, Ordering},
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::time::{sleep, timeout};
|
||||
use tokio::{
|
||||
sync::mpsc,
|
||||
time::{sleep, timeout},
|
||||
};
|
||||
use tokio_stream::StreamExt as _;
|
||||
use tokio_util::time::{DelayQueue, delay_queue::Key};
|
||||
|
||||
type TaskID = u64;
|
||||
enum TimerCommand {
|
||||
Apply(HashMap<String, u64>),
|
||||
RunNow(String),
|
||||
TaskFinished(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TimerTask {
|
||||
pub task_id: TaskID,
|
||||
pub interval_minutes: u64,
|
||||
#[allow(unused)]
|
||||
pub last_run: i64, // Timestamp of last execution
|
||||
struct TaskState {
|
||||
key: Option<Key>,
|
||||
interval_minutes: u64,
|
||||
running: bool,
|
||||
}
|
||||
|
||||
impl TaskState {
|
||||
const fn new(key: Key, interval_minutes: u64) -> Self {
|
||||
Self {
|
||||
key: Some(key),
|
||||
interval_minutes,
|
||||
running: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Timer {
|
||||
/// cron manager
|
||||
pub delay_timer: Arc<RwLock<DelayTimer>>,
|
||||
|
||||
/// save the current state - using RwLock for better read concurrency
|
||||
pub timer_map: Arc<RwLock<HashMap<String, TimerTask>>>,
|
||||
|
||||
/// increment id - atomic counter for better performance
|
||||
pub timer_count: AtomicU64,
|
||||
|
||||
/// Flag to mark if timer is initialized - atomic for better performance
|
||||
command_tx: mpsc::UnboundedSender<TimerCommand>,
|
||||
command_rx: Mutex<Option<mpsc::UnboundedReceiver<TimerCommand>>>,
|
||||
pub timer_map: Arc<RwLock<HashMap<String, u64>>>,
|
||||
pub initialized: AtomicBool,
|
||||
}
|
||||
|
||||
// Use singleton macro
|
||||
singleton!(Timer, TIMER_INSTANCE);
|
||||
|
||||
impl Timer {
|
||||
fn new() -> Self {
|
||||
let (command_tx, command_rx) = mpsc::unbounded_channel();
|
||||
Self {
|
||||
delay_timer: Arc::new(RwLock::new(DelayTimerBuilder::default().build())),
|
||||
command_tx,
|
||||
command_rx: Mutex::new(Some(command_rx)),
|
||||
timer_map: Arc::new(RwLock::new(HashMap::new())),
|
||||
timer_count: AtomicU64::new(1),
|
||||
initialized: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize timer with better error handling and atomic operations
|
||||
pub async fn init(&self) -> Result<()> {
|
||||
// Use compare_exchange for thread-safe initialization check
|
||||
if self
|
||||
.initialized
|
||||
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
||||
@@ -64,76 +70,48 @@ impl Timer {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Initialize timer tasks
|
||||
let command_rx = { self.command_rx.lock().take() };
|
||||
if let Some(command_rx) = command_rx {
|
||||
let command_tx = self.command_tx.clone();
|
||||
AsyncHandler::spawn(move || async move {
|
||||
Self::run_scheduler(command_rx, command_tx).await;
|
||||
});
|
||||
}
|
||||
|
||||
if let Err(e) = self.refresh().await {
|
||||
// Reset initialization flag on error
|
||||
self.initialized.store(false, Ordering::SeqCst);
|
||||
logging_error!(Type::Timer, "Failed to initialize timer: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
// Log timer info first
|
||||
{
|
||||
let timer_map = self.timer_map.read();
|
||||
logging!(info, Type::Timer, "已注册的定时任务数量: {}", timer_map.len());
|
||||
|
||||
for (uid, task) in timer_map.iter() {
|
||||
logging!(debug, Type::Timer, "Registered timer task count: {}", timer_map.len());
|
||||
for (uid, interval) in timer_map.iter() {
|
||||
logging!(
|
||||
info,
|
||||
debug,
|
||||
Type::Timer,
|
||||
"注册了定时任务 - uid={}, interval={}min, task_id={}",
|
||||
"Registered timer task: uid={}, interval={}min",
|
||||
uid,
|
||||
task.interval_minutes,
|
||||
task.task_id
|
||||
interval
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let cur_timestamp = chrono::Local::now().timestamp();
|
||||
|
||||
// Collect profiles that need immediate update
|
||||
let profiles_to_update = if let Some(items) = Config::profiles().await.latest_arc().get_items() {
|
||||
items
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
let allow_auto_update = item.option.as_ref()?.allow_auto_update.unwrap_or_default();
|
||||
if !allow_auto_update {
|
||||
return None;
|
||||
}
|
||||
|
||||
let interval = item.option.as_ref()?.update_interval? as i64;
|
||||
let updated = item.updated? as i64;
|
||||
let uid = item.uid.as_ref()?;
|
||||
|
||||
if interval > 0 && cur_timestamp - updated >= interval * 60 {
|
||||
logging!(info, Type::Timer, "需要立即更新的配置: uid={}", uid);
|
||||
Some(uid.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
// Advance tasks outside of locks to minimize lock contention
|
||||
if !profiles_to_update.is_empty() {
|
||||
logging!(
|
||||
info,
|
||||
Type::Timer,
|
||||
"需要立即更新的配置数量: {}",
|
||||
profiles_to_update.len()
|
||||
);
|
||||
let timer_map = self.timer_map.read();
|
||||
|
||||
for uid in profiles_to_update {
|
||||
if let Some(task) = timer_map.get(&uid) {
|
||||
logging!(info, Type::Timer, "立即执行任务: uid={}", uid);
|
||||
let delay_timer = self.delay_timer.write();
|
||||
if let Err(e) = delay_timer.advance_task(task.task_id) {
|
||||
logging!(warn, Type::Timer, "Failed to advance task {}: {}", uid, e);
|
||||
}
|
||||
if let Some(items) = Config::profiles().await.latest_arc().get_items() {
|
||||
for item in items.iter() {
|
||||
if let Some(option) = item.option.as_ref()
|
||||
&& let Some(allow_auto_update) = option.allow_auto_update
|
||||
&& allow_auto_update
|
||||
&& let Some(interval) = option.update_interval
|
||||
&& interval > 0
|
||||
&& let Some(uid) = item.uid.as_ref()
|
||||
&& let Some(updated) = item.updated
|
||||
&& cur_timestamp - (updated as i64) >= (interval as i64) * 60
|
||||
{
|
||||
logging!(info, Type::Timer, "Running overdue timer task immediately: uid={}", uid);
|
||||
let _ = self.command_tx.send(TimerCommand::RunNow(uid.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,96 +120,29 @@ impl Timer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Refresh timer tasks with better error handling
|
||||
pub async fn refresh(&self) -> Result<()> {
|
||||
// Generate diff outside of lock to minimize lock contention
|
||||
let diff_map = self.gen_diff().await;
|
||||
let new_map = self.gen_map().await;
|
||||
|
||||
if diff_map.is_empty() {
|
||||
let mut cache = self.timer_map.write();
|
||||
if *cache == new_map {
|
||||
logging!(debug, Type::Timer, "No timer changes needed");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
logging!(info, Type::Timer, "Refreshing {} timer tasks", diff_map.len());
|
||||
logging!(
|
||||
info,
|
||||
Type::Timer,
|
||||
"Refreshing timer tasks map, count: {}",
|
||||
new_map.len()
|
||||
);
|
||||
*cache = new_map.clone();
|
||||
drop(cache);
|
||||
|
||||
// Apply changes - first collect operations to perform without holding locks
|
||||
let mut operations_to_add: Vec<(String, TaskID, u64)> = Vec::new();
|
||||
let _operations_to_remove: Vec<String> = Vec::new();
|
||||
|
||||
// Perform sync operations while holding locks
|
||||
{
|
||||
for (uid, diff) in diff_map {
|
||||
match diff {
|
||||
DiffFlag::Del(tid) => {
|
||||
self.timer_map.write().remove(&uid);
|
||||
let value = self.delay_timer.write().remove_task(tid);
|
||||
if let Err(e) = value {
|
||||
logging!(
|
||||
warn,
|
||||
Type::Timer,
|
||||
"Failed to remove task {} for uid {}: {}",
|
||||
tid,
|
||||
uid,
|
||||
e
|
||||
);
|
||||
} else {
|
||||
logging!(debug, Type::Timer, "Removed task {} for uid {}", tid, uid);
|
||||
}
|
||||
}
|
||||
DiffFlag::Add(tid, interval) => {
|
||||
let task = TimerTask {
|
||||
task_id: tid,
|
||||
interval_minutes: interval,
|
||||
last_run: chrono::Local::now().timestamp(),
|
||||
};
|
||||
|
||||
self.timer_map.write().insert(uid.clone(), task);
|
||||
operations_to_add.push((uid, tid, interval));
|
||||
}
|
||||
DiffFlag::Mod(tid, interval) => {
|
||||
// Remove old task first
|
||||
let value = self.delay_timer.write().remove_task(tid);
|
||||
if let Err(e) = value {
|
||||
logging!(
|
||||
warn,
|
||||
Type::Timer,
|
||||
"Failed to remove old task {} for uid {}: {}",
|
||||
tid,
|
||||
uid,
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
// Then add the new one
|
||||
let task = TimerTask {
|
||||
task_id: tid,
|
||||
interval_minutes: interval,
|
||||
last_run: chrono::Local::now().timestamp(),
|
||||
};
|
||||
|
||||
self.timer_map.write().insert(uid.clone(), task);
|
||||
operations_to_add.push((uid, tid, interval));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Locks are dropped here
|
||||
|
||||
// Now perform async operations without holding locks
|
||||
let delay_timer = self.delay_timer.write();
|
||||
for (uid, tid, interval) in operations_to_add {
|
||||
if let Err(e) = self.add_task(&delay_timer, uid.clone(), tid, interval) {
|
||||
logging_error!(Type::Timer, "Failed to add task for uid {}: {}", uid, e);
|
||||
// Rollback on failure - remove from timer_map
|
||||
self.timer_map.write().remove(&uid);
|
||||
} else {
|
||||
logging!(debug, Type::Timer, "Added task {} for uid {}", tid, uid);
|
||||
}
|
||||
}
|
||||
let _ = self.command_tx.send(TimerCommand::Apply(new_map));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate map of profile UIDs to update intervals
|
||||
async fn gen_map(&self) -> HashMap<String, u64> {
|
||||
let mut new_map = HashMap::new();
|
||||
|
||||
@@ -243,218 +154,254 @@ impl Timer {
|
||||
&& allow_auto_update
|
||||
&& interval > 0
|
||||
{
|
||||
logging!(
|
||||
debug,
|
||||
Type::Timer,
|
||||
"找到定时更新配置: uid={}, interval={}min",
|
||||
uid,
|
||||
interval
|
||||
);
|
||||
new_map.insert(uid.clone(), interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logging!(debug, Type::Timer, "生成的定时更新配置数量: {}", new_map.len());
|
||||
new_map
|
||||
}
|
||||
|
||||
/// Generate differences between current and new timer configuration
|
||||
async fn gen_diff(&self) -> HashMap<String, DiffFlag> {
|
||||
let mut diff_map = HashMap::new();
|
||||
let new_map = self.gen_map().await;
|
||||
async fn run_scheduler(
|
||||
mut command_rx: mpsc::UnboundedReceiver<TimerCommand>,
|
||||
command_tx: mpsc::UnboundedSender<TimerCommand>,
|
||||
) {
|
||||
let mut queue = DelayQueue::new();
|
||||
let mut tasks = HashMap::new();
|
||||
|
||||
// Read lock for comparing current state
|
||||
let timer_map = self.timer_map.read();
|
||||
logging!(debug, Type::Timer, "当前 timer_map 大小: {}", timer_map.len());
|
||||
loop {
|
||||
tokio::select! {
|
||||
Some(expired) = queue.next() => {
|
||||
let uid = expired.into_inner();
|
||||
Self::run_expired_task(&mut tasks, uid, command_tx.clone());
|
||||
}
|
||||
|
||||
// Find tasks to modify or delete
|
||||
for (uid, task) in timer_map.iter() {
|
||||
match new_map.get(uid) {
|
||||
Some(&interval) if interval != task.interval_minutes => {
|
||||
// Task exists but interval changed
|
||||
logging!(
|
||||
debug,
|
||||
Type::Timer,
|
||||
"定时任务间隔变更: uid={}, 旧={}, 新={}",
|
||||
uid,
|
||||
task.interval_minutes,
|
||||
interval
|
||||
);
|
||||
diff_map.insert(uid.clone(), DiffFlag::Mod(task.task_id, interval));
|
||||
}
|
||||
None => {
|
||||
// Task no longer needed
|
||||
logging!(debug, Type::Timer, "定时任务已删除: uid={}", uid);
|
||||
diff_map.insert(uid.clone(), DiffFlag::Del(task.task_id));
|
||||
}
|
||||
_ => {
|
||||
// Task exists with same interval, no change needed
|
||||
logging!(debug, Type::Timer, "定时任务保持不变: uid={}", uid);
|
||||
command = command_rx.recv() => {
|
||||
match command {
|
||||
Some(TimerCommand::Apply(new_map)) => {
|
||||
Self::apply_timer_map(&mut queue, &mut tasks, new_map);
|
||||
}
|
||||
Some(TimerCommand::RunNow(uid)) => {
|
||||
Self::run_task_now(&mut queue, &mut tasks, uid, command_tx.clone());
|
||||
}
|
||||
Some(TimerCommand::TaskFinished(uid)) => {
|
||||
Self::finish_task(&mut queue, &mut tasks, uid);
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find new tasks to add
|
||||
let mut next_id = self.timer_count.load(Ordering::Relaxed);
|
||||
let original_id = next_id;
|
||||
|
||||
for (uid, &interval) in new_map.iter() {
|
||||
if !timer_map.contains_key(uid) {
|
||||
logging!(
|
||||
debug,
|
||||
Type::Timer,
|
||||
"新增定时任务: uid={}, interval={}min",
|
||||
uid,
|
||||
interval
|
||||
);
|
||||
diff_map.insert(uid.clone(), DiffFlag::Add(next_id, interval));
|
||||
next_id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Update counter only if we added new tasks
|
||||
if next_id > original_id {
|
||||
self.timer_count.store(next_id, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
logging!(debug, Type::Timer, "定时任务变更数量: {}", diff_map.len());
|
||||
diff_map
|
||||
}
|
||||
|
||||
/// Add a timer task with better error handling
|
||||
fn add_task(&self, delay_timer: &DelayTimer, uid: String, tid: TaskID, minutes: u64) -> Result<()> {
|
||||
fn apply_timer_map(
|
||||
queue: &mut DelayQueue<String>,
|
||||
tasks: &mut HashMap<String, TaskState>,
|
||||
new_map: HashMap<String, u64>,
|
||||
) {
|
||||
tasks.retain(|uid, state| {
|
||||
if new_map.contains_key(uid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some(key) = state.key.take() {
|
||||
queue.remove(&key);
|
||||
}
|
||||
logging!(debug, Type::Timer, "Removed timer task for uid={}", uid);
|
||||
false
|
||||
});
|
||||
|
||||
for (uid, interval_minutes) in new_map {
|
||||
let Some(state) = tasks.get_mut(&uid) else {
|
||||
Self::insert_task(queue, tasks, uid, interval_minutes);
|
||||
continue;
|
||||
};
|
||||
|
||||
if state.interval_minutes == interval_minutes {
|
||||
continue;
|
||||
}
|
||||
|
||||
Self::update_task_interval(queue, &uid, state, interval_minutes);
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_task(
|
||||
queue: &mut DelayQueue<String>,
|
||||
tasks: &mut HashMap<String, TaskState>,
|
||||
uid: String,
|
||||
interval_minutes: u64,
|
||||
) {
|
||||
let key = Self::schedule_task(queue, &uid, interval_minutes);
|
||||
logging!(
|
||||
info,
|
||||
debug,
|
||||
Type::Timer,
|
||||
"Adding task: uid={}, id={}, interval={}min",
|
||||
"Added timer task: uid={}, interval={}min",
|
||||
uid,
|
||||
tid,
|
||||
minutes
|
||||
interval_minutes
|
||||
);
|
||||
tasks.insert(uid, TaskState::new(key, interval_minutes));
|
||||
}
|
||||
|
||||
// Create a task with reasonable retries and backoff
|
||||
let task = TaskBuilder::default()
|
||||
.set_task_id(tid)
|
||||
.set_maximum_parallel_runnable_num(1)
|
||||
.set_frequency_repeated_by_minutes(minutes)
|
||||
.spawn_async_routine(move || {
|
||||
let uid = uid.clone();
|
||||
Box::pin(async move {
|
||||
Self::wait_until_resolve_done(Duration::from_millis(5000)).await;
|
||||
Self::async_task(&uid).await;
|
||||
}) as Pin<Box<dyn std::future::Future<Output = ()> + Send>>
|
||||
})
|
||||
.context("failed to create timer task")?;
|
||||
|
||||
delay_timer.add_task(task).context("failed to add timer task")?;
|
||||
|
||||
Ok(())
|
||||
fn update_task_interval(queue: &mut DelayQueue<String>, uid: &str, state: &mut TaskState, interval_minutes: u64) {
|
||||
state.interval_minutes = interval_minutes;
|
||||
|
||||
if let Some(key) = state.key.as_ref() {
|
||||
queue.reset(key, Self::interval_duration(interval_minutes));
|
||||
} else if !state.running {
|
||||
state.key = Some(Self::schedule_task(queue, uid, interval_minutes));
|
||||
}
|
||||
|
||||
logging!(
|
||||
debug,
|
||||
Type::Timer,
|
||||
"Updated timer task interval: uid={}, interval={}min",
|
||||
uid,
|
||||
interval_minutes
|
||||
);
|
||||
}
|
||||
|
||||
fn run_expired_task(
|
||||
tasks: &mut HashMap<String, TaskState>,
|
||||
uid: String,
|
||||
command_tx: mpsc::UnboundedSender<TimerCommand>,
|
||||
) {
|
||||
let Some(state) = tasks.get_mut(&uid) else {
|
||||
return;
|
||||
};
|
||||
|
||||
state.key = None;
|
||||
if !Self::mark_task_running(state, &uid, false) {
|
||||
return;
|
||||
}
|
||||
|
||||
Self::spawn_update_task(uid, command_tx);
|
||||
}
|
||||
|
||||
fn run_task_now(
|
||||
queue: &mut DelayQueue<String>,
|
||||
tasks: &mut HashMap<String, TaskState>,
|
||||
uid: String,
|
||||
command_tx: mpsc::UnboundedSender<TimerCommand>,
|
||||
) {
|
||||
let Some(state) = tasks.get_mut(&uid) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !Self::mark_task_running(state, &uid, true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(key) = state.key.take() {
|
||||
queue.remove(&key);
|
||||
}
|
||||
Self::spawn_update_task(uid, command_tx);
|
||||
}
|
||||
|
||||
fn mark_task_running(state: &mut TaskState, uid: &str, immediate: bool) -> bool {
|
||||
if !state.running {
|
||||
state.running = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if immediate {
|
||||
logging!(
|
||||
debug,
|
||||
Type::Timer,
|
||||
"Timer task already running, skip immediate uid={}",
|
||||
uid
|
||||
);
|
||||
} else {
|
||||
logging!(debug, Type::Timer, "Timer task already running, skip uid={}", uid);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn finish_task(queue: &mut DelayQueue<String>, tasks: &mut HashMap<String, TaskState>, uid: String) {
|
||||
let Some(state) = tasks.get_mut(&uid) else {
|
||||
return;
|
||||
};
|
||||
|
||||
state.running = false;
|
||||
let key = Self::schedule_task(queue, &uid, state.interval_minutes);
|
||||
state.key = Some(key);
|
||||
}
|
||||
|
||||
fn spawn_update_task(uid: String, command_tx: mpsc::UnboundedSender<TimerCommand>) {
|
||||
logging!(info, Type::Timer, "Starting timer task: uid={}", uid);
|
||||
AsyncHandler::spawn(move || async move {
|
||||
Self::wait_until_resolve_done(Duration::from_millis(5000)).await;
|
||||
Self::async_task(&uid).await;
|
||||
let _ = command_tx.send(TimerCommand::TaskFinished(uid));
|
||||
});
|
||||
}
|
||||
|
||||
const fn interval_duration(interval_minutes: u64) -> Duration {
|
||||
Duration::from_secs(interval_minutes.saturating_mul(60))
|
||||
}
|
||||
|
||||
fn schedule_task(queue: &mut DelayQueue<String>, uid: &str, interval_minutes: u64) -> Key {
|
||||
queue.insert(String::from(uid), Self::interval_duration(interval_minutes))
|
||||
}
|
||||
|
||||
/// Get next update time for a profile
|
||||
pub async fn get_next_update_time(&self, uid: &str) -> Option<i64> {
|
||||
logging!(info, Type::Timer, "获取下次更新时间,uid={}", uid);
|
||||
logging!(debug, Type::Timer, "Getting next update time, uid={}", uid);
|
||||
|
||||
// First extract timer task data without holding the lock across await
|
||||
let task_interval = {
|
||||
let timer_map = self.timer_map.read();
|
||||
match timer_map.get(uid) {
|
||||
Some(t) => t.interval_minutes,
|
||||
None => {
|
||||
logging!(warn, Type::Timer, "找不到对应的定时任务,uid={}", uid);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Get the profile updated timestamp - now safe to await
|
||||
let items = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles_guard = profiles.latest_arc();
|
||||
match profiles_guard.get_items() {
|
||||
Some(i) => i.clone(),
|
||||
None => {
|
||||
logging!(warn, Type::Timer, "获取配置列表失败");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let profile = match items.iter().find(|item| item.uid.as_deref() == Some(uid)) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
logging!(warn, Type::Timer, "找不到对应的配置,uid={}", uid);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let task_interval = *self.timer_map.read().get(uid)?;
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles_guard = profiles.latest_arc();
|
||||
let items = profiles_guard.get_items()?;
|
||||
|
||||
let profile = items.iter().find(|item| item.uid.as_deref() == Some(uid))?;
|
||||
let updated = profile.updated.unwrap_or(0) as i64;
|
||||
|
||||
// Calculate next update time
|
||||
if updated > 0 && task_interval > 0 {
|
||||
let next_time = updated + (task_interval as i64 * 60);
|
||||
logging!(info, Type::Timer, "计算得到下次更新时间: {}, uid={}", next_time, uid);
|
||||
Some(next_time)
|
||||
if updated > 0 {
|
||||
Some(updated + (task_interval as i64 * 60))
|
||||
} else {
|
||||
logging!(
|
||||
warn,
|
||||
Type::Timer,
|
||||
"更新时间或间隔无效,updated={}, interval={}",
|
||||
updated,
|
||||
task_interval
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit update events for frontend notification
|
||||
fn emit_update_event(uid: &String, is_start: bool) {
|
||||
{
|
||||
if is_start {
|
||||
super::handle::Handle::notify_profile_update_started(uid);
|
||||
} else {
|
||||
super::handle::Handle::notify_profile_update_completed(uid);
|
||||
}
|
||||
if is_start {
|
||||
super::handle::Handle::notify_profile_update_started(uid);
|
||||
} else {
|
||||
super::handle::Handle::notify_profile_update_completed(uid);
|
||||
}
|
||||
}
|
||||
|
||||
/// Async task with better error handling and logging
|
||||
async fn async_task(uid: &String) {
|
||||
let task_start = std::time::Instant::now();
|
||||
logging!(info, Type::Timer, "Running timer task for profile: {}", uid);
|
||||
logging!(debug, Type::Timer, "Running timer task for profile: {}", uid);
|
||||
|
||||
match tokio::time::timeout(std::time::Duration::from_secs(40), async {
|
||||
Self::emit_update_event(uid, true);
|
||||
|
||||
let is_current = Config::profiles().await.latest_arc().current.as_ref() == Some(uid);
|
||||
logging!(info, Type::Timer, "配置 {} 是否为当前激活配置: {}", uid, is_current);
|
||||
logging!(
|
||||
debug,
|
||||
Type::Timer,
|
||||
"Profile {} is current active profile: {}",
|
||||
uid,
|
||||
is_current
|
||||
);
|
||||
|
||||
feat::update_profile(uid, None, is_current, false, false).await
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(result) => match result {
|
||||
Ok(_) => {
|
||||
let duration = task_start.elapsed().as_millis();
|
||||
logging!(
|
||||
info,
|
||||
Type::Timer,
|
||||
"Timer task completed successfully for uid: {} (took {}ms)",
|
||||
uid,
|
||||
duration
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
logging_error!(Type::Timer, "Failed to update profile uid {}: {}", uid, e);
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
logging_error!(Type::Timer, "Timer task timed out for uid: {}", uid);
|
||||
Ok(Ok(_)) => {
|
||||
logging!(
|
||||
info,
|
||||
Type::Timer,
|
||||
"Timer task completed for uid: {} (took {}ms)",
|
||||
uid,
|
||||
task_start.elapsed().as_millis()
|
||||
);
|
||||
handle::Handle::notify_profile_update_completed(uid);
|
||||
}
|
||||
Ok(Err(e)) => logging_error!(Type::Timer, "Failed to update profile uid {}: {}", uid, e),
|
||||
Err(_) => logging_error!(Type::Timer, "Timer task timed out for uid: {}", uid),
|
||||
}
|
||||
|
||||
// Emit completed event
|
||||
Self::emit_update_event(uid, false);
|
||||
}
|
||||
|
||||
@@ -468,10 +415,3 @@ impl Timer {
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DiffFlag {
|
||||
Del(TaskID),
|
||||
Add(TaskID, u64),
|
||||
Mod(TaskID, u64),
|
||||
}
|
||||
|
||||
@@ -4,15 +4,15 @@ use crate::{
|
||||
process::AsyncHandler,
|
||||
};
|
||||
|
||||
use clash_verge_logging::{Type, logging, logging_error};
|
||||
use clash_verge_logging::{Type, logging};
|
||||
|
||||
use crate::utils::window_manager::WindowManager;
|
||||
use anyhow::{Context as _, Result};
|
||||
use delay_timer::prelude::TaskBuilder;
|
||||
use anyhow::Result;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::atomic::{AtomicU8, AtomicU32, Ordering};
|
||||
use tauri::Listener as _;
|
||||
|
||||
const LIGHT_WEIGHT_TASK_UID: &str = "light_weight_task";
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::time::{Duration, sleep};
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -43,6 +43,8 @@ static LIGHTWEIGHT_STATE: AtomicU8 = AtomicU8::new(LightweightState::Normal as u
|
||||
static WINDOW_CLOSE_HANDLER_ID: AtomicU32 = AtomicU32::new(0);
|
||||
static WEBVIEW_FOCUS_HANDLER_ID: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
static CANCEL_TX: Mutex<Option<oneshot::Sender<()>>> = Mutex::new(None);
|
||||
|
||||
#[inline]
|
||||
fn get_state() -> LightweightState {
|
||||
LIGHTWEIGHT_STATE.load(Ordering::Acquire).into()
|
||||
@@ -101,7 +103,7 @@ pub async fn enable_auto_light_weight_mode() {
|
||||
|
||||
pub fn disable_auto_light_weight_mode() {
|
||||
logging!(info, Type::Lightweight, "关闭自动轻量模式");
|
||||
let _ = cancel_light_weight_timer();
|
||||
cancel_light_weight_timer();
|
||||
cancel_window_close_listener();
|
||||
cancel_webview_focus_listener();
|
||||
}
|
||||
@@ -114,7 +116,7 @@ pub async fn entry_lightweight_mode() -> bool {
|
||||
}
|
||||
record_state_and_log(LightweightState::In);
|
||||
WindowManager::destroy_main_window();
|
||||
let _ = cancel_light_weight_timer();
|
||||
cancel_light_weight_timer();
|
||||
refresh_lightweight_tray_state().await;
|
||||
true
|
||||
}
|
||||
@@ -140,7 +142,7 @@ pub async fn exit_lightweight_mode() -> bool {
|
||||
setup_window_close_listener();
|
||||
setup_webview_focus_listener();
|
||||
}
|
||||
let _ = cancel_light_weight_timer();
|
||||
cancel_light_weight_timer();
|
||||
record_state_and_log(LightweightState::Normal);
|
||||
refresh_lightweight_tray_state().await;
|
||||
true
|
||||
@@ -148,7 +150,7 @@ pub async fn exit_lightweight_mode() -> bool {
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub async fn add_light_weight_timer() {
|
||||
logging_error!(Type::Lightweight, setup_light_weight_timer().await);
|
||||
setup_light_weight_timer().await;
|
||||
}
|
||||
|
||||
fn setup_window_close_listener() {
|
||||
@@ -160,13 +162,7 @@ fn setup_window_close_listener() {
|
||||
}
|
||||
let handler_id = window.listen("tauri://close-requested", move |_event| {
|
||||
std::mem::drop(AsyncHandler::spawn(|| async {
|
||||
if let Err(e) = setup_light_weight_timer().await {
|
||||
logging!(
|
||||
warn,
|
||||
Type::Lightweight,
|
||||
"Warning: Failed to setup light weight timer: {e}"
|
||||
);
|
||||
}
|
||||
setup_light_weight_timer().await;
|
||||
}));
|
||||
logging!(info, Type::Lightweight, "监听到关闭请求,开始轻量模式计时");
|
||||
});
|
||||
@@ -192,7 +188,7 @@ fn setup_webview_focus_listener() {
|
||||
logging!(debug, Type::Lightweight, "覆盖旧的窗口焦点监听");
|
||||
}
|
||||
let handler_id = window.listen("tauri://focus", move |_event| {
|
||||
logging_error!(Type::Lightweight, cancel_light_weight_timer());
|
||||
cancel_light_weight_timer();
|
||||
logging!(debug, Type::Lightweight, "监听到窗口获得焦点,取消轻量模式计时");
|
||||
});
|
||||
WEBVIEW_FOCUS_HANDLER_ID.store(handler_id, Ordering::Release);
|
||||
@@ -209,72 +205,51 @@ fn cancel_webview_focus_listener() {
|
||||
}
|
||||
}
|
||||
|
||||
async fn setup_light_weight_timer() -> Result<()> {
|
||||
if let Err(e) = Timer::global().init().await {
|
||||
return Err(e).context("failed to initialize timer");
|
||||
}
|
||||
|
||||
async fn setup_light_weight_timer() {
|
||||
let once_by_minutes = Config::verge().await.data_arc().auto_light_weight_minutes.unwrap_or(10);
|
||||
|
||||
{
|
||||
let timer_map = Timer::global().timer_map.read();
|
||||
if timer_map.contains_key(LIGHT_WEIGHT_TASK_UID) {
|
||||
logging!(debug, Type::Timer, "轻量模式计时器已存在,跳过创建");
|
||||
return Ok(());
|
||||
let mut cancel_tx_guard = CANCEL_TX.lock();
|
||||
if cancel_tx_guard.is_some() {
|
||||
logging!(
|
||||
debug,
|
||||
Type::Timer,
|
||||
"Lightweight mode timer already exists, skipping setup"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let (tx, rx) = oneshot::channel::<()>();
|
||||
*cancel_tx_guard = Some(tx);
|
||||
drop(cancel_tx_guard);
|
||||
|
||||
AsyncHandler::spawn(move || async move {
|
||||
tokio::select! {
|
||||
_ = sleep(Duration::from_secs(once_by_minutes.saturating_mul(60))) => {
|
||||
logging!(info, Type::Timer, "Lightweight mode timer expired, entering lightweight mode");
|
||||
{
|
||||
let mut guard = CANCEL_TX.lock();
|
||||
*guard = None;
|
||||
}
|
||||
entry_lightweight_mode().await;
|
||||
}
|
||||
_ = rx => {
|
||||
logging!(debug, Type::Timer, "Received cancel signal, stopping lightweight mode timer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let task_id = {
|
||||
Timer::global()
|
||||
.timer_count
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
|
||||
};
|
||||
|
||||
let task = TaskBuilder::default()
|
||||
.set_task_id(task_id)
|
||||
.set_maximum_parallel_runnable_num(1)
|
||||
.set_frequency_once_by_minutes(once_by_minutes)
|
||||
.spawn_async_routine(move || async move {
|
||||
logging!(info, Type::Timer, "计时器到期,开始进入轻量模式");
|
||||
entry_lightweight_mode().await;
|
||||
})
|
||||
.context("failed to create timer task")?;
|
||||
|
||||
{
|
||||
let delay_timer = Timer::global().delay_timer.write();
|
||||
delay_timer.add_task(task).context("failed to add timer task")?;
|
||||
}
|
||||
|
||||
{
|
||||
let mut timer_map = Timer::global().timer_map.write();
|
||||
let timer_task = crate::core::timer::TimerTask {
|
||||
task_id,
|
||||
interval_minutes: once_by_minutes,
|
||||
last_run: chrono::Local::now().timestamp(),
|
||||
};
|
||||
timer_map.insert(LIGHT_WEIGHT_TASK_UID.into(), timer_task);
|
||||
}
|
||||
});
|
||||
|
||||
logging!(
|
||||
info,
|
||||
Type::Timer,
|
||||
"计时器已设置,{} 分钟后将自动进入轻量模式",
|
||||
"Lightweight mode timer set, entering lightweight mode in {} minutes",
|
||||
once_by_minutes
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cancel_light_weight_timer() -> Result<()> {
|
||||
let value = Timer::global().timer_map.write().remove(LIGHT_WEIGHT_TASK_UID);
|
||||
if let Some(task) = value {
|
||||
Timer::global()
|
||||
.delay_timer
|
||||
.write()
|
||||
.remove_task(task.task_id)
|
||||
.context("failed to remove timer task")?;
|
||||
logging!(debug, Type::Timer, "计时器已取消");
|
||||
fn cancel_light_weight_timer() {
|
||||
let mut cancel_tx_guard = CANCEL_TX.lock();
|
||||
if let Some(tx) = cancel_tx_guard.take() {
|
||||
let _ = tx.send(());
|
||||
logging!(debug, Type::Timer, "Timer cancelled");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
MenuItem,
|
||||
Typography,
|
||||
} from '@mui/material'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import { open } from '@tauri-apps/plugin-shell'
|
||||
import { useLockFn } from 'ahooks'
|
||||
import dayjs from 'dayjs'
|
||||
@@ -201,11 +202,10 @@ export const ProfileItem = (props: Props) => {
|
||||
|
||||
// 订阅定时器更新事件
|
||||
useEffect(() => {
|
||||
// 处理定时器更新事件 - 这个事件专门用于通知定时器变更
|
||||
const handleTimerUpdate = (event: Event) => {
|
||||
const source = event as CustomEvent<string> & { payload?: string }
|
||||
const updatedUid = source.detail ?? source.payload
|
||||
let disposed = false
|
||||
let unlistenTimerUpdate: (() => void) | undefined
|
||||
|
||||
listen<string>('verge://timer-updated', ({ payload: updatedUid }) => {
|
||||
// 只有当更新的是当前配置时才刷新显示
|
||||
if (updatedUid === itemData.uid && showNextUpdateRef.current) {
|
||||
debugLog(`收到定时器更新事件: uid=${updatedUid}`)
|
||||
@@ -216,17 +216,22 @@ export const ProfileItem = (props: Props) => {
|
||||
fetchNextUpdateTime(true)
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
// 只注册定时器更新事件监听
|
||||
window.addEventListener('verge://timer-updated', handleTimerUpdate)
|
||||
})
|
||||
.then((unlisten) => {
|
||||
if (disposed) {
|
||||
unlisten()
|
||||
return
|
||||
}
|
||||
unlistenTimerUpdate = unlisten
|
||||
})
|
||||
.catch(console.error)
|
||||
|
||||
return () => {
|
||||
disposed = true
|
||||
if (refreshTimeoutRef.current !== undefined) {
|
||||
clearTimeout(refreshTimeoutRef.current)
|
||||
}
|
||||
// 清理事件监听
|
||||
window.removeEventListener('verge://timer-updated', handleTimerUpdate)
|
||||
unlistenTimerUpdate?.()
|
||||
}
|
||||
}, [fetchNextUpdateTime, itemData.uid])
|
||||
|
||||
@@ -588,45 +593,49 @@ export const ProfileItem = (props: Props) => {
|
||||
|
||||
// 监听自动更新事件
|
||||
useEffect(() => {
|
||||
const handleUpdateStarted = (event: Event) => {
|
||||
const customEvent = event as CustomEvent<{ uid?: string }>
|
||||
if (customEvent.detail?.uid === itemData.uid) {
|
||||
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true }))
|
||||
}
|
||||
}
|
||||
let disposed = false
|
||||
let unlisteners: Array<() => void> = []
|
||||
|
||||
Promise.allSettled([
|
||||
listen<{ uid?: string }>('profile-update-started', ({ payload }) => {
|
||||
if (payload.uid === itemData.uid) {
|
||||
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true }))
|
||||
}
|
||||
}),
|
||||
listen<{ uid?: string }>('profile-update-completed', ({ payload }) => {
|
||||
if (payload.uid !== itemData.uid) {
|
||||
return
|
||||
}
|
||||
|
||||
const handleUpdateCompleted = (event: Event) => {
|
||||
const customEvent = event as CustomEvent<{ uid?: string }>
|
||||
if (customEvent.detail?.uid === itemData.uid) {
|
||||
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: false }))
|
||||
// 刷新 profile 数据以获取最新的 updated 时间戳
|
||||
void mutateProfiles()
|
||||
// 更新完成后刷新显示
|
||||
if (showNextUpdate) {
|
||||
if (showNextUpdateRef.current) {
|
||||
fetchNextUpdateTime()
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
]).then((results) => {
|
||||
const registeredUnlisteners = results.flatMap((result) =>
|
||||
result.status === 'fulfilled' ? [result.value] : [],
|
||||
)
|
||||
|
||||
// 注册事件监听
|
||||
window.addEventListener('profile-update-started', handleUpdateStarted)
|
||||
window.addEventListener('profile-update-completed', handleUpdateCompleted)
|
||||
if (disposed || results.some((result) => result.status === 'rejected')) {
|
||||
registeredUnlisteners.forEach((unlisten) => unlisten())
|
||||
results.forEach((result) => {
|
||||
if (result.status === 'rejected') console.error(result.reason)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
unlisteners = registeredUnlisteners
|
||||
})
|
||||
|
||||
return () => {
|
||||
// 清理事件监听
|
||||
window.removeEventListener('profile-update-started', handleUpdateStarted)
|
||||
window.removeEventListener(
|
||||
'profile-update-completed',
|
||||
handleUpdateCompleted,
|
||||
)
|
||||
disposed = true
|
||||
unlisteners.forEach((unlisten) => unlisten())
|
||||
}
|
||||
}, [
|
||||
fetchNextUpdateTime,
|
||||
itemData.uid,
|
||||
mutateProfiles,
|
||||
setLoadingCache,
|
||||
showNextUpdate,
|
||||
])
|
||||
}, [fetchNextUpdateTime, itemData.uid, mutateProfiles, setLoadingCache])
|
||||
|
||||
const handleSaveProfileDocument = useLockFn(async () => {
|
||||
const currentValue = profileDocument.value
|
||||
|
||||
Reference in New Issue
Block a user