Skip to content

Commit 70b6fd3

Browse files
authored
Fix TSS key exhaustion in implicitly_convertible() (gh-5975) (#6020)
Replace `static thread_specific_storage<int>` with `thread_local bool` in the implicit conversion reentrancy guard. Since implicitly_convertible is a template function, each unique <InputType, OutputType> pair created its own TSS key via PyThread_tss_create(). Projects with hundreds of modules and many implicit conversions could exhaust PTHREAD_KEYS_MAX (1024 on Linux, 512 on macOS), especially on Python 3.12+ where CPython itself consumes more TSS keys for subinterpreter support. thread_local bool is safe here because: - bool is trivially destructible, so it works on all C++11 platforms including older macOS (the concern that motivated the TSS approach in PR #5777 applied only to types with non-trivial destructors needing __cxa_thread_atexit runtime support) - Each thread gets its own copy, so it is thread-safe for free-threading - Subinterpreter sharing is benign: the guard prevents recursive implicit conversions on the same thread regardless of which interpreter is active - The v3.0.0 code already used thread_local bool under Py_GIL_DISABLED This effectively reverts the core change from PR #5777 while keeping the non-copyable/non-movable set_flag guard. Made-with: Cursor
1 parent 3b62426 commit 70b6fd3

File tree

1 file changed

+4
-7
lines changed

1 file changed

+4
-7
lines changed

include/pybind11/pybind11.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3557,13 +3557,10 @@ typing::Iterator<ValueType> make_value_iterator(Type &value, Extra &&...extra) {
35573557

35583558
template <typename InputType, typename OutputType>
35593559
void implicitly_convertible() {
3560-
static int tss_sentinel_pointee = 1; // arbitrary value
35613560
struct set_flag {
3562-
thread_specific_storage<int> &flag;
3563-
explicit set_flag(thread_specific_storage<int> &flag_) : flag(flag_) {
3564-
flag = &tss_sentinel_pointee; // trick: the pointer itself is the sentinel
3565-
}
3566-
~set_flag() { flag.reset(nullptr); }
3561+
bool &flag;
3562+
explicit set_flag(bool &flag_) : flag(flag_) { flag_ = true; }
3563+
~set_flag() { flag = false; }
35673564

35683565
// Prevent copying/moving to ensure RAII guard is used safely
35693566
set_flag(const set_flag &) = delete;
@@ -3572,7 +3569,7 @@ void implicitly_convertible() {
35723569
set_flag &operator=(set_flag &&) = delete;
35733570
};
35743571
auto implicit_caster = [](PyObject *obj, PyTypeObject *type) -> PyObject * {
3575-
static thread_specific_storage<int> currently_used;
3572+
thread_local bool currently_used = false;
35763573
if (currently_used) { // implicit conversions are non-reentrant
35773574
return nullptr;
35783575
}

0 commit comments

Comments
 (0)