Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,10 @@ impl Client {
// checks the socket, but this ordering avoids a confusing state window.
self.is_connected.store(false, Ordering::Release);
self.retried_group_messages.invalidate_all();
// Drop per-chat message queue senders so workers exit via channel close.
// Without this, stale workers from the old connection survive reconnects
// holding outdated signal/crypto state.
self.message_queues.invalidate_all();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid dropping chat-queue map before workers finish

Calling self.message_queues.invalidate_all() in cleanup_connection_state removes the sender entry immediately, but existing per-chat workers keep running until their channel drains (MessageHandler loops on rx.recv() in src/handlers/message.rs). If a disconnect happens with buffered messages and reconnect is fast, the next incoming message for that chat creates a second worker/queue while the old worker is still draining, so messages for the same chat can be processed concurrently and out of order (breaking the mailbox guarantee used to preserve Signal session ordering).

Useful? React with 👍 / 👎.

// Clear pending retries so stale keys from detached scopeguard
// cleanup don't suppress the first retry after reconnect.
self.pending_retries
Expand Down
1 change: 1 addition & 0 deletions wacore/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub trait Runtime: Send + Sync + 'static {
/// Uses `std::sync::Mutex` internally so that the handle is `Send + Sync`,
/// which is required because it may be stored inside structs shared across
/// tasks (e.g. `NoiseSocket` behind an `Arc`).
#[must_use = "dropping an AbortHandle aborts the task; use .detach() for fire-and-forget"]
pub struct AbortHandle {
abort_fn: std::sync::Mutex<Option<Box<dyn FnOnce() + Send + 'static>>>,
}
Expand Down
Loading