Skip to content

fix(wasm): relax EncHandler Send+Sync via MaybeSendSync, gate async_trait - #793

Merged
jlucaso1 merged 1 commit into
mainfrom
fix/enchandler-wasm-send-sync-gating
Jun 9, 2026
Merged

fix(wasm): relax EncHandler Send+Sync via MaybeSendSync, gate async_trait#793
jlucaso1 merged 1 commit into
mainfrom
fix/enchandler-wasm-send-sync-gating

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

What

EncHandler is a public custom-enc extension point (BotBuilder::with_enc_handler, stored as Arc<dyn EncHandler> on Client). Unlike its sibling extension points EventHandler and SendContextResolver, it hardcoded a Send + Sync supertrait and a plain #[async_trait] with no wasm32 gate.

The high-level crate builds for wasm32, where the client is intentionally !Send and a handler may capture !Send JS handles. So a wasm custom enc handler failed to compile on the supertrait bound.

This mirrors the established convention: the supertrait becomes wacore::sync_marker::MaybeSendSync (which is Send + Sync on native, no bound on wasm32) and async_trait gets the dual cfg_attr(..., async_trait(?Send)) gate.

Why

Unblocks a !Send-handle-backed custom enc handler on the wasm port, consistent with how EventHandler (events.rs) and SendContextResolver (context.rs) already relax their bounds.

Risk

None on native: the blanket MaybeSendSync impl keeps dyn EncHandler Send + Sync, so the Arc<dyn EncHandler> cross-thread storage on Client is untouched. The only impl EncHandler blocks are test-only (native). Verified the native build plus tests AND the wasm32 lib build (cargo build -p whatsapp-rust --lib --release --target wasm32-unknown-unknown --no-default-features --features debug-diagnostics).

…rait

EncHandler is a public custom-enc extension point (BotBuilder::with_enc_handler,
stored as Arc<dyn EncHandler> on Client), but unlike its sibling extension points
EventHandler and SendContextResolver it hardcoded a Send + Sync supertrait and a
plain async_trait with no wasm32 gate. The high-level crate builds for wasm32,
where the client is intentionally !Send and a handler may capture !Send JS
handles, so a wasm custom enc handler failed to compile on the supertrait bound.

Mirror the established convention: supertrait becomes wacore::sync_marker::MaybeSendSync
(Send + Sync on native, no bound on wasm32) and async_trait gets the dual
cfg_attr(?Send) gate. Native behavior is unchanged: the blanket MaybeSendSync impl
keeps dyn EncHandler Send + Sync, so the Arc<dyn EncHandler> cross-thread storage
is untouched. Verified both the native build/tests and the wasm32 lib build.
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Refactor
    • Enhanced encryption handler implementation for improved cross-platform compatibility.

Walkthrough

The EncHandler trait now conditionally applies async_trait based on target architecture and relaxes its trait bounds to MaybeSendSync. On wasm32 targets, the macro uses (?Send) to allow non-Send implementations; on native targets, it uses the default strict configuration. This aligns with the concurrency model differences between platforms.

Changes

EncHandler Platform Compatibility

Layer / File(s) Summary
EncHandler async trait and bounds update
src/types/enc_handler.rs
EncHandler trait bounds changed from Send + Sync to wacore::sync_marker::MaybeSendSync with conditional async_trait macro configuration: async_trait(?Send) on wasm32, default on native targets.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • oxidezap/whatsapp-rust#671: Introduces MaybeSendSync marker and applies analogous async bound relaxations across multiple handler traits for consistent platform-specific trait design.
  • oxidezap/whatsapp-rust#101: Uses the EncHandler trait for custom encryption handler registration, directly consuming this trait's definition and bounds.

Suggested labels

api-design


Look, this is a clean change. The key thing here is: does this actually work correctly on both wasm32 and native? You're using MaybeSendSync instead of hard Send + Sync bounds, and conditionally removing the Send requirement on wasm32 via async_trait(?Send). That's the right move for cross-platform support.

The concern I have is: make sure all the implementations of EncHandler actually respect this contract. If something implements this trait and doesn't properly understand the Send/Sync semantics on each platform, you'll have subtle concurrency bugs. Verify that the downstream code using EncHandler doesn't assume Send when it shouldn't on wasm32. That's where things break in practice.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately captures the main change: relaxing EncHandler's Send+Sync bounds via MaybeSendSync and adding conditional async_trait gating for wasm32 compatibility.
Description check ✅ Passed Description is comprehensive and directly related to the changeset, explaining the what, why, and risk assessment with concrete verification steps.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/enchandler-wasm-send-sync-gating

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/types/enc_handler.rs (1)

54-55: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider matching the trait's conditional async_trait pattern in test code.

Look, the MockEncHandler impl uses unconditional #[async_trait::async_trait], but the trait itself is now conditional. These tokio tests won't run on wasm32 anyway, so it's not breaking anything right now. But if we want this to scale and stay consistent, we should probably make the test impl match:

+    #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
+    #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
-    #[async_trait::async_trait]
     impl EncHandler for MockEncHandler {

This way, if someone adds wasm32 tests later, things just work. Consistency matters when you're building something at scale.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/types/enc_handler.rs` around lines 54 - 55, The MockEncHandler impl uses
an unconditional #[async_trait::async_trait] but the EncHandler trait is
conditionally async; update the impl to use the same conditional attribute
pattern as the trait (e.g., replace the unconditional
#[async_trait::async_trait] above impl EncHandler for MockEncHandler with the
matching cfg_attr/conditional form used on the EncHandler trait) so the test
impl mirrors the trait's async_trait conditionalization and remains consistent
across targets.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/types/enc_handler.rs`:
- Around line 54-55: The MockEncHandler impl uses an unconditional
#[async_trait::async_trait] but the EncHandler trait is conditionally async;
update the impl to use the same conditional attribute pattern as the trait
(e.g., replace the unconditional #[async_trait::async_trait] above impl
EncHandler for MockEncHandler with the matching cfg_attr/conditional form used
on the EncHandler trait) so the test impl mirrors the trait's async_trait
conditionalization and remains consistent across targets.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 21204dfe-36e3-483f-9c43-c21d227395e3

📥 Commits

Reviewing files that changed from the base of the PR and between acc8714 and cbca0c8.

📒 Files selected for processing (1)
  • src/types/enc_handler.rs

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

Benchmark Results

67 unchanged benchmark(s)
Benchmark Current Baseline Change
reporting_token_benchmark::content_extraction_group::bench_content_extraction simple:setup_simple_message() 2,927 2,927 +0.0%
reporting_token_benchmark::content_extraction_group::bench_content_extraction extended:setup_extended_message() 8,448 8,448 +0.0%
reporting_token_benchmark::key_derivation_group::bench_key_derivation 31,317 31,317 +0.0%
reporting_token_benchmark::token_calculation_group::bench_token_calculation 13,827 13,827 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation simple:setup_full_gen_simple() 49,487 49,487 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation extended:setup_full_gen_extended() 55,003 55,003 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding simple:setup_simple_message() 1,679 1,679 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding extended:setup_extended_message() 4,393 4,393 +0.0%
send_receive_benchmark::dm_send::bench_dm_send text:setup_dm_send() 112,970 112,968 +0.0%
send_receive_benchmark::dm_recv::bench_dm_recv text:setup_dm_recv() 1,656,618 1,656,618 +0.0%
send_receive_benchmark::group_send::bench_group_send group_10:setup_group_send_10() 644,469 644,487 -0.0%
send_receive_benchmark::group_send::bench_group_send group_50:setup_group_send_50() 868,551 868,554 -0.0%
send_receive_benchmark::group_send::bench_group_send group_256:setup_group_send_256() 2,076,300 2,076,644 -0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_10:setup_group_skdm_10() 741,851 741,825 +0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_50:setup_group_skdm_50() 1,323,035 1,323,023 +0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_256:setup_group_skdm_256() 4,377,968 4,378,917 -0.0%
send_receive_benchmark::group_recv::bench_group_recv text:setup_group_recv() 519,852 517,382 +0.5%
binary_benchmark::marshal_group::bench_marshal_allocating 45,381 45,381 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_allocating 45,431 45,431 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_allocating 66,334 66,334 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer 43,492 43,492 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer_vec_writer 45,487 45,487 +0.0%
binary_benchmark::marshal_group::bench_marshal_long_string 4,945 4,945 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_long_string 4,976 4,976 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_long_string 6,747 6,747 +0.0%
binary_benchmark::marshal_group::bench_marshal_huge_bytes_allocating 528,544 528,544 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_huge_bytes_allocating 528,165 528,165 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_huge_bytes_allocating 529,411 529,411 +0.0%
binary_benchmark::marshal_group::bench_marshal_many_children_allocating 5,417,732 5,417,732 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_many_children_allocating 5,362,047 5,362,047 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_many_children_allocating 13,276,365 13,276,365 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal small:setup_small_marshaled() 1,850 1,850 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal large:setup_large_marshaled() 29,217 29,217 +0.0%
binary_benchmark::unpack_group::bench_unpack_uncompressed 618 618 +0.0%
binary_benchmark::unpack_group::bench_unpack_compressed 672,890 672,890 +0.0%
binary_benchmark::attr_parser_group::bench_attr_parser attr_lookup:setup_attr_marshaled() 3,736 3,736 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip small:setup_small_marshaled() 3,840 3,840 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip large:setup_large_marshaled() 48,274 48,274 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto small:setup_small_marshaled() 3,866 3,866 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto large:setup_large_marshaled() 48,335 48,335 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact small:setup_small_marshaled() 5,206 5,206 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact large:setup_large_marshaled() 66,659 66,659 +0.0%
binary_benchmark::child_iteration_group::bench_get_children_by_tag 310,312 310,312 +0.0%
binary_benchmark::jid_optimization_group::bench_jid_to_owned_access jid_access:setup_jid_heavy_marshaled() 8,291 8,291 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_u32 254 254 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_u32 91 91 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_u64 292 292 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_u64 137 137 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_i64 317 317 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_i64 145 145 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_loop_100_u64 27,425 27,425 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_loop_100_u64 10,725 10,725 +0.0%
libsignal_benchmark::dm_group::bench_dm_session_establishment setup:setup_dm_users() 4,141,698 4,139,079 +0.1%
libsignal_benchmark::dm_group::bench_dm_encrypt_first_message first_msg:setup_dm_session() 100,133 100,133 +0.0%
libsignal_benchmark::dm_group::bench_dm_decrypt_first_message decrypt_prekey:setup_dm_with_first_message() 4,264,189 4,264,189 +0.0%
libsignal_benchmark::dm_group::bench_dm_encrypt_subsequent_message subsequent:setup_established_dm_session() 100,399 100,399 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_create_distribution_message create:setup_group_sender() 210,262 210,262 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_encrypt_message encrypt:setup_group_with_distribution() 496,921 496,921 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_decrypt_message decrypt:setup_group_with_encrypted_message() 510,222 508,432 +0.4%
libsignal_benchmark::conversation_group::bench_full_dm_conversation full:setup_conversation_data() 11,972,126 11,979,023 -0.1%
libsignal_benchmark::signature_group::bench_signature_creation sign:setup_keypair_with_message() 2,466,138 2,466,138 +0.0%
libsignal_benchmark::signature_group::bench_signature_verification verify:setup_keypair_with_message() 4,910,732 4,932,132 -0.4%
libsignal_benchmark::signature_group::bench_key_generation keygen 2,043,397 2,043,397 +0.0%
libsignal_benchmark::session_optimization_group::bench_decrypt_with_previous_session previous_session:setup_with_archived_sessions() 37,414 37,404 +0.0%
libsignal_benchmark::session_optimization_group::bench_out_of_order_decryption out_of_order:setup_out_of_order_messages() 3,617,967 3,617,967 +0.0%
libsignal_benchmark::session_optimization_group::bench_promote_matching_session promote:setup_promote_matching_session() 230,638 230,638 +0.0%
libsignal_benchmark::session_optimization_group::bench_message_key_eviction eviction:setup_message_key_eviction() 9,980,959 9,980,959 +0.0%
No significant changes detected.

@jlucaso1
jlucaso1 merged commit 582a4ee into main Jun 9, 2026
12 checks passed
@jlucaso1
jlucaso1 deleted the fix/enchandler-wasm-send-sync-gating branch June 9, 2026 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant