Conversation
📝 WalkthroughWalkthroughRefactors lock usage to avoid deadlocks: perspective_instance.rs minimizes lock scope and moves async work outside critical sections; prolog_service/mod.rs changes engine update to perform loads and state updates while holding the write lock. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~70 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
rust-executor/src/perspectives/perspective_instance.rs (1)
1196-1213: Avoid constructing an unused LinkExpression.
The match currently builds aLinkExpressiononly to discard it. This extra conversion/cloning is avoidable since only the status is used.♻️ Proposed tweak
- let (_link, link_status) = match decorated_link_option { - Some(decorated) => { - let status = decorated.status.clone().unwrap_or(LinkStatus::Local); - (LinkExpression::from(decorated), status) - } + let link_status = match decorated_link_option { + Some(decorated) => decorated.status.clone().unwrap_or(LinkStatus::Local), None => { return Err(AnyError::msg(format!( "NH [{}] ({}) Link not found in perspective \"{}\": {:?}", handle .shared_url .clone() .unwrap_or("not-shared".to_string()), handle.name.clone().unwrap_or("<no name>".to_string()), handle.uuid, old_link ))) } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@rust-executor/src/perspectives/perspective_instance.rs` around lines 1196 - 1213, The match arm is constructing a LinkExpression via LinkExpression::from(decorated) only to discard it; instead, avoid that unnecessary conversion and cloning by extracting the status directly from decorated (use decorated.status.clone().unwrap_or(LinkStatus::Local)) and return (or assign) just the status; update the Some(decorated) arm in the decorated_link_option match to produce only the status (remove LinkExpression::from call) and adjust downstream usage of (_link, link_status) or replace tuple with a single link_status variable if the unused _link can be removed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@rust-executor/src/perspectives/perspective_instance.rs`:
- Around line 1196-1213: The match arm is constructing a LinkExpression via
LinkExpression::from(decorated) only to discard it; instead, avoid that
unnecessary conversion and cloning by extracting the status directly from
decorated (use decorated.status.clone().unwrap_or(LinkStatus::Local)) and return
(or assign) just the status; update the Some(decorated) arm in the
decorated_link_option match to produce only the status (remove
LinkExpression::from call) and adjust downstream usage of (_link, link_status)
or replace tuple with a single link_status variable if the unused _link can be
removed.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
rust-executor/src/prolog_service/mod.rs (1)
236-239: Unnecessary placeholder allocation when engine already exists.When
engine_existsistrue, twoPrologEngine::new()instances are created at line 238 but never inserted or used — they're silently dropped at theEntry::Occupiedbranch (line 282). This is wasted allocation on every dirty-update of an existing engine.♻️ Suggested refactor: use Option to avoid unnecessary allocation
- let (query_engine, subscription_engine) = if !engine_exists { + let new_engines = if !engine_exists { let mut qe = PrologEngine::new(); qe.spawn().await?; // Expensive async operation - no lock held let mut se = PrologEngine::new(); se.spawn().await?; // Expensive async operation - no lock held - (qe, se) + Some((qe, se)) } else { - // Engines exist, we'll update them - create placeholders for now - (PrologEngine::new(), PrologEngine::new()) + None };Then adjust the
Entryblock:match engines.entry(engine_key.clone()) { Entry::Vacant(entry) => { - // Insert new engines - entry.insert(SimpleEngine { - query_engine, - subscription_engine, - dirty: true, - current_links: Vec::new(), - current_sdna_links: None, - }); + if let Some((qe, se)) = new_engines { + entry.insert(SimpleEngine { + query_engine: qe, + subscription_engine: se, + dirty: true, + current_links: Vec::new(), + current_sdna_links: None, + }); + } else { + // engine_exists was true but entry is vacant - should not happen + return Err(Error::msg("Prolog engine state inconsistency")); + } } Entry::Occupied(_) => { - // Engine was inserted concurrently; clean up the unused spawned engines - // (they will be dropped automatically, no explicit stop needed) + // Engine already exists (or was inserted concurrently). + // Any spawned engines in new_engines will be dropped automatically. } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@rust-executor/src/prolog_service/mod.rs` around lines 236 - 239, The current branch creates two PrologEngine::new() placeholders even when engine_exists is true, wasting allocations; change the temporary pair to Option<(PrologEngine, PrologEngine)> (or two Options) so you only construct PrologEngine instances when needed: set the temp value to None when engine_exists is true and to Some((PrologEngine::new(), PrologEngine::new())) when false, then update the subsequent Entry handling (including Entry::Occupied and Entry::Vacant) to take/create engines from the Option only when inserting new engines rather than always allocating placeholders.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@rust-executor/src/prolog_service/mod.rs`:
- Around line 236-239: The current branch creates two PrologEngine::new()
placeholders even when engine_exists is true, wasting allocations; change the
temporary pair to Option<(PrologEngine, PrologEngine)> (or two Options) so you
only construct PrologEngine instances when needed: set the temp value to None
when engine_exists is true and to Some((PrologEngine::new(),
PrologEngine::new())) when false, then update the subsequent Entry handling
(including Entry::Occupied and Entry::Vacant) to take/create engines from the
Option only when inserting new engines rather than always allocating
placeholders.
Summary
Deadlock fixes for perspective_instance.rs and prolog_service.
Commits
Created by Data at Nico's request.
Summary by CodeRabbit
Bug Fixes
Performance
Refactor