Re: Would an increase to PTHREAD_KEYS_MAX be an example of a FreeBSD ABI change? (lang/rust involved in why I ask)

From: Mark Millard <marklmi_at_yahoo.com>
Date: Thu, 14 May 2026 21:52:45 UTC
On 5/14/26 14:11, Konstantin Belousov wrote:
> On Thu, May 14, 2026 at 11:30:10AM -0700, Mark Millard wrote:
>> Would an increase to PTHREAD_KEYS_MAX be an example of a FreeBSD ABI change?
>>
>>
>> Why I ask: an active failure for at least rust use on armv7 . . .
>>
>> The armv7 1.94.0 rust-bootstrap used to try to build 1.95.0 of lang/rust
>> for armv7 gets the Thread Local Storage failure "Out of TLS keys".
>>
>> The same was true for the armv7 1.93.0 rust-bootstrap used to try to
>> build the armv7 1.94.0:
>>
>> <https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=294545>
>> (Robert Clausecker, not me.)
>>
>>
>> OpenBSD increased PTHREAD_KEYS_MAX to 512 to avoid getting such "Out of
>> TLS keys" failures, not necessarily for armv7 or for building rustc:
>>
>> <https://github.com/openbsd/src/commit/f90c65410264d8346b711e36bca7ee1f0dde5ca9>
>> (2025-11-11)
>>
>> QUOTE from OpenBSD's commit:
>> bump PTHREAD_KEYS_MAX to 512
>> the rust libstd emutls version is using one pthread_key per
>> thread-variable. it means the maximum number of thread-variable at a
>> time is PTHREAD_KEYS_MAX.
>>
>> with recent rustc 1.91.0, we start hiting the limit while compiling some
>> programs (like sysutils/rustic).
>>
>> ok robert@
>> "Let's go 512 and hope someone eventually does proper TLS" deraadt@
>> END QUOTE
>>
>> I think they had 256 previous to the change, not just 128.
>>
>> (The "libstd emutls version" may not be always involved for rust's
>> context? Also: the failure type is apparently not limited to building
>> the rust compiler/toolchain. The overall issue is not FreeBSD OS specific.)
>>
>>
>> Note: It appears that the official FreeBSD port-package building for
>> armv7 stopped shortly before this started to be an issue. That is why
>> fallout reports have not been accumulating for the "out of TLS keys" issue.
>>
>>
>> If increasing PTHREAD_KEYS_MAX is an ABI change, an implication is that
>> 14.*-RELEASE and 15.*-RELEASE could not be changed, possibly eliminating
>> rust use on armv7 going forward.
>>
>>
>> Note:
>> It is my understanding that alternate Thread Local Storage models for
>> rust require use of rust-nightly (unstable) builds instead of stable
>> rust builds.
> 
> Surely increasing the value would be ABI breaking change, but perhaps
> this is the change that we can tolerate.
> 
> OTOH, we do have compiler-supported TLS, so I am at least curious why
> Rust doesn't use it.
> 
> 

For the OTOH . . .

https://github.com/rust-lang/rust/issues/29594 is about "Tracking issue
for thread_local stabilization #29594" and goes back to 2015 with the
most recent comment being on May 14, 2025. It is still not classified as
stable. I do not claim to have gone through it all.

QUOTE (of google AI results about the issues holding target_thread_local
up for stabilization for rust):

Key Aspects of the Stabilization Effort

Platform Support: The major hurdle is that #[thread_local] translates
directly to LLVM's thread_local attribute, which is not supported
identically across all platforms.

Destructors & Safety: Unlike the stable thread_local! macro,
#[thread_local] does not implicitly support destructors on all
platforms. Issues arise regarding when the memory is cleaned up.

Alternative: The stable alternative is the thread_local! macro, which is
safe and manages initialization and destruction, but has higher overhead.

Current State

As of 2026, the #[thread_local] attribute remains experimental, with
efforts focused on defining safe abstractions that handle platform
differences and initialization behavior consistently.

END QUOTE

Additional detail shows up in another google AI result:

QUOTE
When Rust Uses OS pthreads TLS Keys:

Platform Constraints: On platforms lacking native #[thread_local]
support (e.g., some embedded systems or specific older Unix versions),
Rust falls back to pthread_key_create and pthread_setspecific to
implement thread-local storage.

Dynamic TLS Initialization: When using thread_local! macros, Rust might
use OS-based TLS to manage the initialization of thread-local variables
that are not known at compile-time, especially when they need a
destructor called upon thread exit.

Thread Exit Destructors: If a thread-local variable requires a
destructor to run when the thread terminates, Rust often uses pthreads
keys because they support associating a destructor function
(pthread_key_create) with the TLS value.

Internal Library Components: Some components within the standard library
(std) use OS TLS to store thread-specific information (e.g., panicking
state, thread handles).

Caveats and Details

Main Thread Limitations: On many Unix systems, when pthread-based TLS is
used, destructors for thread-local storage are not run on the main
thread when it exits.

Performance: Using pthreads keys (pthread_setspecific) is generally
slower than #[thread_local] (which uses ELF TLS) because it involves
calling into the OS, often allocating memory on the first access.

Async Signal Safety: Accessing thread_local! variables is not typically
async-signal-safe because they can call into pthread functions which are
not safe to use in signal handlers.
END QUOTE


Reads to me like an effort to find one, clean abstraction to span such a
range. Not having found such, the variability and control of it leads to
the "unstable" classification where use is only allowed via nightly
builds instead of via stable builds.


-- 
===
Mark Millard
marklmi at yahoo.com