Skip to content

Conversation

@cdmurph32
Copy link
Contributor

@cdmurph32 cdmurph32 commented Jan 12, 2026

PR #147572 changed WASI to use the Unix threading implementation, but WASI does not support threading. When the Unix code tries to call pthread_create, it fails with EAGAIN, causing libraries like rayon to panic when trying to initialize their global thread pool.

The old wasip1/wasip2 implementations:

  • wasip1: Threading conditionally available with atomics (experimental)
  • wasip2: Threading unconditionally unsupported

This fix restores that behavior by disabling pthread-based threading for all WASI targets:

  1. Guard the pthread-based Thread implementation with #[cfg(not(target_os = "wasi"))]
  2. Provide an unsupported stub (Thread(!)) for WASI
  3. Return Err(io::Error::UNSUPPORTED_PLATFORM) when Thread::new is called

Fixes the regression where rayon-based code (e.g., lopdf in PDF handling) panicked on WASI after nightly-2025-12-10.

Before fix:
pthread_create returns EAGAIN (error code 6)
ThreadPoolBuildError { kind: IOError(Os { code: 6,
kind: WouldBlock, message: "Resource temporarily unavailable" }) }

After fix:
Thread::new returns Err(io::Error::UNSUPPORTED_PLATFORM)
Libraries can gracefully handle the lack of threading support

r? @alexcrichton

@rustbot rustbot added O-wasi Operating system: Wasi, Webassembly System Interface S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jan 12, 2026
@rust-log-analyzer

This comment has been minimized.

@JayanAXHF JayanAXHF added O-wasip1-threads Operating system: *-wasmp1-threads, WASI Preview1 with atomics and threads and removed O-wasip1-threads Operating system: *-wasmp1-threads, WASI Preview1 with atomics and threads labels Jan 12, 2026
@JayanAXHF
Copy link
Contributor

please link any relevant issue(s) on this regression if there are any :)

@cdmurph32 cdmurph32 marked this pull request as ready for review January 12, 2026 19:19
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jan 12, 2026
@rustbot

This comment has been minimized.

@alexcrichton
Copy link
Member

Can you share a link to the rayon code that's panicking? Is it testing for a specific ErrorKind for example to swallow errors with?

I'd ideally prefer to not sprinkle #[cfg(..)] for WASI throughout this file since the main benefit is to use most of the file as-is. If a special exception needs to be made I'd perfer to have something like if cfg!(...) { return /* specific error */ } in one location if that'd work for detection in rayon for example.

@rust-log-analyzer

This comment has been minimized.

@cdmurph32
Copy link
Contributor Author

This is the code in lopdf that uses rayon:
https://github.com/J-F-Liu/lopdf/blob/main/src/reader.rs#L953-L962

self.document.objects = self
                .document
                .reference_table
                .entries
                .par_iter()
                .filter_map(entries_filter_map)
                .collect();

I could work around this in c2pa-rs, by disabling rayon for lopdf, but that will introduce another WASI specific entry in my Cargo.toml. WASI specific dependencies and code in my SDK make it harder for me to justify supporting the target. Therefore the choice is to either have WASI specific code in this one place in the Rust source, or in every crate that uses it.

I realize it's a balancing act, and I will respect your decision here. It won't ruin my day either way.

@alexcrichton
Copy link
Member

I agree it'd be best to keep rayon working here, but I'd like to better understand the shape of the problem. For example can you share the location in Rayon that is panicking currently? Do you know the location in Rayon that keeps WASI working today? The crux of the question is why a changing error code for spawning a thread caused Rayon to regress. It would be good to pinpoint that as it'll inform the shape of the solution.

@cdmurph32
Copy link
Contributor Author

cdmurph32 commented Jan 13, 2026

Of course. That makes complete sense.

Rayon checks to see if a target is supported here: https://github.com/rayon-rs/rayon/blob/main/rayon-core/src/lib.rs#L745-L747

    fn is_unsupported(&self) -> bool {
        matches!(&self.kind, ErrorKind::IOError(e) if e.kind() == io::ErrorKind::Unsupported)
    }

https://github.com/rayon-rs/rayon/blob/main/rayon-core/src/registry.rs#L209-L228

fn default_global_registry() -> Result<Arc<Registry>, ThreadPoolBuildError> {
    let result = Registry::new(ThreadPoolBuilder::new());

    // If we're running in an environment that doesn't support threads at all, we can fall back to
    // using the current thread alone. This is crude, and probably won't work for non-blocking
    // calls like `spawn` or `broadcast_spawn`, but a lot of stuff does work fine.
    //
    // Notably, this allows current WebAssembly targets to work even though their threading support
    // is stubbed out, and we won't have to change anything if they do add real threading.
    let unsupported = matches!(&result, Err(e) if e.is_unsupported());
    if unsupported && WorkerThread::current().is_null() {
        let builder = ThreadPoolBuilder::new().num_threads(1).use_current_thread();
        let fallback_result = Registry::new(builder);
        if fallback_result.is_ok() {
            return fallback_result;
        }
    }

    result
}

If that check indicates that threading is supported, the thread registry will fail to initialize here:
https://github.com/rayon-rs/rayon/blob/main/rayon-core/src/registry.rs#L313C1-L315C14

            if let Err(e) = builder.get_spawn_handler().spawn(thread) {
                return Err(ThreadPoolBuildError::new(ErrorKind::IOError(e)));
            }

alexcrichton added a commit to alexcrichton/wasi-libc that referenced this pull request Jan 13, 2026
This changes from `EAGAIN` to `ENOTSUP` since this operation can't ever
succeed, it's entirely unsupported. This is in relation to
rust-lang/rust#151016 where in the Rust ecosystem crates were using the
"Unsupported" error kind to gracefully handle errors from spawning a
thread, such a falling back to single-threaded code. By switching to
`EAGAIN` it caused these graceful fallbacks to not get triggered.
@alexcrichton
Copy link
Member

Ok thanks that makes sense. I think the "real" fix here is WebAssembly/wasi-libc#716 which is to change the source of truth about the error code here. That'll take some time to propagate over here, however, so I don't think it makes sense to block on that.

In the meantime, I'd personally prefer a fix such that somewhere on the thread creation path you'd add if cfg!(target_os = "wasi") { return ... }. That should be less invasive than this current PR, continue using most of unix.rs, and that block can have a FIXME pointing at the libc PR saying that the block can be deleted as soon as wasi-sdk is updated with that fix.

@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

Comment on lines 1 to 5
#[cfg_attr(target_os = "wasi", allow(dead_code))]
pub struct Handler;

impl Handler {
#[cfg_attr(target_os = "wasi", allow(dead_code))]
Copy link
Member

Choose a reason for hiding this comment

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

I think these two attributes can be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes we can. Thanks

alexcrichton added a commit to WebAssembly/wasi-libc that referenced this pull request Jan 14, 2026
This changes from `EAGAIN` to `ENOTSUP` since this operation can't ever
succeed, it's entirely unsupported. This is in relation to
rust-lang/rust#151016 where in the Rust ecosystem crates were using the
"Unsupported" error kind to gracefully handle errors from spawning a
thread, such a falling back to single-threaded code. By switching to
`EAGAIN` it caused these graceful fallbacks to not get triggered.
Recent changes made WASI targets use the Unix threading implementation, but
WASI does not support threading. When the Unix code tries to call
pthread_create, it fails with EAGAIN, causing libraries like rayon to
panic when trying to initialize their global thread pool.

This fix adds an early return in Thread::new() that checks for WASI and
returns UNSUPPORTED_PLATFORM. This approach:
- Continues using most of the unix.rs code path (less invasive)
- Only requires a small cfg check at the start of Thread::new()
- Can be easily removed once wasi-sdk is updated with the proper fix

The real fix is being tracked in `WebAssembly/wasi-libc#716` which will
change the error code returned by pthread_create to properly indicate
unsupported operations. Once that propagates to wasi-sdk, this workaround
can be removed.

Fixes the regression where rayon-based code (e.g., lopdf in PDF handling)
panicked on WASI after nightly-2025-12-10.

Before fix:
  pthread_create returns EAGAIN (error code 6)
  ThreadPoolBuildError { kind: IOError(Os { code: 6,
  kind: WouldBlock, message: "Resource temporarily unavailable" }) }

After fix:
  Thread::new returns Err(io::Error::UNSUPPORTED_PLATFORM)
  Libraries can gracefully handle the lack of threading support
@rustbot
Copy link
Collaborator

rustbot commented Jan 14, 2026

⚠️ Warning ⚠️

  • There are issue links (such as #123) in the commit messages of the following commits.
    Please move them to the PR description, to avoid spamming the issues with references to the commit, and so this bot can automatically canonicalize them to avoid issues with subtree.

@alexcrichton
Copy link
Member

@bors r+

@rust-bors rust-bors bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 14, 2026
@rust-bors
Copy link
Contributor

rust-bors bot commented Jan 14, 2026

📌 Commit c1bcae0 has been approved by alexcrichton

It is now in the queue for this repository.

JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jan 14, 2026
fix: WASI threading regression by disabling pthread usage

PR rust-lang#147572 changed WASI to use the Unix threading implementation, but WASI does not support threading. When the Unix code tries to call pthread_create, it fails with EAGAIN, causing libraries like rayon to panic when trying to initialize their global thread pool.

The old wasip1/wasip2 implementations:
- wasip1: Threading conditionally available with atomics (experimental)
- wasip2: Threading unconditionally unsupported

This fix restores that behavior by disabling pthread-based threading for all WASI targets:
1. Guard the pthread-based Thread implementation with #[cfg(not(target_os = "wasi"))]
2. Provide an unsupported stub (Thread(!)) for WASI
3. Return Err(io::Error::UNSUPPORTED_PLATFORM) when Thread::new is called

Fixes the regression where rayon-based code (e.g., lopdf in PDF handling) panicked on WASI after nightly-2025-12-10.

Before fix:
  pthread_create returns EAGAIN (error code 6)
  ThreadPoolBuildError { kind: IOError(Os { code: 6,
  kind: WouldBlock, message: "Resource temporarily unavailable" }) }

After fix:
  Thread::new returns Err(io::Error::UNSUPPORTED_PLATFORM)
  Libraries can gracefully handle the lack of threading support

r? @alexcrichton
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jan 14, 2026
fix: WASI threading regression by disabling pthread usage

PR rust-lang#147572 changed WASI to use the Unix threading implementation, but WASI does not support threading. When the Unix code tries to call pthread_create, it fails with EAGAIN, causing libraries like rayon to panic when trying to initialize their global thread pool.

The old wasip1/wasip2 implementations:
- wasip1: Threading conditionally available with atomics (experimental)
- wasip2: Threading unconditionally unsupported

This fix restores that behavior by disabling pthread-based threading for all WASI targets:
1. Guard the pthread-based Thread implementation with #[cfg(not(target_os = "wasi"))]
2. Provide an unsupported stub (Thread(!)) for WASI
3. Return Err(io::Error::UNSUPPORTED_PLATFORM) when Thread::new is called

Fixes the regression where rayon-based code (e.g., lopdf in PDF handling) panicked on WASI after nightly-2025-12-10.

Before fix:
  pthread_create returns EAGAIN (error code 6)
  ThreadPoolBuildError { kind: IOError(Os { code: 6,
  kind: WouldBlock, message: "Resource temporarily unavailable" }) }

After fix:
  Thread::new returns Err(io::Error::UNSUPPORTED_PLATFORM)
  Libraries can gracefully handle the lack of threading support

r? @alexcrichton
rust-bors bot pushed a commit that referenced this pull request Jan 14, 2026
…uwer

Rollup of 12 pull requests

Successful merges:

 - #150585 (Add a context-consistency check before emitting redundant generic-argument suggestions)
 - #150586 (rustdoc: Fix intra-doc link bugs involving type aliases and associated items)
 - #150590 (Don't try to recover keyword as non-keyword identifier )
 - #150817 (cleanup: remove borrowck handling for inline const patterns)
 - #150939 (resolve: Relax some asserts in glob overwriting and add tests)
 - #150966 (rustc_target: Remove unused Arch::PowerPC64LE)
 - #150971 (Disallow eii in statement position)
 - #151016 (fix: WASI threading regression by disabling pthread usage)
 - #151046 (compiler: Make Externally Implementable Item (eii) macros "semiopaque")
 - #151103 (mir_build: Simplify length-determination and indexing for array/slice patterns)
 - #151117 (Avoid serde dependency in build_helper when not necessary)
 - #151127 (Delete `MetaItemOrLitParser::Err`)

r? @ghost
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jan 14, 2026
fix: WASI threading regression by disabling pthread usage

PR rust-lang#147572 changed WASI to use the Unix threading implementation, but WASI does not support threading. When the Unix code tries to call pthread_create, it fails with EAGAIN, causing libraries like rayon to panic when trying to initialize their global thread pool.

The old wasip1/wasip2 implementations:
- wasip1: Threading conditionally available with atomics (experimental)
- wasip2: Threading unconditionally unsupported

This fix restores that behavior by disabling pthread-based threading for all WASI targets:
1. Guard the pthread-based Thread implementation with #[cfg(not(target_os = "wasi"))]
2. Provide an unsupported stub (Thread(!)) for WASI
3. Return Err(io::Error::UNSUPPORTED_PLATFORM) when Thread::new is called

Fixes the regression where rayon-based code (e.g., lopdf in PDF handling) panicked on WASI after nightly-2025-12-10.

Before fix:
  pthread_create returns EAGAIN (error code 6)
  ThreadPoolBuildError { kind: IOError(Os { code: 6,
  kind: WouldBlock, message: "Resource temporarily unavailable" }) }

After fix:
  Thread::new returns Err(io::Error::UNSUPPORTED_PLATFORM)
  Libraries can gracefully handle the lack of threading support

r? @alexcrichton
rust-bors bot pushed a commit that referenced this pull request Jan 14, 2026
…uwer

Rollup of 16 pull requests

Successful merges:

 - #150585 (Add a context-consistency check before emitting redundant generic-argument suggestions)
 - #150586 (rustdoc: Fix intra-doc link bugs involving type aliases and associated items)
 - #150590 (Don't try to recover keyword as non-keyword identifier )
 - #150817 (cleanup: remove borrowck handling for inline const patterns)
 - #150939 (resolve: Relax some asserts in glob overwriting and add tests)
 - #150962 (Remove `FeedConstTy` and provide ty when lowering const arg)
 - #150966 (rustc_target: Remove unused Arch::PowerPC64LE)
 - #150971 (Disallow eii in statement position)
 - #151016 (fix: WASI threading regression by disabling pthread usage)
 - #151046 (compiler: Make Externally Implementable Item (eii) macros "semiopaque")
 - #151099 (Recover parse gracefully from `<const N>`)
 - #151117 (Avoid serde dependency in build_helper when not necessary)
 - #151127 (Delete `MetaItemOrLitParser::Err`)
 - #151128 (Add temporary new bors e-mail address to the mailmap)
 - #151130 (resolve: Downgrade `ambiguous_glob_imports` to warn-by-default)
 - #151138 (Rename `rust.use-lld` to `rust.bootstrap-override-lld` in INSTALL.md)

r? @ghost
rust-bors bot pushed a commit that referenced this pull request Jan 14, 2026
…uwer

Rollup of 15 pull requests

Successful merges:

 - #150585 (Add a context-consistency check before emitting redundant generic-argument suggestions)
 - #150586 (rustdoc: Fix intra-doc link bugs involving type aliases and associated items)
 - #150590 (Don't try to recover keyword as non-keyword identifier )
 - #150817 (cleanup: remove borrowck handling for inline const patterns)
 - #150939 (resolve: Relax some asserts in glob overwriting and add tests)
 - #150962 (Remove `FeedConstTy` and provide ty when lowering const arg)
 - #150966 (rustc_target: Remove unused Arch::PowerPC64LE)
 - #150971 (Disallow eii in statement position)
 - #151016 (fix: WASI threading regression by disabling pthread usage)
 - #151046 (compiler: Make Externally Implementable Item (eii) macros "semiopaque")
 - #151099 (Recover parse gracefully from `<const N>`)
 - #151117 (Avoid serde dependency in build_helper when not necessary)
 - #151127 (Delete `MetaItemOrLitParser::Err`)
 - #151128 (Add temporary new bors e-mail address to the mailmap)
 - #151138 (Rename `rust.use-lld` to `rust.bootstrap-override-lld` in INSTALL.md)

r? @ghost
@rust-bors rust-bors bot merged commit 2c17e0e into rust-lang:main Jan 15, 2026
11 checks passed
@rustbot rustbot added this to the 1.94.0 milestone Jan 15, 2026
rust-timer added a commit that referenced this pull request Jan 15, 2026
Rollup merge of #151016 - fix_wasi_threading, r=alexcrichton

fix: WASI threading regression by disabling pthread usage

PR #147572 changed WASI to use the Unix threading implementation, but WASI does not support threading. When the Unix code tries to call pthread_create, it fails with EAGAIN, causing libraries like rayon to panic when trying to initialize their global thread pool.

The old wasip1/wasip2 implementations:
- wasip1: Threading conditionally available with atomics (experimental)
- wasip2: Threading unconditionally unsupported

This fix restores that behavior by disabling pthread-based threading for all WASI targets:
1. Guard the pthread-based Thread implementation with #[cfg(not(target_os = "wasi"))]
2. Provide an unsupported stub (Thread(!)) for WASI
3. Return Err(io::Error::UNSUPPORTED_PLATFORM) when Thread::new is called

Fixes the regression where rayon-based code (e.g., lopdf in PDF handling) panicked on WASI after nightly-2025-12-10.

Before fix:
  pthread_create returns EAGAIN (error code 6)
  ThreadPoolBuildError { kind: IOError(Os { code: 6,
  kind: WouldBlock, message: "Resource temporarily unavailable" }) }

After fix:
  Thread::new returns Err(io::Error::UNSUPPORTED_PLATFORM)
  Libraries can gracefully handle the lack of threading support

r? @alexcrichton
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Jan 15, 2026
…uwer

Rollup of 15 pull requests

Successful merges:

 - rust-lang/rust#150585 (Add a context-consistency check before emitting redundant generic-argument suggestions)
 - rust-lang/rust#150586 (rustdoc: Fix intra-doc link bugs involving type aliases and associated items)
 - rust-lang/rust#150590 (Don't try to recover keyword as non-keyword identifier )
 - rust-lang/rust#150817 (cleanup: remove borrowck handling for inline const patterns)
 - rust-lang/rust#150939 (resolve: Relax some asserts in glob overwriting and add tests)
 - rust-lang/rust#150962 (Remove `FeedConstTy` and provide ty when lowering const arg)
 - rust-lang/rust#150966 (rustc_target: Remove unused Arch::PowerPC64LE)
 - rust-lang/rust#150971 (Disallow eii in statement position)
 - rust-lang/rust#151016 (fix: WASI threading regression by disabling pthread usage)
 - rust-lang/rust#151046 (compiler: Make Externally Implementable Item (eii) macros "semiopaque")
 - rust-lang/rust#151099 (Recover parse gracefully from `<const N>`)
 - rust-lang/rust#151117 (Avoid serde dependency in build_helper when not necessary)
 - rust-lang/rust#151127 (Delete `MetaItemOrLitParser::Err`)
 - rust-lang/rust#151128 (Add temporary new bors e-mail address to the mailmap)
 - rust-lang/rust#151138 (Rename `rust.use-lld` to `rust.bootstrap-override-lld` in INSTALL.md)

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

Labels

O-wasi Operating system: Wasi, Webassembly System Interface S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants