Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion advanced/signal-protocol.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ This mechanism ensures that group messages from newly-paired companion devices a
// src/message.rs — simplified flow
async fn handle_unknown_device_sync(&self, info: &Arc<MessageInfo>) {
let user_jid = info.source.sender.to_non_ad();
if !self.pending_device_sync.add(user_jid.clone()).await {
if !self.pending_device_sync.add(&user_jid) {
return; // already queued, dedup
}
if info.is_offline {
Expand Down
9 changes: 4 additions & 5 deletions advanced/websocket-handling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ impl Client {
).await?;

// Store socket and start receivers
self.noise_socket.store(Some(Arc::clone(&noise_socket)));
*self.noise_socket.lock().unwrap_or_else(|p| p.into_inner()) = Some(Arc::clone(&noise_socket));
self.start_frame_receiver(events, noise_socket).await;

Ok(())
Expand All @@ -795,7 +795,7 @@ async fn read_messages_loop(self: &Arc<Self>) -> Result<(), anyhow::Error> {
.ok_or_else(|| anyhow!("Cannot start message loop: not connected"))?;
// Noise socket is stable for the lifetime of this loop; resolve once
// instead of locking the mutex on every inbound frame.
let noise_socket = self.get_noise_socket().await
let noise_socket = self.get_noise_socket()
.map_err(|_| anyhow!("Cannot start message loop: no noise socket"))?;
let mut frame_decoder = FrameDecoder::new();

Expand Down Expand Up @@ -874,8 +874,7 @@ Frame decryption is always sequential (noise protocol counter ordering), but nod
```rust
impl Client {
pub async fn send_node(&self, node: &Node) -> Result<()> {
let noise_socket = self.noise_socket.load()
.ok_or_else(|| anyhow!("not connected"))?;
let noise_socket = self.get_noise_socket()?;

// Marshal node to binary with auto-sized buffer
let plaintext_buf = marshal_auto(node)?;
Expand All @@ -898,7 +897,7 @@ impl Client {
async fn cleanup_connection_state(&self) {
self.shutdown_notifier.notify(usize::MAX);
*self.transport.lock().await = None;
*self.noise_socket.lock().await = None;
*self.noise_socket.lock().unwrap_or_else(|p| p.into_inner()) = None;
self.is_connected.store(false, Ordering::Release);

// Drop per-chat lane senders so workers exit via channel close.
Expand Down
4 changes: 2 additions & 2 deletions api/chatstate.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ client.register_chatstate_handler(Arc::new(|event: ChatStateEvent| {
}
_ => {}
}
})).await;
}));
```

### ReceivedChatState
Expand Down Expand Up @@ -412,4 +412,4 @@ tokio::time::sleep(Duration::from_secs(3)).await;

client.chatstate().send_paused(&recipient).await?;
println!("Recording stopped (would send audio here)");
```
```
11 changes: 6 additions & 5 deletions api/client.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1808,13 +1808,14 @@ See [ChannelEventHandler](/concepts/events#channeleventhandler) for details.
### register_chatstate_handler

```rust
pub async fn register_chatstate_handler(
&self,
handler: Arc<dyn Fn(ChatStateEvent) + Send + Sync>,
)
pub fn register_chatstate_handler(&self, handler: Arc<dyn Fn(ChatStateEvent) + Send + Sync>)
```

Registers a handler for chat state events (typing indicators). The handler is wrapped in `Arc` for thread-safe sharing across the event dispatching system.
Registers a handler for chat state events (typing indicators). The handler is wrapped in `Arc` for thread-safe sharing across the event dispatching system. Registration is copy-on-write, so dispatch never blocks a concurrent registration and the zero-handler default path takes no lock at all.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

<Warning>
**Breaking change (as of PR #1227):** `register_chatstate_handler` is no longer `async`. Drop the `.await` at call sites: `client.register_chatstate_handler(handler)`.
</Warning>
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### set_raw_node_forwarding

Expand Down
6 changes: 3 additions & 3 deletions concepts/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,14 @@ See [custom backends](/guides/custom-backends) for implementing your own runtime

**Location:** `src/client.rs`

**Purpose:** Orchestrates connection lifecycle, event bus, and high-level operations. Uses `async-lock` (runtime-agnostic) for all internal synchronization instead of Tokio-specific primitives.
**Purpose:** Orchestrates connection lifecycle, event bus, and high-level operations. The synchronization primitive follows the shape of the state, not a single default. State whose critical section awaits uses `async-lock` (runtime-agnostic, not Tokio-specific). State whose critical section never awaits — a clone, a store, a set op — uses a `std::sync` lock instead. State that is built once and never replaced uses `std::sync::OnceLock`. On a path that must produce a `Send` future (e.g. a spawned task), a `std::sync::MutexGuard` isn't `Send`, so holding one across an `.await` there is a compile error rather than something a reviewer has to catch by hand ([#1227](https://github.com/oxidezap/whatsapp-rust/pull/1227)).

```rust
pub struct Client {
pub(crate) core: wacore::client::CoreClient,
pub(crate) persistence_manager: Arc<PersistenceManager>,
pub(crate) media_conn: Arc<RwLock<Option<MediaConn>>>,
pub(crate) noise_socket: Arc<Mutex<Option<Arc<NoiseSocket>>>>,
pub(crate) noise_socket: Arc<std::sync::Mutex<Option<Arc<NoiseSocket>>>>,
Comment thread
jlucaso1 marked this conversation as resolved.
Comment thread
jlucaso1 marked this conversation as resolved.
Comment thread
jlucaso1 marked this conversation as resolved.
// ... connection state, caches, locks
}
```
Expand Down Expand Up @@ -513,7 +513,7 @@ When online (not during offline sync), unknown devices trigger an immediate back
```rust
// src/pending_device_sync.rs
pub(crate) struct PendingDeviceSync {
pending: async_lock::Mutex<HashSet<Jid>>,
pending: std::sync::Mutex<HashSet<Jid>>,
}
```

Expand Down