Skip to content

Conversation

@alexcrichton
Copy link
Collaborator

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.

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.
Copy link
Member

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

Technically I believe this is not one of the allowed error codes for this function:

https://man7.org/linux/man-pages/man3/pthread_create.3.html

@alexcrichton
Copy link
Collaborator Author

In my experience man pages list a subset of possible errors from functions, so I wouldn't personally consider man pages as an exhaustive source of truth for all possible errors. In the abstract "not supported" here feels more appropriate to me than "try again later" since it's impossible for this operation to succeed on wasm32-wasip{1,2} targets

@sbc100
Copy link
Member

sbc100 commented Jan 13, 2026

In my experience man pages list a subset of possible errors from functions, so I wouldn't personally consider man pages as an exhaustive source of truth for all possible errors. In the abstract "not supported" here feels more appropriate to me than "try again later" since it's impossible for this operation to succeed on wasm32-wasip{1,2} targets

EAGAIN is also returned when you hit the limit of the number of threads you can create, so we could make the argument that on single threaded systems are just systems with a thread limit of 1 ?

@sbc100
Copy link
Member

sbc100 commented Jan 13, 2026

Is this more official than the linux man page maybe? https://pubs.opengroup.org/onlinepubs/000095399/functions/pthread_create.html ? It still looks like a man page but at least its published by the opengroup?

@alexcrichton
Copy link
Collaborator Author

I really don't think we should be using man pages/opengroup/etc as an ironclad set of guidelines for what is possible to return as an error code. For example their documentation of sigaction says that it can only return EINVAL but in Linux it might return EFAULT. My impression has always been that documented errors are examples, but not exhaustive. Especially once you factor in things like seccomp filters and such where arbitrary syscalls can be denied the documented list of return values is almost always never exhaustive.

@sbc100
Copy link
Member

sbc100 commented Jan 13, 2026

If I ask AI "can pthead_create set errno to ENOTSUP and be spec compliant" is says no, but I guess its just using the same docs I'm referring to.

Note that emscripten's stub returns EAGAIN and we've not had any complaints or requests to change it so far: https://github.com/emscripten-core/emscripten/blob/d3d9e0a1ba2d57b812e63e0b96b24a850744ddc5/system/lib/pthread/library_pthread_stub.c#L94-L96.

I don't think its a huge deal either way, but couldn't the rust code instead be tought to fall back in the case of EAGAIN for pthread_create? I guess we would need to change the emscripten version too if rust doesn't want to change?

@sbc100
Copy link
Member

sbc100 commented Jan 13, 2026

Hmm, AI also says: "The POSIX standard explicitly states that implementations may support additional errors not included in the standard's list for a particular function.", so maybe you are right? If we do land this change we should probably mirror it in emscripten though.

@alexcrichton
Copy link
Collaborator Author

For the particular error code here, the motivation is from rust-lang/rust#151016 where std::thread::spawn, in Rust, would return an error with io::ErrorKind::Unsupported historically. In rust-lang/rust#147572 I changed much of the Rust standard library (including thread creation) to "just" use the libc-based symbols instead. This means that the source of errors shifted from Rust's standard library to wasi-libc. Crates in the Rust ecosystem pattern-matched on the Unsupported error kind to gracefully handle the inability to spawn threads, and so this change in error code caused an issue there.

The fixes here range from updating the crate in question to handle another error code, to having a special case in Rust's standard library for returning this error code, to wasi-libc changing its origin error code. Personally I opted for the last of these options, changing wasi-libc, as it felt the most "core" in terms of correcting the problem at the source. Rust's standard library will probably carry a WASI special case from rust-lang/rust#151016 no matter what while it waits for wasi-sdk to update, however.

For Emscripten I don't have a good handle on the fallout of such a change. I'd still naively say that ENOTSUP is a more direct error code than EAGAIN, but just like Rust libraries were depending on the old error code I wouldn't be surprised if something in the wild for Emscripten depended on the EAGAIN error code, making the change difficult.

Copy link
Member

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

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

OK, LGTM.

Equivalent emscripten change is emscripten-core/emscripten#26105

sbc100 added a commit to sbc100/emscripten that referenced this pull request Jan 14, 2026
sbc100 added a commit to sbc100/emscripten that referenced this pull request Jan 14, 2026
sbc100 added a commit to sbc100/emscripten that referenced this pull request Jan 14, 2026
sbc100 added a commit to sbc100/emscripten that referenced this pull request Jan 14, 2026
sbc100 added a commit to sbc100/emscripten that referenced this pull request Jan 14, 2026
cdmurph32 added a commit to cdmurph32/rust that referenced this pull request Jan 14, 2026
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
cdmurph32 added a commit to cdmurph32/rust that referenced this pull request Jan 14, 2026
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
@alexcrichton alexcrichton merged commit 5aea520 into WebAssembly:main Jan 14, 2026
27 checks passed
@alexcrichton alexcrichton deleted the pthread-create-enotsup branch January 14, 2026 02:46
cdmurph32 added a commit to cdmurph32/rust that referenced this pull request Jan 14, 2026
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
sbc100 added a commit to sbc100/emscripten that referenced this pull request Jan 14, 2026
sbc100 added a commit to emscripten-core/emscripten that referenced this pull request Jan 14, 2026
tyhdefu pushed a commit to tyhdefu/rust that referenced this pull request Jan 15, 2026
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants