mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
perf: make preload tasks discardable
This commit is contained in:
parent
aae3f9ea4a
commit
0698a7fe28
13 changed files with 79 additions and 30 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -1308,9 +1308,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.172"
|
version = "0.2.173"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
|
|
@ -3735,6 +3735,7 @@ dependencies = [
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"trash",
|
"trash",
|
||||||
"yazi-config",
|
"yazi-config",
|
||||||
|
|
@ -3752,6 +3753,7 @@ version = "25.6.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"crossterm 0.29.0",
|
"crossterm 0.29.0",
|
||||||
|
"foldhash",
|
||||||
"futures",
|
"futures",
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ foldhash = "0.1.5"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
globset = "0.4.16"
|
globset = "0.4.16"
|
||||||
indexmap = { version = "2.9.0", features = [ "serde" ] }
|
indexmap = { version = "2.9.0", features = [ "serde" ] }
|
||||||
libc = "0.2.172"
|
libc = "0.2.173"
|
||||||
lru = "0.14.0"
|
lru = "0.14.0"
|
||||||
mlua = { version = "0.10.5", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serialize" ] }
|
mlua = { version = "0.10.5", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serialize" ] }
|
||||||
objc = "0.2.7"
|
objc = "0.2.7"
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ impl Watcher {
|
||||||
|
|
||||||
async fn fan_out(rx: UnboundedReceiver<Url>) {
|
async fn fan_out(rx: UnboundedReceiver<Url>) {
|
||||||
// TODO: revert this once a new notification is implemented
|
// TODO: revert this once a new notification is implemented
|
||||||
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(350));
|
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(250));
|
||||||
pin!(rx);
|
pin!(rx);
|
||||||
|
|
||||||
while let Some(chunk) = rx.next().await {
|
while let Some(chunk) = rx.next().await {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ impl Tasks {
|
||||||
let mut loaded = self.scheduler.prework.loaded.lock();
|
let mut loaded = self.scheduler.prework.loaded.lock();
|
||||||
let mut tasks: [Vec<_>; MAX_PREWORKERS as usize] = Default::default();
|
let mut tasks: [Vec<_>; MAX_PREWORKERS as usize] = Default::default();
|
||||||
for f in paged {
|
for f in paged {
|
||||||
let hash = f.hash();
|
let hash = f.hash_u64();
|
||||||
for g in YAZI.plugin.fetchers(&f.url, mimetype.by_file(f).unwrap_or_default()) {
|
for g in YAZI.plugin.fetchers(&f.url, mimetype.by_file(f).unwrap_or_default()) {
|
||||||
match loaded.get_mut(&hash) {
|
match loaded.get_mut(&hash) {
|
||||||
Some(n) if *n & (1 << g.idx) != 0 => continue,
|
Some(n) if *n & (1 << g.idx) != 0 => continue,
|
||||||
|
|
@ -31,7 +31,7 @@ impl Tasks {
|
||||||
pub fn preload_paged(&self, paged: &[File], mimetype: &Mimetype) {
|
pub fn preload_paged(&self, paged: &[File], mimetype: &Mimetype) {
|
||||||
let mut loaded = self.scheduler.prework.loaded.lock();
|
let mut loaded = self.scheduler.prework.loaded.lock();
|
||||||
for f in paged {
|
for f in paged {
|
||||||
let hash = f.hash();
|
let hash = f.hash_u64();
|
||||||
for p in YAZI.plugin.preloaders(&f.url, mimetype.by_file(f).unwrap_or_default()) {
|
for p in YAZI.plugin.preloaders(&f.url, mimetype.by_file(f).unwrap_or_default()) {
|
||||||
match loaded.get_mut(&hash) {
|
match loaded.get_mut(&hash) {
|
||||||
Some(n) if *n & (1 << p.idx) != 0 => continue,
|
Some(n) if *n & (1 << p.idx) != 0 => continue,
|
||||||
|
|
@ -49,7 +49,7 @@ impl Tasks {
|
||||||
}
|
}
|
||||||
|
|
||||||
let targets: Vec<_> = {
|
let targets: Vec<_> = {
|
||||||
let loading = self.scheduler.prework.size_loading.read();
|
let loading = self.scheduler.prework.sizing.read();
|
||||||
targets
|
targets
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|f| f.is_dir() && !targets.sizes.contains_key(f.urn()) && !loading.contains(&f.url))
|
.filter(|f| f.is_dir() && !targets.sizes.contains_key(f.urn()) && !loading.contains(&f.url))
|
||||||
|
|
@ -60,7 +60,7 @@ impl Tasks {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut loading = self.scheduler.prework.size_loading.write();
|
let mut loading = self.scheduler.prework.sizing.write();
|
||||||
for &target in &targets {
|
for &target in &targets {
|
||||||
loading.insert(target.clone());
|
loading.insert(target.clone());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ impl File {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hash(&self) -> u64 {
|
pub fn hash_u64(&self) -> u64 {
|
||||||
let mut h = foldhash::fast::FixedState::default().build_hasher();
|
let mut h = foldhash::fast::FixedState::default().build_hasher();
|
||||||
self.url.hash(&mut h);
|
self.url.hash(&mut h);
|
||||||
h.write_u8(0);
|
h.write_u8(0);
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ macro_rules! impl_file_fields {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_file_methods {
|
macro_rules! impl_file_methods {
|
||||||
($methods:ident) => {
|
($methods:ident) => {
|
||||||
$methods.add_method("hash", |_, me, ()| Ok(me.hash()));
|
$methods.add_method("hash", |_, me, ()| Ok(me.hash_u64()));
|
||||||
|
|
||||||
$methods.add_method("icon", |_, me, ()| {
|
$methods.add_method("icon", |_, me, ()| {
|
||||||
use yazi_shared::theme::IconCache;
|
use yazi_shared::theme::IconCache;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ lru = { workspace = true }
|
||||||
parking_lot = { workspace = true }
|
parking_lot = { workspace = true }
|
||||||
scopeguard = { workspace = true }
|
scopeguard = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
tokio-util = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
||||||
[target."cfg(unix)".dependencies]
|
[target."cfg(unix)".dependencies]
|
||||||
|
|
|
||||||
42
yazi-scheduler/src/hooks.rs
Normal file
42
yazi-scheduler/src/hooks.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use futures::future::BoxFuture;
|
||||||
|
use yazi_shared::Id;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(super) struct Hooks {
|
||||||
|
inner: HashMap<Id, Hook>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hooks {
|
||||||
|
pub(super) fn add_sync<F>(&mut self, id: Id, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(bool) + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
self.inner.insert(id, Hook::Sync(Box::new(f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_async<F>(&mut self, id: Id, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(bool) -> BoxFuture<'static, ()> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
self.inner.insert(id, Hook::Async(Box::new(f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn run_or_pop(&mut self, id: Id, cancel: bool) -> Option<BoxFuture<'static, ()>> {
|
||||||
|
match self.inner.remove(&id)? {
|
||||||
|
Hook::Sync(f) => {
|
||||||
|
f(cancel);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Hook::Async(f) => Some(f(cancel)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Hook
|
||||||
|
// TODO: remove Send + Sync bounds when not needed
|
||||||
|
pub(super) enum Hook {
|
||||||
|
Sync(Box<dyn FnOnce(bool) + Send + Sync>),
|
||||||
|
Async(Box<dyn (FnOnce(bool) -> BoxFuture<'static, ()>) + Send + Sync>),
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
yazi_macro::mod_pub!(file plugin prework process);
|
yazi_macro::mod_pub!(file plugin prework process);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(ongoing out r#in scheduler task);
|
yazi_macro::mod_flat!(hooks ongoing out r#in scheduler task);
|
||||||
|
|
||||||
const LOW: u8 = yazi_config::Priority::Low as u8;
|
const LOW: u8 = yazi_config::Priority::Low as u8;
|
||||||
const NORMAL: u8 = yazi_config::Priority::Normal as u8;
|
const NORMAL: u8 = yazi_config::Priority::Normal as u8;
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ use yazi_config::YAZI;
|
||||||
use yazi_shared::{Id, Ids};
|
use yazi_shared::{Id, Ids};
|
||||||
|
|
||||||
use super::{Task, TaskStage};
|
use super::{Task, TaskStage};
|
||||||
use crate::TaskKind;
|
use crate::{Hooks, TaskKind};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Ongoing {
|
pub struct Ongoing {
|
||||||
pub(super) hooks: HashMap<Id, Box<dyn (FnOnce(bool) -> BoxFuture<'static, ()>) + Send + Sync>>,
|
pub(super) hooks: Hooks,
|
||||||
pub(super) all: HashMap<Id, Task>,
|
pub(super) all: HashMap<Id, Task>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,8 +67,8 @@ impl Ongoing {
|
||||||
if task.succ < task.total {
|
if task.succ < task.total {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if let Some(hook) = self.hooks.remove(&id) {
|
if let Some(fut) = self.hooks.run_or_pop(id, false) {
|
||||||
return Some(hook(false));
|
return Some(fut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TaskStage::Hooked => {}
|
TaskStage::Hooked => {}
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ impl Scheduler {
|
||||||
pub fn cancel(&self, id: Id) -> bool {
|
pub fn cancel(&self, id: Id) -> bool {
|
||||||
let mut ongoing = self.ongoing.lock();
|
let mut ongoing = self.ongoing.lock();
|
||||||
|
|
||||||
if let Some(hook) = ongoing.hooks.remove(&id) {
|
if let Some(fut) = ongoing.hooks.run_or_pop(id, true) {
|
||||||
self.micro.try_send(hook(true), HIGH).ok();
|
self.micro.try_send(fut, HIGH).ok();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,11 +81,11 @@ impl Scheduler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ongoing.hooks.insert(id, {
|
ongoing.hooks.add_async(id, {
|
||||||
let ongoing = self.ongoing.clone();
|
let ongoing = self.ongoing.clone();
|
||||||
let (from, to) = (from.clone(), to.clone());
|
let (from, to) = (from.clone(), to.clone());
|
||||||
|
|
||||||
Box::new(move |canceled: bool| {
|
move |canceled: bool| {
|
||||||
async move {
|
async move {
|
||||||
if !canceled {
|
if !canceled {
|
||||||
remove_dir_clean(&from).await;
|
remove_dir_clean(&from).await;
|
||||||
|
|
@ -94,7 +94,7 @@ impl Scheduler {
|
||||||
ongoing.lock().try_remove(id, TaskStage::Hooked);
|
ongoing.lock().try_remove(id, TaskStage::Hooked);
|
||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
})
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let file = self.file.clone();
|
let file = self.file.clone();
|
||||||
|
|
@ -158,11 +158,11 @@ impl Scheduler {
|
||||||
let mut ongoing = self.ongoing.lock();
|
let mut ongoing = self.ongoing.lock();
|
||||||
let id = ongoing.add(TaskKind::User, format!("Delete {target}"));
|
let id = ongoing.add(TaskKind::User, format!("Delete {target}"));
|
||||||
|
|
||||||
ongoing.hooks.insert(id, {
|
ongoing.hooks.add_async(id, {
|
||||||
let target = target.clone();
|
let target = target.clone();
|
||||||
let ongoing = self.ongoing.clone();
|
let ongoing = self.ongoing.clone();
|
||||||
|
|
||||||
Box::new(move |canceled: bool| {
|
move |canceled: bool| {
|
||||||
async move {
|
async move {
|
||||||
if !canceled {
|
if !canceled {
|
||||||
fs::remove_dir_all(&target).await.ok();
|
fs::remove_dir_all(&target).await.ok();
|
||||||
|
|
@ -172,7 +172,7 @@ impl Scheduler {
|
||||||
ongoing.lock().try_remove(id, TaskStage::Hooked);
|
ongoing.lock().try_remove(id, TaskStage::Hooked);
|
||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
})
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let file = self.file.clone();
|
let file = self.file.clone();
|
||||||
|
|
@ -187,11 +187,11 @@ impl Scheduler {
|
||||||
let mut ongoing = self.ongoing.lock();
|
let mut ongoing = self.ongoing.lock();
|
||||||
let id = ongoing.add(TaskKind::User, format!("Trash {target}"));
|
let id = ongoing.add(TaskKind::User, format!("Trash {target}"));
|
||||||
|
|
||||||
ongoing.hooks.insert(id, {
|
ongoing.hooks.add_async(id, {
|
||||||
let target = target.clone();
|
let target = target.clone();
|
||||||
let ongoing = self.ongoing.clone();
|
let ongoing = self.ongoing.clone();
|
||||||
|
|
||||||
Box::new(move |canceled: bool| {
|
move |canceled: bool| {
|
||||||
async move {
|
async move {
|
||||||
if !canceled {
|
if !canceled {
|
||||||
MgrProxy::update_tasks(&target);
|
MgrProxy::update_tasks(&target);
|
||||||
|
|
@ -200,7 +200,7 @@ impl Scheduler {
|
||||||
ongoing.lock().try_remove(id, TaskStage::Hooked);
|
ongoing.lock().try_remove(id, TaskStage::Hooked);
|
||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
})
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let file = self.file.clone();
|
let file = self.file.clone();
|
||||||
|
|
@ -275,9 +275,9 @@ impl Scheduler {
|
||||||
let mut ongoing = self.ongoing.lock();
|
let mut ongoing = self.ongoing.lock();
|
||||||
|
|
||||||
let id = ongoing.add(TaskKind::User, name);
|
let id = ongoing.add(TaskKind::User, name);
|
||||||
ongoing.hooks.insert(id, {
|
ongoing.hooks.add_async(id, {
|
||||||
let ongoing = self.ongoing.clone();
|
let ongoing = self.ongoing.clone();
|
||||||
Box::new(move |canceled: bool| {
|
move |canceled: bool| {
|
||||||
async move {
|
async move {
|
||||||
if canceled {
|
if canceled {
|
||||||
cancel_tx.send(()).await.ok();
|
cancel_tx.send(()).await.ok();
|
||||||
|
|
@ -289,7 +289,7 @@ impl Scheduler {
|
||||||
ongoing.lock().try_remove(id, TaskStage::Hooked);
|
ongoing.lock().try_remove(id, TaskStage::Hooked);
|
||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
})
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let cmd = OsString::from(&opener.run);
|
let cmd = OsString::from(&opener.run);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ yazi-macro = { path = "../yazi-macro", version = "25.6.11" }
|
||||||
# External dependencies
|
# External dependencies
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
crossterm = { workspace = true }
|
crossterm = { workspace = true }
|
||||||
|
foldhash = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
memchr = "2.7.5"
|
memchr = "2.7.5"
|
||||||
parking_lot = { workspace = true }
|
parking_lot = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{ffi::OsStr, fmt::{Debug, Display, Formatter}, hash::{Hash, Hasher}, ops::Deref, path::{Path, PathBuf}};
|
use std::{ffi::OsStr, fmt::{Debug, Display, Formatter}, hash::{BuildHasher, Hash, Hasher}, ops::Deref, path::{Path, PathBuf}};
|
||||||
|
|
||||||
use percent_encoding::{AsciiSet, CONTROLS, percent_decode_str, percent_encode};
|
use percent_encoding::{AsciiSet, CONTROLS, percent_decode_str, percent_encode};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -171,6 +171,9 @@ impl Url {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn pair(&self) -> Option<(Self, UrnBuf)> { Some((self.parent_url()?, self.loc.urn_owned())) }
|
pub fn pair(&self) -> Option<(Self, UrnBuf)> { Some((self.parent_url()?, self.loc.urn_owned())) }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn hash_u64(&self) -> u64 { foldhash::fast::FixedState::default().hash_one(self) }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn rebase(&self, parent: &Path) -> Self {
|
pub fn rebase(&self, parent: &Path) -> Self {
|
||||||
debug_assert!(self.is_regular());
|
debug_assert!(self.is_regular());
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue