feat(library): make LibraryLoader/DocumentLoader Send on native targets#71
Open
bkfunk wants to merge 1 commit into
Open
feat(library): make LibraryLoader/DocumentLoader Send on native targets#71bkfunk wants to merge 1 commit into
bkfunk wants to merge 1 commit into
Conversation
`LibraryLoader` and `DocumentLoader` were declared with
`#[async_trait(?Send)]`, which propagated `?Send` through every
resolver and through `resolve_dependencies*`. On wasm32 that's
necessary (wasm-bindgen futures are deliberately `!Send` because the
web platform is single-threaded), but on native it forced consumers
running on a multi-threaded tokio runtime (e.g. Tauri's default) into
ceremonies like `spawn_blocking` + `new_current_thread`, paying a
fresh-runtime construction on every part load.
The underlying work is already `Send` on native: `LocalLoader` uses
`tokio::fs` and `BufReader` (both `Send`), `HttpLoader` uses
`reqwest::Client` futures (`Send`), and `PartCache` lives in
`Arc<RwLock<...>>` (already `Send + Sync`). The `?Send` was the only
thing forcing the worse codepath.
Switch the four `#[async_trait(?Send)]` attributes (the two traits in
`library.rs` and the four impls across the two resolvers) to:
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
This is the standard pattern for libraries that target both platforms.
Native consumers can now `.await` `LibraryLoader` futures directly on
worker threads; the wasm32 build is unaffected.
Adds compile-time `Send` checks in both resolvers: dead helper
functions whose bodies call `assert_send::<F>(_)` on the future
returned by `load_ref`/`load_colors`. These don't run at runtime, but
will fail to compile if the futures ever lose `Send` again.
Verified:
- `cargo check --workspace` passes on native
- `cargo check -p ldraw --target wasm32-unknown-unknown` passes
- `cargo test -p ldraw --lib` passes
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR removes the ?Send constraint on LibraryLoader and DocumentLoader traits (and their LocalLoader/HttpLoader impls) on native targets, while preserving ?Send on wasm32 where it is still required. This lets callers running on a multi-threaded tokio runtime (e.g. Tauri) .await these futures directly across threads instead of constructing a per-call current-thread runtime inside spawn_blocking.
Changes:
- Switch four
#[async_trait(?Send)]attributes to acfg_attrpair that uses the plain#[async_trait](Send) on non-wasm32 and#[async_trait(?Send)]on wasm32, applied to both traits inlibrary.rsand the four impls inresolvers/local.rsandresolvers/http.rs. - Add per-resolver dead
_assert_*_futures_are_sendhelpers gated to non-wasm targets that pass the futures returned byload_refandload_colorsto a genericassert_send::<F: Send>to provide a compile-time regression guard.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| ldraw/src/library.rs | Replaces ?Send on the DocumentLoader and LibraryLoader trait declarations with target-gated async_trait attributes. |
| ldraw/src/resolvers/local.rs | Same gating on the two LocalLoader impls; adds a non-wasm _assert_localloader_futures_are_send compile-time check. |
| ldraw/src/resolvers/http.rs | Same gating on the two HttpLoader impls; adds a parallel non-wasm _assert_httploader_futures_are_send compile-time check. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
LibraryLoaderandDocumentLoader(inldraw/src/library.rs) are declared with#[async_trait(?Send)]. That?Sendpropagates through both native resolvers (LocalLoader,HttpLoader) and throughresolve_dependencies_multipart.On wasm32 this is necessary — wasm-bindgen futures are deliberately
!Sendbecause the web platform is single-threaded. But on native, the underlying work is alreadySend:LocalLoaderusestokio::fs::File/BufReader/try_exists— allSend.HttpLoaderusesreqwest::Clientfutures — alsoSend.PartCachelives inArc<RwLock<...>>(alreadySend + Sync).The
?Sendwas the only thing forcing native consumers running on a multi-threaded tokio runtime (e.g. Tauri's default) into ceremonies like:A fresh runtime is constructed on every call. That's fine for one-shot loads but adds up for thumbnail-heavy UIs that batch dozens of part loads.
Change
Switch the four
#[async_trait(?Send)]attributes (the two traits inlibrary.rsand the four impls acrossresolvers/local.rsandresolvers/http.rs) to the standard cfg-gated pattern:On native, futures returned by
LibraryLoader::load_ref,LibraryLoader::load_colors, andDocumentLoader::load_documentare nowSend. On wasm32, the existing?Sendbehavior is preserved.Compile-time
Sendregression guardAdds dead helper functions in each resolver whose bodies call
assert_send::<F>(_)on the futures returned byload_refandload_colors. These never run, but the compiler type-checks their bodies, so any future regression that makes the futures!Sendon native will fail compilation.Test plan
cargo check --workspacepasses on nativecargo check -p ldraw --target wasm32-unknown-unknownpassescargo test -p ldraw --libpasses