mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
fix: a race condition when terminal resumes while signal system is restarting
This commit is contained in:
parent
e7cd66370f
commit
5bbc6c1287
11 changed files with 54 additions and 41 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -4747,6 +4747,7 @@ name = "yazi-binding"
|
|||
version = "25.9.15"
|
||||
dependencies = [
|
||||
"ansi-to-tui",
|
||||
"futures",
|
||||
"hashbrown 0.16.1",
|
||||
"mlua",
|
||||
"paste",
|
||||
|
|
@ -5087,6 +5088,7 @@ dependencies = [
|
|||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"yazi-binding",
|
||||
"yazi-config",
|
||||
"yazi-dds",
|
||||
"yazi-fs",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use crossterm::{execute, style::Print};
|
|||
use hashbrown::HashMap;
|
||||
use scopeguard::defer;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use yazi_binding::Permit;
|
||||
use yazi_config::{YAZI, opener::OpenerRule};
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::{File, FilesOp, Splatter, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}};
|
||||
|
|
@ -65,8 +66,7 @@ impl Actor for BulkRename {
|
|||
)
|
||||
.await;
|
||||
|
||||
let _permit = HIDER.acquire().await.unwrap();
|
||||
defer!(AppProxy::resume());
|
||||
let _permit = Permit::new(HIDER.acquire().await.unwrap(), AppProxy::resume());
|
||||
AppProxy::stop().await;
|
||||
|
||||
let new: Vec<_> = Local::regular(&tmp)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use anyhow::Result;
|
|||
use crossterm::{execute, terminal::{disable_raw_mode, enable_raw_mode}};
|
||||
use scopeguard::defer;
|
||||
use tokio::{io::{AsyncReadExt, stdin}, select, sync::mpsc, time};
|
||||
use yazi_binding::Permit;
|
||||
use yazi_macro::succ;
|
||||
use yazi_parser::VoidOpt;
|
||||
use yazi_proxy::{AppProxy, HIDER};
|
||||
|
|
@ -26,7 +27,7 @@ impl Actor for Inspect {
|
|||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
let _permit = HIDER.acquire().await.unwrap();
|
||||
let _permit = Permit::new(HIDER.acquire().await.unwrap(), AppProxy::resume());
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
|
||||
let buffered = {
|
||||
|
|
@ -37,9 +38,7 @@ impl Actor for Inspect {
|
|||
task.logs.clone()
|
||||
};
|
||||
|
||||
defer!(AppProxy::resume());
|
||||
AppProxy::stop().await;
|
||||
|
||||
terminal_clear(TTY.writer()).ok();
|
||||
TTY.writer().write_all(buffered.as_bytes()).ok();
|
||||
TTY.writer().flush().ok();
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ yazi-shared = { path = "../yazi-shared", version = "25.9.15" }
|
|||
|
||||
# External dependencies
|
||||
ansi-to-tui = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
mlua = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,42 +1,51 @@
|
|||
use std::{mem, ops::Deref};
|
||||
|
||||
use futures::{FutureExt, future::BoxFuture};
|
||||
use mlua::{UserData, prelude::LuaUserDataMethods};
|
||||
use tokio::sync::SemaphorePermit;
|
||||
|
||||
pub type PermitRef<F> = mlua::UserDataRef<Permit<F>>;
|
||||
pub type PermitRef = mlua::UserDataRef<Permit>;
|
||||
|
||||
pub struct Permit<F: FnOnce()> {
|
||||
pub struct Permit {
|
||||
inner: Option<SemaphorePermit<'static>>,
|
||||
destruct: Option<F>,
|
||||
destruct: Option<BoxFuture<'static, ()>>,
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> Deref for Permit<F> {
|
||||
impl Deref for Permit {
|
||||
type Target = Option<SemaphorePermit<'static>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> Permit<F> {
|
||||
pub fn new(inner: SemaphorePermit<'static>, f: F) -> Self {
|
||||
Self { inner: Some(inner), destruct: Some(f) }
|
||||
impl Permit {
|
||||
pub fn new<F>(inner: SemaphorePermit<'static>, f: F) -> Self
|
||||
where
|
||||
F: Future<Output = ()> + 'static + Send,
|
||||
{
|
||||
Self { inner: Some(inner), destruct: Some(f.boxed()) }
|
||||
}
|
||||
|
||||
fn dropping(&mut self) {
|
||||
if let Some(f) = self.destruct.take() {
|
||||
f();
|
||||
}
|
||||
if let Some(p) = self.inner.take() {
|
||||
mem::drop(p);
|
||||
fn dropping(&mut self) -> impl Future<Output = ()> + 'static {
|
||||
let inner = self.inner.take();
|
||||
let destruct = self.destruct.take();
|
||||
|
||||
async move {
|
||||
if let Some(f) = destruct {
|
||||
f.await;
|
||||
}
|
||||
if let Some(p) = inner {
|
||||
mem::drop(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> Drop for Permit<F> {
|
||||
fn drop(&mut self) { self.dropping(); }
|
||||
impl Drop for Permit {
|
||||
fn drop(&mut self) { tokio::spawn(self.dropping()); }
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> UserData for Permit<F> {
|
||||
impl UserData for Permit {
|
||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method_mut("drop", |_, me, ()| Ok(me.dropping()));
|
||||
methods.add_async_method_mut("drop", |_, mut me, ()| async move { Ok(me.dropping().await) });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ impl Signals {
|
|||
}
|
||||
});
|
||||
}
|
||||
SIGCONT if HIDER.try_acquire().is_ok() => AppProxy::resume(),
|
||||
SIGCONT if HIDER.try_acquire().is_ok() => _ = tokio::spawn(AppProxy::resume()),
|
||||
_ => {}
|
||||
}
|
||||
true
|
||||
|
|
|
|||
|
|
@ -48,14 +48,14 @@ pub(super) fn hide(lua: &Lua) -> mlua::Result<Value> {
|
|||
return Err("Cannot call `ui.hide()` during app initialization".into_lua_err());
|
||||
}
|
||||
|
||||
if lua.named_registry_value::<PermitRef<fn()>>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) {
|
||||
if lua.named_registry_value::<PermitRef>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) {
|
||||
return Err("Cannot hide while already hidden".into_lua_err());
|
||||
}
|
||||
|
||||
let permit = HIDER.acquire().await.unwrap();
|
||||
AppProxy::stop().await;
|
||||
|
||||
lua.set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume as fn()))?;
|
||||
lua.set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume()))?;
|
||||
lua.named_registry_value::<AnyUserData>("HIDE_PERMIT")
|
||||
})?;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,14 +35,14 @@ impl Utils {
|
|||
lua.create_async_function(|lua, ()| async move {
|
||||
deprecate!(lua, "`ya.hide()` is deprecated, use `ui.hide()` instead, in your {}\nSee #2939 for more details: https://github.com/sxyazi/yazi/pull/2939");
|
||||
|
||||
if lua.named_registry_value::<PermitRef<fn()>>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) {
|
||||
if lua.named_registry_value::<PermitRef>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) {
|
||||
return Err("Cannot hide while already hidden".into_lua_err());
|
||||
}
|
||||
|
||||
let permit = HIDER.acquire().await.unwrap();
|
||||
AppProxy::stop().await;
|
||||
|
||||
lua.set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume as fn()))?;
|
||||
lua.set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume()))?;
|
||||
lua.named_registry_value::<AnyUserData>("HIDE_PERMIT")
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,10 @@ impl AppProxy {
|
|||
rx.await.ok();
|
||||
}
|
||||
|
||||
pub fn resume() {
|
||||
emit!(Call(relay!(app:resume)));
|
||||
pub async fn resume() {
|
||||
let (tx, rx) = oneshot::channel::<()>();
|
||||
emit!(Call(relay!(app:resume).with_any("tx", tx)));
|
||||
rx.await.ok();
|
||||
}
|
||||
|
||||
pub fn notify(opt: NotifyOpt) {
|
||||
|
|
|
|||
|
|
@ -12,15 +12,16 @@ repository = "https://github.com/sxyazi/yazi"
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
yazi-config = { path = "../yazi-config", version = "25.9.15" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.9.15" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.9.15" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }
|
||||
yazi-parser = { path = "../yazi-parser", version = "25.9.15" }
|
||||
yazi-plugin = { path = "../yazi-plugin", version = "25.9.15" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.9.15" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.9.15" }
|
||||
yazi-vfs = { path = "../yazi-vfs", version = "25.9.15" }
|
||||
yazi-binding = { path = "../yazi-binding", version = "25.9.15" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.9.15" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.9.15" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.9.15" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.9.15" }
|
||||
yazi-parser = { path = "../yazi-parser", version = "25.9.15" }
|
||||
yazi-plugin = { path = "../yazi-plugin", version = "25.9.15" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.9.15" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.9.15" }
|
||||
yazi-vfs = { path = "../yazi-vfs", version = "25.9.15" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use anyhow::{Result, anyhow};
|
||||
use scopeguard::defer;
|
||||
use tokio::{io::{AsyncBufReadExt, BufReader}, select, sync::mpsc};
|
||||
use yazi_binding::Permit;
|
||||
use yazi_proxy::{AppProxy, HIDER};
|
||||
|
||||
use super::{ProcessInBg, ProcessInBlock, ProcessInOrphan, ShellOpt};
|
||||
|
|
@ -14,8 +14,7 @@ impl Process {
|
|||
pub(crate) fn new(ops: &mpsc::UnboundedSender<TaskOp>) -> Self { Self { ops: ops.into() } }
|
||||
|
||||
pub(crate) async fn block(&self, task: ProcessInBlock) -> Result<(), ProcessOutBlock> {
|
||||
let _permit = HIDER.acquire().await.unwrap();
|
||||
defer!(AppProxy::resume());
|
||||
let _permit = Permit::new(HIDER.acquire().await.unwrap(), AppProxy::resume());
|
||||
AppProxy::stop().await;
|
||||
|
||||
let (id, cmd) = (task.id, task.cmd.clone());
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue