Skip to content

Conversation

@datagutt
Copy link
Member

@datagutt datagutt commented Aug 9, 2025

Summary by CodeRabbit

  • New Features

    • Added support for monitoring and displaying modem and network interface status, including a new chat command (!bbm) to show connected modems and bitrate.
    • Introduced configurable network monitoring with customizable timeout settings.
    • Enhanced relay server and account configuration options.
  • Improvements

    • Made several configuration fields optional for greater flexibility.
    • Improved formatting and detail in status and stats messages.
    • Enhanced concurrency and state management for command handling and monitoring.
  • Documentation

    • Updated README with new configuration options and chat commands.
  • Dependency Updates

    • Upgraded and added several dependencies for improved functionality and compatibility.

@coderabbitai
Copy link

coderabbitai bot commented Aug 9, 2025

Walkthrough

This set of changes introduces substantial enhancements to modem and network interface monitoring, including new fields and message variants for tracking modem status, relay servers, and available updates. Several data structures are made more flexible by making fields optional. The command and monitoring subsystems are extended to support a new modem status command and periodic network status reporting. Documentation and dependency versions are updated accordingly.

Changes

Cohort / File(s) Change Summary
Dependency and Version Updates
Cargo.toml
Updated package version to 0.4.0. Added serde_with, upgraded strsim, tokio-tungstenite, and thiserror. Removed tracing-log.
Documentation Updates
README.md
Updated configuration example to include new monitor fields. Added new chat command for modem status.
Message and Data Model Extensions
src/belabox/messages.rs
Added new message variants and structs for relays, modems, and updates. Made several fields optional. Implemented custom deserialization for StatusKind.
Request Struct and Serialization Changes
src/belabox/requests.rs
Made multiple fields in Start optional, added new fields for relays and remote key. Updated From<Config> and tests accordingly.
WebSocket Message Handling Adjustments
src/belabox.rs
Ensured all text messages are converted to owned String. Improved error logging consistency.
Bot State and Command Handler Synchronization
src/bot.rs
Extended BelaState with modem tracking and network timeout. Introduced shared, synchronized command handler. Updated message handling for new status kinds.
Command Handler Enhancements
src/command_handler.rs
Made CommandHandler clonable. Added modem status command. Improved stats formatting and audio source assignment.
Configuration and Command Additions
src/config.rs
Added network and network_timeout to Monitor. Introduced Modems command and default chat command entry.
Monitor Logic and Concurrency
src/monitor.rs
Added mutex-protected command handler and custom interface name mapping. Implemented periodic network status reporting and custom naming logic for interfaces.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Bot
    participant CommandHandler
    participant BelaState
    participant Monitor

    User->>Bot: Sends chat command (!bbm)
    Bot->>CommandHandler: Dispatches BotCommand::Modems
    CommandHandler->>BelaState: Reads modem and interface state
    CommandHandler-->>Bot: Returns modem status string
    Bot-->>User: Replies with modem status

    Note over Monitor,CommandHandler: Periodically (if enabled)
    Monitor->>BelaState: Checks streaming and timeout
    Monitor->>CommandHandler: Gets stats (network status)
    CommandHandler-->>Monitor: Returns stats string
    Monitor-->>User: Sends periodic network status
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Poem

A bunny with code in its paws,
Hopped through the modem fields with cause.
It sniffed out relays, updates, and stats,
Gave custom names to networked cats.
Now with commands both fresh and new,
The bot keeps watch—modems in view!
🐇✨

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (1)
src/belabox/requests.rs (1)

96-115: Unit-test no longer compiles after the Option<T> transition

asrc, acodec, relay_server, relay_account, and the SRT fields are now Option<T> but the test still passes bare Strings and omits the new optional fields.

-            asrc: "No audio".to_string(),
-            acodec: "opus".to_string(),
-            remote_key: "remote_key".to_string(),
-            relay_server: "1".to_string(),
-            relay_account: "1".to_string(),
+            asrc: Some("No audio".to_string()),
+            acodec: Some("opus".to_string()),
+            remote_key: "remote_key".to_string(),
+            relay_server: Some("1".to_string()),
+            relay_account: Some("1".to_string()),
+            srt_streamid: None,
+            srtla_addr: None,
+            srtla_port: None,

Without this fix the crate will fail to compile.

🧹 Nitpick comments (8)
src/config.rs (1)

39-43: Clarify units and guardrails for network_timeout

Consider documenting that network_timeout is in seconds and validating/clamping unreasonable values (e.g., 0) at load time to prevent spammy behavior.

src/belabox.rs (3)

151-151: Redundant .into() on String

auth_request is already a String. Remove .into() for clarity.

-        if let Err(e) = write.send(TMessage::Text(auth_request.into())).await {
+        if let Err(e) = write.send(TMessage::Text(auth_request)).await {

214-217: Redundant .into() on String in keepalive

serde_json::to_string(...).unwrap() returns String. No need for .into().

-                .send(TMessage::Text(
-                    serde_json::to_string(&Request::Keepalive(None))
-                        .unwrap()
-                        .into(),
-                ))
+                .send(TMessage::Text(
+                    serde_json::to_string(&Request::Keepalive(None)).unwrap(),
+                ))

315-315: Redundant .into() on String in request sender

request.message is already a String.

-                .send(TMessage::Text(request.message.into()))
+                .send(TMessage::Text(request.message))
README.md (2)

25-29: Docs: new monitor fields captured; add explanations below

You added network and network_timeout to the example. Consider adding brief bullets in the BELABOX/monitor section explaining:

  • network: enable periodic network status messages
  • network_timeout: minimum seconds between messages while streaming

140-141: Clarify Stats vs Modems descriptions and keep examples consistent

Both rows currently claim to show “connected modems status and bitrate.” Based on code:

  • Stats reports per-interface bitrate (+ UPS), not modem status
  • Modems reports modem details (type, network, connection, signal, roaming)

Please adjust descriptions and consider adding a “Modems” entry to the commands JSON example above for consistency.

src/monitor.rs (1)

69-82: Redundant string allocation in netif_name

netif_name clones the key three times and allocates a new String even when no rename occurs.
A simple &str return (or Cow<'_, str>) would avoid the extra heap allocation on every iteration.

src/belabox/messages.rs (1)

124-148: Key matching is case-sensitive – camelCase variants are ignored

The deserializer recognises "available_updates" but not "availableUpdates", "isStreaming" etc.
If firmware switches to camelCase the message will silently fall back to the bulky Status variant.

Consider normalising the key or matching both styles.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 246248e and 9dafe40.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • Cargo.toml (2 hunks)
  • README.md (2 hunks)
  • src/belabox.rs (4 hunks)
  • src/belabox/messages.rs (8 hunks)
  • src/belabox/requests.rs (3 hunks)
  • src/bot.rs (9 hunks)
  • src/command_handler.rs (5 hunks)
  • src/config.rs (4 hunks)
  • src/monitor.rs (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/command_handler.rs (2)
src/monitor.rs (2)
  • modems (62-112)
  • network (179-199)
src/bot.rs (1)
  • new (64-111)
src/bot.rs (1)
src/config.rs (1)
  • default (46-56)
🔇 Additional comments (10)
src/config.rs (4)

39-43: Monitor: new network fields are backward-compatible and sensible defaults

Adding network: bool and network_timeout: u64 under a #[serde(default)]-ed struct, with a Default impl, preserves compatibility with existing configs.


51-55: Default values align with README

network: false and network_timeout: 30 match the README example; no issues found.


79-79: New BotCommand::Modems variant added

This integrates cleanly with the command dispatcher and default commands. No enum serialization concerns spotted.


289-295: Default command for Modems added; verify intended permission

You set !bbm to Broadcaster. Given that Stats is Public, double-check whether Modems should also be public (info-only) or restricted as-is.

src/belabox.rs (1)

251-251: Improved error logging context

Logging ?text alongside the error is a good improvement for diagnosing deserialization issues.

Cargo.toml (1)

3-3: Multiple dependency upgrades; validate for breaking changes

  • tokio-tungstenite 0.26 and thiserror 2.x are major upgrades. APIs used here look OK, but please build and run end-to-end to catch subtle breaks (e.g., error variants, CloseFrame types).
  • Added serde_with likely for skip-serializing Option::None; ensure code paths using it are covered.

Action items:

  • Build and run with feature flags used in production.
  • Sanity-check WebSocket connect/auth/send flows at runtime.

Also applies to: 13-16, 20-20

src/command_handler.rs (4)

15-15: Derive Clone for CommandHandler

Reasonable given it’s shared across tasks; fields are Arcs or owned collections so clone cost is bounded.


56-57: Command routing: Modems handler wired in

Dispatch for BotCommand::Modems looks correct; matches config and README additions.


182-187: Stats: append total bitrate only when multiple interfaces

Behavior change from always appending. Verify this is intended UX—some users may still want a Total even with a single interface.


665-665: audio_src: set Option explicitly

Wrapping in Some(...) is correct given Start/config now treats asrc as optional. Matches serde_with usage to skip None.

Comment on lines +163 to +176
let current_modems = &mut lock.modems;

for (key, modem) in incoming.modems.iter() {
current_modems
.entry(key.to_string())
.and_modify(|existing| {
existing.status = modem.status.clone();
})
.or_insert_with(|| modem.clone());
}

// Remove modems that are not present in the incoming update.
current_modems.retain(|key, _| incoming.modems.contains_key(key));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Only the status field is refreshed – other modem data may go stale

Incoming updates can carry altered ifname, network_type, signal strength, etc., yet the merge only mutates status, leaving older fields untouched.

-                .and_modify(|existing| {
-                    existing.status = modem.status.clone();
-                })
+                .and_modify(|existing| *existing = modem.clone())

This keeps the cache fully in sync with the BELABOX state.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let current_modems = &mut lock.modems;
for (key, modem) in incoming.modems.iter() {
current_modems
.entry(key.to_string())
.and_modify(|existing| {
existing.status = modem.status.clone();
})
.or_insert_with(|| modem.clone());
}
// Remove modems that are not present in the incoming update.
current_modems.retain(|key, _| incoming.modems.contains_key(key));
}
let current_modems = &mut lock.modems;
for (key, modem) in incoming.modems.iter() {
current_modems
.entry(key.to_string())
- .and_modify(|existing| {
- existing.status = modem.status.clone();
- })
+ .and_modify(|existing| *existing = modem.clone())
.or_insert_with(|| modem.clone());
}
// Remove modems that are not present in the incoming update.
current_modems.retain(|key, _| incoming.modems.contains_key(key));
}
🤖 Prompt for AI Agents
In src/bot.rs around lines 163 to 176, the code only updates the `status` field
of existing modems during the merge, which causes other fields like `ifname`,
`network_type`, and signal strength to become stale. To fix this, update the
entire modem entry with the incoming modem data instead of just the `status`
field when modifying existing entries, ensuring the cache stays fully
synchronized with the BELABOX state.

Comment on lines +196 to +242
pub async fn modems(&self) -> Result<String> {
let (netifs, modems) = {
let state = self.bela_state.read().await;
(state.netif.clone(), state.modems.clone())
};

let modem_infos: Vec<String> = modems
.values()
.map(|modem| {
// Determine the interface name using a custom name if available
// FIXME: The name could be set once in bot.rs
let name_label = if let Some(ifname) = &modem.ifname {
netifs
.as_ref()
.and_then(|netifs| netifs.get(ifname))
.and_then(|iface| {
self.custom_interface_name
.get(ifname)
.or_else(|| self.custom_interface_name.get(&iface.ip))
})
.cloned()
.unwrap_or_else(|| ifname.to_owned())
} else {
"Modem".to_string()
};

// Build status string if available
let status_info = modem.status.as_ref().map_or(String::new(), |status| {
let network_type = status.network_type.as_deref().unwrap_or("?G");
let network = status.network.as_deref().unwrap_or("Unknown Network");

let mut info = format!(
"{} on {}, {}, signal {}",
network_type, network, status.connection, status.signal
);
if status.roaming {
info.push_str(", roaming");
}
info
});

format!("{}: {}", name_label, status_info).trim().to_owned()
})
.collect();

Ok(modem_infos.join(" - "))
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Modems: handle empty/no-status cases and avoid dangling colon

  • Current formatting can produce “Label:” with no details if status is None, and an empty final message if there are zero modems.
  • Tighten message construction to omit the colon when no status, and return a friendly message if there are no modems.

Proposed refactor:

-        let modem_infos: Vec<String> = modems
-            .values()
-            .map(|modem| {
-                // Determine the interface name using a custom name if available
-                // FIXME: The name could be set once in bot.rs
-                let name_label = if let Some(ifname) = &modem.ifname {
-                    netifs
-                        .as_ref()
-                        .and_then(|netifs| netifs.get(ifname))
-                        .and_then(|iface| {
-                            self.custom_interface_name
-                                .get(ifname)
-                                .or_else(|| self.custom_interface_name.get(&iface.ip))
-                        })
-                        .cloned()
-                        .unwrap_or_else(|| ifname.to_owned())
-                } else {
-                    "Modem".to_string()
-                };
-
-                // Build status string if available
-                let status_info = modem.status.as_ref().map_or(String::new(), |status| {
-                    let network_type = status.network_type.as_deref().unwrap_or("?G");
-                    let network = status.network.as_deref().unwrap_or("Unknown Network");
-
-                    let mut info = format!(
-                        "{} on {}, {}, signal {}",
-                        network_type, network, status.connection, status.signal
-                    );
-                    if status.roaming {
-                        info.push_str(", roaming");
-                    }
-                    info
-                });
-
-                format!("{}: {}", name_label, status_info).trim().to_owned()
-            })
-            .collect();
-
-        Ok(modem_infos.join(" - "))
+        let modem_infos: Vec<String> = modems
+            .values()
+            .map(|modem| {
+                // Determine interface display name using a custom name if available
+                let label = if let Some(ifname) = &modem.ifname {
+                    netifs
+                        .as_ref()
+                        .and_then(|ifs| ifs.get(ifname))
+                        .and_then(|iface| {
+                            self.custom_interface_name
+                                .get(ifname)
+                                .or_else(|| self.custom_interface_name.get(&iface.ip))
+                        })
+                        .cloned()
+                        .unwrap_or_else(|| ifname.to_owned())
+                } else {
+                    "Modem".to_string()
+                };
+
+                if let Some(status) = modem.status.as_ref() {
+                    let network_type = status.network_type.as_deref().unwrap_or("?G");
+                    let network = status.network.as_deref().unwrap_or("Unknown Network");
+                    let mut info = format!(
+                        "{} on {}, {}, signal {}",
+                        network_type, network, status.connection, status.signal
+                    );
+                    if status.roaming {
+                        info.push_str(", roaming");
+                    }
+                    format!("{label}: {info}")
+                } else {
+                    label
+                }
+            })
+            .filter(|s| !s.is_empty())
+            .collect();
+
+        if modem_infos.is_empty() {
+            return Ok("No modems found".to_string());
+        }
+
+        Ok(modem_infos.join(" - "))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub async fn modems(&self) -> Result<String> {
let (netifs, modems) = {
let state = self.bela_state.read().await;
(state.netif.clone(), state.modems.clone())
};
let modem_infos: Vec<String> = modems
.values()
.map(|modem| {
// Determine the interface name using a custom name if available
// FIXME: The name could be set once in bot.rs
let name_label = if let Some(ifname) = &modem.ifname {
netifs
.as_ref()
.and_then(|netifs| netifs.get(ifname))
.and_then(|iface| {
self.custom_interface_name
.get(ifname)
.or_else(|| self.custom_interface_name.get(&iface.ip))
})
.cloned()
.unwrap_or_else(|| ifname.to_owned())
} else {
"Modem".to_string()
};
// Build status string if available
let status_info = modem.status.as_ref().map_or(String::new(), |status| {
let network_type = status.network_type.as_deref().unwrap_or("?G");
let network = status.network.as_deref().unwrap_or("Unknown Network");
let mut info = format!(
"{} on {}, {}, signal {}",
network_type, network, status.connection, status.signal
);
if status.roaming {
info.push_str(", roaming");
}
info
});
format!("{}: {}", name_label, status_info).trim().to_owned()
})
.collect();
Ok(modem_infos.join(" - "))
}
pub async fn modems(&self) -> Result<String> {
let (netifs, modems) = {
let state = self.bela_state.read().await;
(state.netif.clone(), state.modems.clone())
};
let modem_infos: Vec<String> = modems
.values()
.map(|modem| {
// Determine interface display name using a custom name if available
let label = if let Some(ifname) = &modem.ifname {
netifs
.as_ref()
.and_then(|ifs| ifs.get(ifname))
.and_then(|iface| {
self.custom_interface_name
.get(ifname)
.or_else(|| self.custom_interface_name.get(&iface.ip))
})
.cloned()
.unwrap_or_else(|| ifname.to_owned())
} else {
"Modem".to_string()
};
if let Some(status) = modem.status.as_ref() {
let network_type = status.network_type.as_deref().unwrap_or("?G");
let network = status.network.as_deref().unwrap_or("Unknown Network");
let mut info = format!(
"{} on {}, {}, signal {}",
network_type, network, status.connection, status.signal
);
if status.roaming {
info.push_str(", roaming");
}
format!("{label}: {info}")
} else {
label
}
})
.filter(|s| !s.is_empty())
.collect();
if modem_infos.is_empty() {
return Ok("No modems found".to_string());
}
Ok(modem_infos.join(" - "))
}
🤖 Prompt for AI Agents
In src/command_handler.rs between lines 196 and 242, the modems function
currently formats modem info with a colon even when there is no status,
resulting in dangling colons and an empty string if no modems exist. To fix
this, modify the formatting logic to only include the colon and status info if
status is present, otherwise just show the label without a colon. Additionally,
check if the modems collection is empty and return a friendly message like "No
modems found" instead of an empty string. This ensures cleaner output and better
user feedback.

Comment on lines +179 to +199
pub async fn network(&self, network_timeout: u64) {
{
let mut lock = self.bela_state.write().await;
if !lock.is_streaming {
return;
}

let timeout = &mut lock.network_timeout;
if timeout.elapsed() < Duration::from_secs(network_timeout) {
return;
} else {
*timeout = Instant::now();
}
}

let lock = self.command_handler.lock().await;
let Some(ch) = &*lock else { return };
let Ok(msg) = ch.stats().await else { return };

self.send(msg).await;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Hold-off the mutex before an .await to prevent lock contention

self.command_handler.lock().await is kept across the asynchronous ch.stats().await call.
If stats() performs I/O the mutex stays locked for its whole duration, blocking any writer that tries to swap or drop the handler.

-        let lock = self.command_handler.lock().await;
-        let Some(ch) = &*lock else { return };
-        let Ok(msg) = ch.stats().await else { return };
+        // Grab the handler, then release the mutex before awaiting.
+        let ch = {
+            let lock = self.command_handler.lock().await;
+            match &*lock {
+                Some(h) => h.clone(),
+                None => return,
+            }
+        };
+
+        let Ok(msg) = ch.stats().await else { return };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub async fn network(&self, network_timeout: u64) {
{
let mut lock = self.bela_state.write().await;
if !lock.is_streaming {
return;
}
let timeout = &mut lock.network_timeout;
if timeout.elapsed() < Duration::from_secs(network_timeout) {
return;
} else {
*timeout = Instant::now();
}
}
let lock = self.command_handler.lock().await;
let Some(ch) = &*lock else { return };
let Ok(msg) = ch.stats().await else { return };
self.send(msg).await;
}
pub async fn network(&self, network_timeout: u64) {
{
let mut lock = self.bela_state.write().await;
if !lock.is_streaming {
return;
}
let timeout = &mut lock.network_timeout;
if timeout.elapsed() < Duration::from_secs(network_timeout) {
return;
} else {
*timeout = Instant::now();
}
}
// Grab the handler, then release the mutex before awaiting.
let ch = {
let lock = self.command_handler.lock().await;
match &*lock {
Some(h) => h.clone(),
None => return,
}
};
let Ok(msg) = ch.stats().await else { return };
self.send(msg).await;
}
🤖 Prompt for AI Agents
In src/monitor.rs around lines 179 to 199, the mutex lock on
self.command_handler is held across the asynchronous call ch.stats().await,
which can cause lock contention if stats() performs I/O. To fix this, acquire
the lock, clone or extract the command handler reference needed for stats(),
then release the lock before calling the async stats() method. This ensures the
mutex is not held during the await, preventing blocking other writers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants