Revert "replace try_lock() with lock_blocking() across UI thread"

This reverts commit 4225294d16.
This commit is contained in:
Kent Overstreet 2026-04-25 17:15:53 -04:00
commit 09896cd38b
28 changed files with 67 additions and 4199 deletions

View file

@ -59,7 +59,7 @@ const ACTIVITY_LINGER: std::time::Duration = std::time::Duration::from_secs(5);
impl Drop for ActivityGuard {
fn drop(&mut self) {
{ let mut st = self.agent.state.lock_blocking();
if let Ok(mut st) = self.agent.state.try_lock() {
if let Some(entry) = st.activities.iter_mut().find(|a| a.id == self.id) {
entry.label.push_str(" (complete)");
entry.expires_at = std::time::Instant::now() + ACTIVITY_LINGER;

View file

@ -152,7 +152,7 @@ async fn ensure_init(agent: Option<&std::sync::Arc<super::super::Agent>>) -> Res
let msg = format!("MCP server {} failed: {:#}", cfg.name, e);
dbglog!("{}", msg);
if let Some(a) = agent {
{ let mut st = a.state.lock_blocking();
if let Ok(mut st) = a.state.try_lock() {
st.notify(msg);
}
}

View file

@ -135,23 +135,6 @@ impl<T> TrackedMutex<T> {
location,
})
}
/// Block the current thread until the lock is acquired.
/// Safe to call from sync contexts (UI thread, slash commands) where
/// .await isn't available. Uses block_in_place so the tokio runtime
/// can schedule other tasks while we wait.
#[track_caller]
pub fn lock_blocking(&self) -> TrackedMutexGuard<'_, T> {
let location = Location::caller();
let guard = tokio::task::block_in_place(|| {
futures::executor::block_on(self.inner.lock())
});
TrackedMutexGuard {
guard,
acquired_at: Instant::now(),
location,
}
}
}
pub struct TrackedMutexGuard<'a, T> {

View file

@ -104,6 +104,6 @@ async fn run(
prior_context: render_prior_context(entries, entry_idx, 2),
timestamp_ns: node_timestamp_ns(node),
});
{ let st = agent.state.lock_blocking(); st.changed.notify_one(); }
if let Ok(st) = agent.state.try_lock() { st.changed.notify_one(); }
}
}

View file

@ -736,7 +736,7 @@ async fn run_finetune(
gen_alternates, &activity,
move |c| {
shared.lock().unwrap().finetune_candidates.push(c);
{ let st = agent.state.lock_blocking(); st.changed.notify_one(); }
if let Ok(st) = agent.state.try_lock() { st.changed.notify_one(); }
},
).await {
Ok((above_threshold, max_div)) => FinetuneScoringStats {

View file

@ -34,12 +34,12 @@ fn commands() -> Vec<SlashCommand> { vec![
handler: |s, _| { let _ = s.mind_tx.send(MindCommand::NewSession); } },
SlashCommand { name: "/save", help: "Save session to disk",
handler: |s, _| {
{ let mut ag = s.agent.state.lock_blocking(); ag.notify("saved"); }
if let Ok(mut ag) = s.agent.state.try_lock() { ag.notify("saved"); }
} },
SlashCommand { name: "/model", help: "Show/switch model (/model <name>)",
handler: |s, arg| {
if arg.is_empty() {
{ let mut ag = s.agent.state.lock_blocking();
if let Ok(mut ag) = s.agent.state.try_lock() {
let names = s.agent.app_config.model_names();
let label = if names.is_empty() {
format!("model: {}", s.agent.model())
@ -62,7 +62,7 @@ fn commands() -> Vec<SlashCommand> { vec![
SlashCommand { name: "/dmn", help: "Show DMN state",
handler: |s, _| {
let st = s.shared_mind.lock().unwrap();
{ let mut ag = s.agent.state.lock_blocking();
if let Ok(mut ag) = s.agent.state.try_lock() {
ag.notify(format!("DMN: {:?} ({}/{})", st.dmn, st.dmn_turns, st.max_dmn_turns));
}
} },
@ -71,7 +71,7 @@ fn commands() -> Vec<SlashCommand> { vec![
let mut st = s.shared_mind.lock().unwrap();
st.dmn = crate::mind::subconscious::State::Resting { since: std::time::Instant::now() };
st.dmn_turns = 0;
{ let mut ag = s.agent.state.lock_blocking(); ag.notify("DMN sleeping"); }
if let Ok(mut ag) = s.agent.state.try_lock() { ag.notify("DMN sleeping"); }
} },
SlashCommand { name: "/wake", help: "Wake DMN to foraging",
handler: |s, _| {
@ -79,14 +79,14 @@ fn commands() -> Vec<SlashCommand> { vec![
if matches!(st.dmn, crate::mind::subconscious::State::Off) { crate::mind::subconscious::set_off(false); }
st.dmn = crate::mind::subconscious::State::Foraging;
st.dmn_turns = 0;
{ let mut ag = s.agent.state.lock_blocking(); ag.notify("DMN foraging"); }
if let Ok(mut ag) = s.agent.state.try_lock() { ag.notify("DMN foraging"); }
} },
SlashCommand { name: "/pause", help: "Full stop — no autonomous ticks (Ctrl+P)",
handler: |s, _| {
let mut st = s.shared_mind.lock().unwrap();
st.dmn = crate::mind::subconscious::State::Paused;
st.dmn_turns = 0;
{ let mut ag = s.agent.state.lock_blocking(); ag.notify("DMN paused"); }
if let Ok(mut ag) = s.agent.state.try_lock() { ag.notify("DMN paused"); }
} },
SlashCommand { name: "/help", help: "Show this help",
handler: |s, _| { notify_help(&s.agent); } },
@ -116,7 +116,7 @@ pub async fn cmd_switch_model(
}
fn notify_help(agent: &std::sync::Arc<crate::agent::Agent>) {
{ let mut ag = agent.state.lock_blocking();
if let Ok(mut ag) = agent.state.try_lock() {
let mut help = String::new();
for cmd in &commands() {
help.push_str(&format!("{:12} {}\n", cmd.name, cmd.help));
@ -581,10 +581,16 @@ impl InteractScreen {
self.pending_display_count = 0;
let (generation, entries) = {
let st = self.agent.state.lock_blocking();
let st = match self.agent.state.try_lock() {
Ok(st) => st,
Err(_) => return,
};
let generation = st.generation;
drop(st);
let ctx = self.agent.context.lock_blocking();
let ctx = match self.agent.context.try_lock() {
Ok(ctx) => ctx,
Err(_) => return,
};
(generation, ctx.conversation().to_vec())
};
@ -648,7 +654,7 @@ impl InteractScreen {
if let Some(cmd) = dispatch_command(input) {
(cmd.handler)(self, &input[cmd.name.len()..].trim_start());
} else {
{ let mut ag = self.agent.state.lock_blocking();
if let Ok(mut ag) = self.agent.state.try_lock() {
ag.notify(format!("unknown: {}", input.split_whitespace().next().unwrap_or(input)));
}
}
@ -764,8 +770,9 @@ impl InteractScreen {
/// Draw the main (F1) screen — four-pane layout with status bar.
fn draw_main(&mut self, frame: &mut Frame, size: Rect, app: &App) {
// Main layout: content area + active tools overlay + status bar
let st_guard = app.agent.state.lock_blocking();
let tool_lines = st_guard.active_tools.len() as u16;
let st_guard = app.agent.state.try_lock().ok();
let tool_lines = st_guard.as_ref()
.map(|st| st.active_tools.len() as u16).unwrap_or(0);
let main_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
@ -854,9 +861,10 @@ impl InteractScreen {
frame.render_widget(gutter, input_chunks[0]);
frame.render_widget(&self.textarea, input_chunks[1]);
if !st_guard.active_tools.is_empty() {
if let Some(ref st) = st_guard {
if !st.active_tools.is_empty() {
let tool_style = Style::default().fg(Color::Yellow).add_modifier(Modifier::DIM);
let tool_text: Vec<Line> = st_guard.active_tools.iter().map(|t| {
let tool_text: Vec<Line> = st.active_tools.iter().map(|t| {
let elapsed = t.started.elapsed().as_secs();
let line = if t.detail.is_empty() {
format!(" [{}] ({}s)", t.name, elapsed)
@ -867,7 +875,7 @@ impl InteractScreen {
}).collect();
let tool_para = Paragraph::new(tool_text);
frame.render_widget(tool_para, tools_overlay_area);
}
}}
// Draw status bar with live activity indicator
let timer = if !app.activity.is_empty() {
@ -1018,7 +1026,7 @@ impl ScreenView for InteractScreen {
self.sync_from_agent();
// Read status from agent + mind state
{ let mut st = self.agent.state.lock_blocking();
if let Ok(mut st) = self.agent.state.try_lock() {
st.expire_activities();
app.status.prompt_tokens = st.last_prompt_tokens;
app.status.model = self.agent.model().to_string();
@ -1028,7 +1036,7 @@ impl ScreenView for InteractScreen {
app.activity_started = st.activities.last()
.map(|a| a.started);
}
{ let ctx = self.agent.context.lock_blocking();
if let Ok(ctx) = self.agent.context.try_lock() {
let window = crate::agent::context::context_window();
if window > 0 {
let sys = ctx.system().iter().map(|n| n.tokens()).sum::<usize>();

View file

@ -20,7 +20,10 @@ impl ConsciousScreen {
}
fn read_context_views(&self) -> Vec<SectionView> {
let ctx = self.agent.context.lock_blocking();
let ctx = match self.agent.context.try_lock() {
Ok(ctx) => ctx,
Err(_) => return Vec::new(),
};
let mut views: Vec<SectionView> = Vec::new();
@ -158,7 +161,8 @@ impl ScreenView for ConsciousScreen {
)));
lines.push(Line::raw(format!(" Reasoning: {}", app.reasoning_effort)));
lines.push(Line::raw(format!(" Running processes: {}", app.running_processes)));
let tool_count = { let st = app.agent.state.lock_blocking(); st.active_tools.len() };
let tool_count = app.agent.state.try_lock()
.map(|st| st.active_tools.len()).unwrap_or(0);
lines.push(Line::raw(format!(" Active tools: {}", tool_count)));
let block = pane_block("context")

View file

@ -292,7 +292,7 @@ async fn start(cli: crate::user::CliArgs) -> Result<()> {
}
fn hotkey_cycle_reasoning(mind: &crate::mind::Mind) {
{ let mut ag = mind.agent.state.lock_blocking();
if let Ok(mut ag) = mind.agent.state.try_lock() {
let next = match ag.reasoning_effort.as_str() {
"none" => "low",
"low" => "high",
@ -344,7 +344,7 @@ fn hotkey_cycle_autonomy(mind: &crate::mind::Mind) {
};
s.dmn_turns = 0;
drop(s);
{ let mut ag = mind.agent.state.lock_blocking();
if let Ok(mut ag) = mind.agent.state.try_lock() {
ag.notify(format!("DMN → {}", label));
}
}
@ -419,7 +419,7 @@ async fn run(
terminal.hide_cursor()?;
{ let mut ag = agent.state.lock_blocking(); ag.notify("consciousness v0.3"); }
if let Ok(mut ag) = agent.state.try_lock() { ag.notify("consciousness v0.3"); }
// Initial render
{
@ -526,7 +526,7 @@ async fn run(
}
app.walked_count = mind.subconscious_walked().await.len();
if !startup_done {
{ let mut ag = agent.state.lock_blocking();
if let Ok(mut ag) = agent.state.try_lock() {
let model = agent.model().to_string();
ag.notify(format!("model: {}", model));
startup_done = true;
@ -545,7 +545,7 @@ async fn run(
if let Some(rx_mutex) = STDERR_RX.get() {
if let Ok(rx) = rx_mutex.try_lock() {
while let Ok(line) = rx.try_recv() {
{ let mut ag = agent.state.lock_blocking();
if let Ok(mut ag) = agent.state.try_lock() {
ag.notify(format!("stderr: {}", line));
dirty = true;
}

View file

@ -222,30 +222,31 @@ impl SubconsciousScreen {
let fork_point = app.agent_state.get(self.selected())
.map(|s| s.fork_point).unwrap_or(0);
{
let ctx = agent.context.lock_blocking();
let mut views = Vec::new();
views.push(section_to_view("System", ctx.system()));
views.push(section_to_view("Identity", ctx.identity()));
views.push(section_to_view("Journal", ctx.journal()));
agent.context.try_lock().ok()
.map(|ctx| {
let mut views = Vec::new();
views.push(section_to_view("System", ctx.system()));
views.push(section_to_view("Identity", ctx.identity()));
views.push(section_to_view("Journal", ctx.journal()));
// Conversation: skip to fork point for subconscious agents
let conv = ctx.conversation();
let conv_view = section_to_view("Conversation", conv);
let fork = fork_point.min(conv_view.children.len());
let conv_children: Vec<SectionView> = conv_view.children
.into_iter().skip(fork).collect();
views.push(SectionView {
name: format!("Conversation ({} entries)", conv_children.len()),
tokens: conv_children.iter().map(|c| c.tokens).sum(),
content: String::new(),
token_ids: Vec::new(),
children: conv_children,
status: String::new(),
});
// Conversation: skip to fork point for subconscious agents
let conv = ctx.conversation();
let conv_view = section_to_view("Conversation", conv);
let fork = fork_point.min(conv_view.children.len());
let conv_children: Vec<SectionView> = conv_view.children
.into_iter().skip(fork).collect();
views.push(SectionView {
name: format!("Conversation ({} entries)", conv_children.len()),
tokens: conv_children.iter().map(|c| c.tokens).sum(),
content: String::new(),
token_ids: Vec::new(),
children: conv_children,
status: String::new(),
});
views
}
views
})
.unwrap_or_default()
}
fn draw_list(&mut self, frame: &mut Frame, area: Rect, app: &App) {

View file

@ -45,7 +45,7 @@ impl ScreenView for ThalamusScreen {
}
KeyCode::Char('t') => {
app.think_native = !app.think_native;
{ let mut st = app.agent.state.lock_blocking();
if let Ok(mut st) = app.agent.state.try_lock() {
st.think_native = app.think_native;
let status = if app.think_native { "enabled" } else { "disabled" };
st.notify(format!("native thinking {}", status));
@ -53,7 +53,7 @@ impl ScreenView for ThalamusScreen {
}
KeyCode::Char('T') => {
app.think_tool = !app.think_tool;
{ let mut st = app.agent.state.lock_blocking();
if let Ok(mut st) = app.agent.state.try_lock() {
st.think_tool = app.think_tool;
// Add or remove the think tool from the tools list
if app.think_tool {