GeezerVison is showing github.com — gh-130115: fix thread identifiers for 32-bit musl by vfazio · Pull Request #130391 · python/cpython
Conversation
vfazio
commented
Feb 21, 2025
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
CPython's pthread-based thread identifier relies on pthread_t being able to be represented as an unsigned integer type.
This is true in most Linux libc implementations where it's defined as an unsigned long, however musl typedefs it as a struct *.
If the pointer has the high bit set and is cast to PyThread_ident_t, the resultant value can be sign-extended (https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Arrays-and-pointers-implementation.html). This can cause issues when comparing against threading._MainThread's identifier. The main thread's identifier value is retrieved via _get_main_thread_ident which is backed by an unsigned long which truncates sign extended bits.
>>> hex(threading.main_thread().ident)
'0xb6f33f3c'
>>> hex(threading.current_thread().ident)
'0xffffffffb6f33f3c'
Work around this by making a new function which translates a pthread_t to PyThread_ident_t by casting through an integer type of the appropriate size to avoid sign extension. Factoring this out allows us to replace the internal logic in the future with some min-heap or other data structure to manage pthread_t objects in a more opaque fashion.
Note musl isn't "officially" supported in PEP 11, however platform detection was added in c163d7f and similar PRs have been merged in the past which target it 5633c4f
This PR is intended to be a "minimum" to get this working. Longer term there should maybe be work to keep pthread_t opaque and not make assumptions about its type.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
CPython's pthread-based thread identifier relies on pthread_t being able
to be represented as an unsigned integer type.
This is true in most Linux libc implementations where it's defined as an
unsigned long, however musl typedefs it as a struct *.
If the pointer has the high bit set and is cast to PyThread_ident_t, the
resultant value can be sign-extended [0]. This can cause issues when
comparing against threading._MainThread's identifier. The main thread's
identifier value is retrieved via _get_main_thread_ident which is backed
by an unsigned long which truncates sign extended bits.
>>> hex(threading.main_thread().ident)
'0xb6f33f3c'
>>> hex(threading.current_thread().ident)
'0xffffffffb6f33f3c'
Work around this by conditionally compiling in some code for non-glibc
based Linux platforms that are at risk of sign-extension to return a
PyLong based on the main thread's unsigned long thread identifier if the
current thread is the main thread.
[0]: https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Arrays-and-pointers-implementation.html
Signed-off-by: Vincent Fazio <[email protected]>
|
Drafting this.
After doing some initial testing, changing only get_ident is not sufficient for the threading unit tests to pass. There are numerous edge cases to account for, namely things like:
- test_2_join_in_forked_process requiring ThreadHandle.join() to work
- test_excepthook_thread_None requiring the thread identifier in the exception string to match
- test_main_thread_after_fork_from_foreign_thread(dummy=True) converting a dummy thread to a main thread testing that the identifiers are the same
- PyThread_start_joinable_thread creates its own identifier and there's expectation's that the handle's identifier will be consistent
(test_foreign_thread, test_reinit_tls_after_fork, test_ident_of_no_threading_threads, test_main_thread_after_fork_from_nonmain_thread)
I plan on testing on actual hardware (RPi on 32bit Alpine) at some point, but I've emulated this environment in Docker using arm32v7/alpine & the qemu user space emulator in the mean time.
I'll rework the patch and then validate on hardware as well
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Move this to a static function so all callers have consistent behavior.
Signed-off-by: Vincent Fazio <[email protected]>
vfazio
changed the title
gh-130115: fix return value of threading.get_ident for the main thread on 32bit musl
gh-130115: fix thread identifiers for 32-bit musl
Feb 23, 2025
vfazio
commented
Feb 23, 2025
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
I built python --with-assertions for warm and fuzzies and ran the threading tests w/o issue:
5c3ddfcc4230:/tmp/tmp.pdpMFD/cpython# cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.21.3
PRETTY_NAME="Alpine Linux v3.21"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
5c3ddfcc4230:/tmp/tmp.pdpMFD/cpython# /lib/ld-musl-armhf.so.1
musl libc (armhf)
Version 1.2.5
Dynamic Program Loader
Usage: /lib/ld-musl-armhf.so.1 [options] [--] pathname [args]
5c3ddfcc4230:/tmp/tmp.pdpMFD/cpython# git rev-parse HEAD
af1233b283e1fd6343dad8a2a2605a8009bb4556
5c3ddfcc4230:/tmp/tmp.pdpMFD/cpython# ./python -m test -v test_threading
== CPython 3.14.0a5+ (heads/vfazio-thread_get_ident:af1233b283, Feb 23 2025, 02:09:24) [GCC 14.2.0]
== Linux-6.8.0-51-generic-armv7l-with little-endian
== Python build: release with_assert
== cwd: /tmp/tmp.pdpMFD/cpython/build/test_python_worker_176382æ
== CPU count: 32
== encodings: locale=UTF-8 FS=utf-8
== resources: all test resources are disabled, use -u option to unskip tests
Using random seed: 3615652272
0:00:00 load avg: 0.15 Run 1 test sequentially in a single process
0:00:00 load avg: 0.15 [1/1] test_threading
test_atexit_after_shutdown (test.test_threading.AtexitTests.test_atexit_after_shutdown) ... ok
test_atexit_called_once (test.test_threading.AtexitTests.test_atexit_called_once) ... ok
test_atexit_output (test.test_threading.AtexitTests.test_atexit_output) ... ok
test_abort (test.test_threading.BarrierTests.test_abort)
Test that an abort will put the barrier in a broken state ... ok
test_abort_and_reset (test.test_threading.BarrierTests.test_abort_and_reset)
Test that a barrier can be reset after being broken. ... ok
test_action (test.test_threading.BarrierTests.test_action)
Test the 'action' callback ... ok
test_barrier (test.test_threading.BarrierTests.test_barrier)
Test that a barrier is passed in lockstep ... ok
test_barrier_10 (test.test_threading.BarrierTests.test_barrier_10)
Test that a barrier works for 10 consecutive runs ... ok
test_constructor (test.test_threading.BarrierTests.test_constructor) ... ok
test_default_timeout (test.test_threading.BarrierTests.test_default_timeout)
Test the barrier's default timeout ... ok
test_repr (test.test_threading.BarrierTests.test_repr) ... ok
test_reset (test.test_threading.BarrierTests.test_reset)
Test that a 'reset' on a barrier frees the waiting threads ... ok
test_single_thread (test.test_threading.BarrierTests.test_single_thread) ... ok
test_timeout (test.test_threading.BarrierTests.test_timeout)
Test wait(timeout) ... ok
test_wait_return (test.test_threading.BarrierTests.test_wait_return)
test the return value from barrier.wait ... ok
test_acquire (test.test_threading.BoundedSemaphoreTests.test_acquire) ... ok
test_acquire_contended (test.test_threading.BoundedSemaphoreTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.BoundedSemaphoreTests.test_acquire_destroy) ... ok
test_acquire_timeout (test.test_threading.BoundedSemaphoreTests.test_acquire_timeout) ... ok
test_constructor (test.test_threading.BoundedSemaphoreTests.test_constructor) ... ok
test_default_value (test.test_threading.BoundedSemaphoreTests.test_default_value) ... ok
test_multirelease (test.test_threading.BoundedSemaphoreTests.test_multirelease) ... ok
test_release_unacquired (test.test_threading.BoundedSemaphoreTests.test_release_unacquired) ... ok
test_repr (test.test_threading.BoundedSemaphoreTests.test_repr) ... ok
test_try_acquire (test.test_threading.BoundedSemaphoreTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.BoundedSemaphoreTests.test_try_acquire_contended) ... ok
test_with (test.test_threading.BoundedSemaphoreTests.test_with) ... ok
test__is_owned (test.test_threading.CRLockTests.test__is_owned) ... ok
test_acquire_contended (test.test_threading.CRLockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.CRLockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.CRLockTests.test_acquire_release) ... ok
test_constructor (test.test_threading.CRLockTests.test_constructor) ... ok
test_different_thread (test.test_threading.CRLockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.CRLockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.CRLockTests.test_reacquire) ... ok
test_recursion_count (test.test_threading.CRLockTests.test_recursion_count) ... ok
test_release_save_unacquired (test.test_threading.CRLockTests.test_release_save_unacquired) ... ok
test_release_unacquired (test.test_threading.CRLockTests.test_release_unacquired) ... ok
test_repr (test.test_threading.CRLockTests.test_repr) ... ok
test_signature (test.test_threading.CRLockTests.test_signature) ... ok
test_thread_leak (test.test_threading.CRLockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.CRLockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.CRLockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.CRLockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.CRLockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.CRLockTests.test_weakref_exists) ... ok
test_with (test.test_threading.CRLockTests.test_with) ... ok
test__is_owned (test.test_threading.ConditionAsRLockTests.test__is_owned) ... ok
test_acquire_contended (test.test_threading.ConditionAsRLockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.ConditionAsRLockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.ConditionAsRLockTests.test_acquire_release) ... ok
test_constructor (test.test_threading.ConditionAsRLockTests.test_constructor) ... ok
test_different_thread (test.test_threading.ConditionAsRLockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.ConditionAsRLockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.ConditionAsRLockTests.test_reacquire) ... ok
test_recursion_count (test.test_threading.ConditionAsRLockTests.test_recursion_count) ... skipped 'Condition does not expose _recursion_count()'
test_release_save_unacquired (test.test_threading.ConditionAsRLockTests.test_release_save_unacquired) ... ok
test_release_unacquired (test.test_threading.ConditionAsRLockTests.test_release_unacquired) ... ok
test_repr (test.test_threading.ConditionAsRLockTests.test_repr) ... ok
test_thread_leak (test.test_threading.ConditionAsRLockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.ConditionAsRLockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.ConditionAsRLockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.ConditionAsRLockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.ConditionAsRLockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.ConditionAsRLockTests.test_weakref_exists) ... ok
test_with (test.test_threading.ConditionAsRLockTests.test_with) ... ok
test_acquire (test.test_threading.ConditionTests.test_acquire) ... ok
test_notify (test.test_threading.ConditionTests.test_notify) ... ok
test_timeout (test.test_threading.ConditionTests.test_timeout) ... ok
test_unacquired_notify (test.test_threading.ConditionTests.test_unacquired_notify) ... ok
test_unacquired_wait (test.test_threading.ConditionTests.test_unacquired_wait) ... ok
test_waitfor (test.test_threading.ConditionTests.test_waitfor) ... ok
test_waitfor_timeout (test.test_threading.ConditionTests.test_waitfor_timeout) ... ok
test_at_fork_reinit (test.test_threading.EventTests.test_at_fork_reinit) ... ok
test_is_set (test.test_threading.EventTests.test_is_set) ... ok
test_notify (test.test_threading.EventTests.test_notify) ... ok
test_repr (test.test_threading.EventTests.test_repr) ... ok
test_set_and_clear (test.test_threading.EventTests.test_set_and_clear) ... ok
test_timeout (test.test_threading.EventTests.test_timeout) ... ok
test_custom_excepthook (test.test_threading.ExceptHookTests.test_custom_excepthook) ... ok
test_custom_excepthook_fail (test.test_threading.ExceptHookTests.test_custom_excepthook_fail) ... ok
test_excepthook (test.test_threading.ExceptHookTests.test_excepthook) ... ok
test_excepthook_thread_None (test.test_threading.ExceptHookTests.test_excepthook_thread_None) ... ok
test_original_excepthook (test.test_threading.ExceptHookTests.test_original_excepthook) ... ok
test_system_exit (test.test_threading.ExceptHookTests.test_system_exit) ... ok
test_can_interrupt_tight_loops (test.test_threading.InterruptMainTests.test_can_interrupt_tight_loops) ... ok
test_interrupt_main_invalid_signal (test.test_threading.InterruptMainTests.test_interrupt_main_invalid_signal) ... ok
test_interrupt_main_mainthread (test.test_threading.InterruptMainTests.test_interrupt_main_mainthread) ... ok
test_interrupt_main_noerror (test.test_threading.InterruptMainTests.test_interrupt_main_noerror) ... ok
test_interrupt_main_subthread (test.test_threading.InterruptMainTests.test_interrupt_main_subthread) ... ok
test_interrupt_main_with_signal_handler (test.test_threading.InterruptMainTests.test_interrupt_main_with_signal_handler) ... ok
test_acquire_contended (test.test_threading.LockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.LockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.LockTests.test_acquire_release) ... ok
test_at_fork_reinit (test.test_threading.LockTests.test_at_fork_reinit) ... ok
test_constructor (test.test_threading.LockTests.test_constructor) ... ok
test_different_thread (test.test_threading.LockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.LockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.LockTests.test_reacquire) ... ok
test_repr (test.test_threading.LockTests.test_repr) ... ok
test_state_after_timeout (test.test_threading.LockTests.test_state_after_timeout) ... ok
test_thread_leak (test.test_threading.LockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.LockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.LockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.LockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.LockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.LockTests.test_weakref_exists) ... ok
test_with (test.test_threading.LockTests.test_with) ... ok
test__all__ (test.test_threading.MiscTestCase.test__all__) ... ok
test_change_name (test.test_threading.MiscTestCase.test_change_name) ... ok
test_set_name (test.test_threading.MiscTestCase.test_set_name) ... ok
test__is_owned (test.test_threading.PyRLockTests.test__is_owned) ... ok
test_acquire_contended (test.test_threading.PyRLockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.PyRLockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.PyRLockTests.test_acquire_release) ... ok
test_constructor (test.test_threading.PyRLockTests.test_constructor) ... ok
test_different_thread (test.test_threading.PyRLockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.PyRLockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.PyRLockTests.test_reacquire) ... ok
test_recursion_count (test.test_threading.PyRLockTests.test_recursion_count) ... ok
test_release_save_unacquired (test.test_threading.PyRLockTests.test_release_save_unacquired) ... ok
test_release_unacquired (test.test_threading.PyRLockTests.test_release_unacquired) ... ok
test_repr (test.test_threading.PyRLockTests.test_repr) ... ok
test_thread_leak (test.test_threading.PyRLockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.PyRLockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.PyRLockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.PyRLockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.PyRLockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.PyRLockTests.test_weakref_exists) ... ok
test_with (test.test_threading.PyRLockTests.test_with) ... ok
test_acquire (test.test_threading.SemaphoreTests.test_acquire) ... ok
test_acquire_contended (test.test_threading.SemaphoreTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.SemaphoreTests.test_acquire_destroy) ... ok
test_acquire_timeout (test.test_threading.SemaphoreTests.test_acquire_timeout) ... ok
test_constructor (test.test_threading.SemaphoreTests.test_constructor) ... ok
test_default_value (test.test_threading.SemaphoreTests.test_default_value) ... ok
test_multirelease (test.test_threading.SemaphoreTests.test_multirelease) ... ok
test_release_unacquired (test.test_threading.SemaphoreTests.test_release_unacquired) ... ok
test_repr (test.test_threading.SemaphoreTests.test_repr) ... ok
test_try_acquire (test.test_threading.SemaphoreTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.SemaphoreTests.test_try_acquire_contended) ... ok
test_with (test.test_threading.SemaphoreTests.test_with) ... ok
test_daemon_threads_fatal_error (test.test_threading.SubinterpThreadingTests.test_daemon_threads_fatal_error) ... ok
test_daemon_threads_not_allowed (test.test_threading.SubinterpThreadingTests.test_daemon_threads_not_allowed) ... ok
test_threads_join (test.test_threading.SubinterpThreadingTests.test_threads_join) ... ok
test_threads_join_2 (test.test_threading.SubinterpThreadingTests.test_threads_join_2) ... ok
test_threads_join_with_no_main (test.test_threading.SubinterpThreadingTests.test_threads_join_with_no_main) ... ok
test_threads_not_allowed (test.test_threading.SubinterpThreadingTests.test_threads_not_allowed) ... ok
test_1_join_on_shutdown (test.test_threading.ThreadJoinOnShutdown.test_1_join_on_shutdown) ... ok
test_2_join_in_forked_process (test.test_threading.ThreadJoinOnShutdown.test_2_join_in_forked_process) ... ok
test_3_join_in_forked_from_thread (test.test_threading.ThreadJoinOnShutdown.test_3_join_in_forked_from_thread) ... ok
test_4_daemon_threads (test.test_threading.ThreadJoinOnShutdown.test_4_daemon_threads) ... ok
test_clear_threads_states_after_fork (test.test_threading.ThreadJoinOnShutdown.test_clear_threads_states_after_fork) ... ok
test_reinit_tls_after_fork (test.test_threading.ThreadJoinOnShutdown.test_reinit_tls_after_fork) ... ok
test_thread_from_thread (test.test_threading.ThreadJoinOnShutdown.test_thread_from_thread) ... ok
test_BoundedSemaphore_limit (test.test_threading.ThreadTests.test_BoundedSemaphore_limit) ... ok
test_PyThreadState_SetAsyncExc (test.test_threading.ThreadTests.test_PyThreadState_SetAsyncExc) ... skipped "No module named '_ctypes'"
test_args_argument (test.test_threading.ThreadTests.test_args_argument) ... ok
test_boolean_target (test.test_threading.ThreadTests.test_boolean_target) ... ok
test_daemon_param (test.test_threading.ThreadTests.test_daemon_param) ... ok
test_dummy_thread_after_fork (test.test_threading.ThreadTests.test_dummy_thread_after_fork) ... ok
test_enumerate_after_join (test.test_threading.ThreadTests.test_enumerate_after_join) ... ok
test_finalization_shutdown (test.test_threading.ThreadTests.test_finalization_shutdown) ... ok
test_finalize_daemon_thread_hang (test.test_threading.ThreadTests.test_finalize_daemon_thread_hang) ... ok
test_finalize_running_thread (test.test_threading.ThreadTests.test_finalize_running_thread) ... skipped "No module named '_ctypes'"
test_finalize_with_trace (test.test_threading.ThreadTests.test_finalize_with_trace) ... ok
test_foreign_thread (test.test_threading.ThreadTests.test_foreign_thread) ... ok
test_frame_tstate_tracing (test.test_threading.ThreadTests.test_frame_tstate_tracing) ... ok
test_getprofile (test.test_threading.ThreadTests.test_getprofile) ... ok
test_getprofile_all_threads (test.test_threading.ThreadTests.test_getprofile_all_threads) ... ok
test_gettrace (test.test_threading.ThreadTests.test_gettrace) ... ok
test_gettrace_all_threads (test.test_threading.ThreadTests.test_gettrace_all_threads) ... ok
test_ident_of_no_threading_threads (test.test_threading.ThreadTests.test_ident_of_no_threading_threads) ... ok
test_import_from_another_thread (test.test_threading.ThreadTests.test_import_from_another_thread) ... ok
test_is_alive_after_fork (test.test_threading.ThreadTests.test_is_alive_after_fork) ... ok
test_join_from_multiple_threads (test.test_threading.ThreadTests.test_join_from_multiple_threads) ... ok
test_join_nondaemon_on_shutdown (test.test_threading.ThreadTests.test_join_nondaemon_on_shutdown) ... ok
test_join_with_timeout (test.test_threading.ThreadTests.test_join_with_timeout) ... ok
test_leak_without_join (test.test_threading.ThreadTests.test_leak_without_join) ... ok
test_limbo_cleanup (test.test_threading.ThreadTests.test_limbo_cleanup) ... ok
test_locals_at_exit (test.test_threading.ThreadTests.test_locals_at_exit) ... ok
test_lock_no_args (test.test_threading.ThreadTests.test_lock_no_args) ... ok
test_lock_no_subclass (test.test_threading.ThreadTests.test_lock_no_subclass) ... ok
test_lock_or_none (test.test_threading.ThreadTests.test_lock_or_none) ... ok
test_main_thread (test.test_threading.ThreadTests.test_main_thread) ... ok
test_main_thread_after_fork (test.test_threading.ThreadTests.test_main_thread_after_fork) ... ok
test_main_thread_after_fork_from_dummy_thread (test.test_threading.ThreadTests.test_main_thread_after_fork_from_dummy_thread) ... ok
test_main_thread_after_fork_from_foreign_thread (test.test_threading.ThreadTests.test_main_thread_after_fork_from_foreign_thread) ... ok
test_main_thread_after_fork_from_nonmain_thread (test.test_threading.ThreadTests.test_main_thread_after_fork_from_nonmain_thread) ... ok
test_main_thread_during_shutdown (test.test_threading.ThreadTests.test_main_thread_during_shutdown) ... ok
test_name (test.test_threading.ThreadTests.test_name) ... ok
test_no_refcycle_through_target (test.test_threading.ThreadTests.test_no_refcycle_through_target) ... ok
test_old_threading_api (test.test_threading.ThreadTests.test_old_threading_api) ... ok
test_repr_daemon (test.test_threading.ThreadTests.test_repr_daemon) ... ok
test_repr_stopped (test.test_threading.ThreadTests.test_repr_stopped) ... ok
test_start_new_thread_at_finalization (test.test_threading.ThreadTests.test_start_new_thread_at_finalization) ... ok
test_start_new_thread_failed (test.test_threading.ThreadTests.test_start_new_thread_failed) ... skipped 'RLIMIT_NPROC had no effect; probably superuser'
test_various_ops (test.test_threading.ThreadTests.test_various_ops) ... task <thread 0> will run for 72.2 usec
1 tasks are running
task <thread 1> will run for 98.7 usec
task <thread 0> done
task <thread 2> will run for 61.4 usec
task <thread 3> will run for 52.4 usec
task <thread 4> will run for 44.5 usec
task <thread 5> will run for 14.9 usec
task <thread 6> will run for 66.8 usec
task <thread 7> will run for 45.8 usec
task <thread 8> will run for 48.9 usec
task <thread 9> will run for 94.0 usec
waiting for all tasks to complete
2 tasks are running
<thread 0> is finished. 1 tasks are running
task <thread 1> done
2 tasks are running
3 tasks are running
<thread 1> is finished. 2 tasks are running
task <thread 3> done
<thread 3> is finished. 1 tasks are running
2 tasks are running
task <thread 2> done
<thread 2> is finished. 1 tasks are running
2 tasks are running
task <thread 4> done
3 tasks are running
task <thread 5> done
<thread 4> is finished. 2 tasks are running
<thread 5> is finished. 1 tasks are running
task <thread 6> done
2 tasks are running
3 tasks are running
<thread 6> is finished. 2 tasks are running
task <thread 7> done
<thread 7> is finished. 1 tasks are running
task <thread 8> done
2 tasks are running
<thread 8> is finished. 1 tasks are running
task <thread 9> done
<thread 9> is finished. 0 tasks are running
all tasks done
ok
test_various_ops_large_stack (test.test_threading.ThreadTests.test_various_ops_large_stack) ... with 1 MiB thread stack size...
task <thread 0> will run for 93.7 usec
1 tasks are running
task <thread 1> will run for 76.0 usec
task <thread 0> done
task <thread 2> will run for 50.7 usec
task <thread 3> will run for 92.3 usec
task <thread 4> will run for 11.0 usec
task <thread 5> will run for 9.0 usec
2 tasks are running
task <thread 6> will run for 2.9 usec
<thread 0> is finished. 1 tasks are running
task <thread 8> will run for 48.2 usec
task <thread 9> will run for 27.5 usec
waiting for all tasks to complete
2 tasks are running
task <thread 7> will run for 86.2 usec
task <thread 1> done
3 tasks are running
<thread 1> is finished. 2 tasks are running
task <thread 2> done
<thread 2> is finished. 1 tasks are running
task <thread 3> done
2 tasks are running
3 tasks are running
task <thread 5> done
<thread 3> is finished. 2 tasks are running
task <thread 4> done
<thread 5> is finished. 1 tasks are running
<thread 4> is finished. 0 tasks are running
1 tasks are running
2 tasks are running
3 tasks are running
task <thread 8> done
task <thread 9> done
<thread 8> is finished. 2 tasks are running
task <thread 6> done
<thread 9> is finished. 1 tasks are running
2 tasks are running
<thread 6> is finished. 1 tasks are running
task <thread 7> done
<thread 7> is finished. 0 tasks are running
all tasks done
ok
test_various_ops_small_stack (test.test_threading.ThreadTests.test_various_ops_small_stack) ... with 256 KiB thread stack size...
task <thread 0> will run for 64.8 usec
1 tasks are running
task <thread 1> will run for 25.5 usec
task <thread 0> done
task <thread 2> will run for 69.5 usec
2 tasks are running
task <thread 3> will run for 95.5 usec
task <thread 1> done
3 tasks are running
task <thread 4> will run for 26.1 usec
task <thread 5> will run for 23.0 usec
task <thread 2> done
task <thread 6> will run for 4.6 usec
task <thread 7> will run for 40.8 usec
waiting for all tasks to complete
task <thread 8> will run for 63.4 usec
<thread 0> is finished. 2 tasks are running
task <thread 9> will run for 16.8 usec
<thread 1> is finished. 1 tasks are running
<thread 2> is finished. 0 tasks are running
1 tasks are running
2 tasks are running
3 tasks are running
task <thread 4> done
<thread 4> is finished. 2 tasks are running
task <thread 3> done
task <thread 5> done
3 tasks are running
<thread 3> is finished. 2 tasks are running
<thread 5> is finished. 1 tasks are running
task <thread 6> done
2 tasks are running
3 tasks are running
<thread 6> is finished. 2 tasks are running
task <thread 7> done
task <thread 8> done
<thread 7> is finished. 1 tasks are running
2 tasks are running
<thread 8> is finished. 1 tasks are running
task <thread 9> done
<thread 9> is finished. 0 tasks are running
all tasks done
ok
test_bare_raise_in_brand_new_thread (test.test_threading.ThreadingExceptionTests.test_bare_raise_in_brand_new_thread) ... ok
test_daemonize_active_thread (test.test_threading.ThreadingExceptionTests.test_daemonize_active_thread) ... ok
test_joining_current_thread (test.test_threading.ThreadingExceptionTests.test_joining_current_thread) ... ok
test_joining_inactive_thread (test.test_threading.ThreadingExceptionTests.test_joining_inactive_thread) ... ok
test_multithread_modify_file_noerror (test.test_threading.ThreadingExceptionTests.test_multithread_modify_file_noerror) ... ok
test_print_exception (test.test_threading.ThreadingExceptionTests.test_print_exception) ... ok
test_print_exception_gh_102056 (test.test_threading.ThreadingExceptionTests.test_print_exception_gh_102056) ... ok
test_print_exception_stderr_is_none_1 (test.test_threading.ThreadingExceptionTests.test_print_exception_stderr_is_none_1) ... ok
test_print_exception_stderr_is_none_2 (test.test_threading.ThreadingExceptionTests.test_print_exception_stderr_is_none_2) ... ok
test_recursion_limit (test.test_threading.ThreadingExceptionTests.test_recursion_limit) ... ok
test_releasing_unacquired_lock (test.test_threading.ThreadingExceptionTests.test_releasing_unacquired_lock) ... ok
test_start_thread_again (test.test_threading.ThreadingExceptionTests.test_start_thread_again) ... ok
test_init_immutable_default_args (test.test_threading.TimerTests.test_init_immutable_default_args) ... ok
----------------------------------------------------------------------
Ran 213 tests in 35.318s
OK (skipped=4)
0:00:36 load avg: 0.54 [1/1] test_threading passed in 35.9 sec
== Tests result: SUCCESS ==
1 test OK.
Total duration: 36.0 sec
Total tests: run=213 skipped=4
Total test files: run=1/1
Result: SUCCESS
I then edited the ident function to always set the upper bit to force the broken behavior I was seeing in #130115 and re-ran the tests
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -315,6 +315,7 @@ _pthread_t_to_ident(pthread_t value) {
#else
PyThread_ident_t ident;
#if defined(__linux__) && !defined(__GLIBC__)
+ value = (pthread_t) ((uintptr_t) value | 0x80000000);
ident = (PyThread_ident_t) (uintptr_t) value;
assert(pthread_equal(value, (pthread_t) (uintptr_t) ident));
#else
5c3ddfcc4230:/tmp/tmp.pdpMFD/cpython# ./python -c "import threading; print(hex(threading.current_thread().ident))"
0xc0884f34
5c3ddfcc4230:/tmp/tmp.pdpMFD/cpython# ./python -c "import threading; print(threading.current_thread() is threading.main_thread())"
True
5c3ddfcc4230:/tmp/tmp.pdpMFD/cpython# ./python -m test test_threading
Using random seed: 920746765
0:00:00 load avg: 0.42 Run 1 test sequentially in a single process
0:00:00 load avg: 0.42 [1/1] test_threading
0:00:35 load avg: 0.71 [1/1] test_threading passed in 35.3 sec
== Tests result: SUCCESS ==
1 test OK.
Total duration: 35.3 sec
Total tests: run=213 skipped=4
Total test files: run=1/1
Result: SUCCESS
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
I still want to test on physical hardware and not on an emulated stack, but I have more confidence in this solution.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
forgot to revert the initial commit. Test still pass on a --with-assertions build:
05788ca7897b:/tmp/tmp.PHjnCN/cpython# ./python -m test -v test_threading
== CPython 3.14.0a5+ (heads/vfazio-thread_get_ident-dirty:dc7c4974e0, Feb 23 2025, 18:05:52) [GCC 14.2.0]
== Linux-6.8.0-51-generic-armv7l-with little-endian
== Python build: release with_assert
== cwd: /tmp/tmp.PHjnCN/cpython/build/test_python_worker_51235æ
== CPU count: 32
== encodings: locale=UTF-8 FS=utf-8
== resources: all test resources are disabled, use -u option to unskip tests
Using random seed: 3053357805
</snip>
----------------------------------------------------------------------
Ran 213 tests in 35.285s
OK (skipped=2)
0:00:35 load avg: 0.52 [1/1] test_threading passed in 35.5 sec
== Tests result: SUCCESS ==
1 test OK.
Total duration: 35.7 sec
Total tests: run=213 skipped=2
Total test files: run=1/1
Result: SUCCESS
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
tested on hardware:
rpi-fc79b4:/var/tmp/cpython# uname -a
Linux rpi-fc79b4 6.12.13-0-rpi #1-Alpine SMP Thu Feb 13 22:14:23 UTC 2025 armv7l Linux
rpi-fc79b4:/var/tmp/cpython# cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.21.3
PRETTY_NAME="Alpine Linux v3.21"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
rpi-fc79b4:/var/tmp/cpython# ./python -m test -v test_threading
== CPython 3.14.0a5+ (heads/vfazio-thread_get_ident:dc7c4974e0, Feb 24 2025, 11:45:03) [GCC 14.2.0]
== Linux-6.12.13-0-rpi-armv7l-with little-endian
== Python build: release with_assert
== cwd: /var/tmp/cpython/build/test_python_worker_12205æ
== CPU count: 4
== encodings: locale=UTF-8 FS=utf-8
== resources: all test resources are disabled, use -u option to unskip tests
Using random seed: 2651729950
0:00:00 load avg: 0.81 Run 1 test sequentially in a single process
0:00:00 load avg: 0.81 [1/1] test_threading
test_atexit_after_shutdown (test.test_threading.AtexitTests.test_atexit_after_shutdown) ... ok
test_atexit_called_once (test.test_threading.AtexitTests.test_atexit_called_once) ... ok
test_atexit_output (test.test_threading.AtexitTests.test_atexit_output) ... ok
test_abort (test.test_threading.BarrierTests.test_abort)
Test that an abort will put the barrier in a broken state ... ok
test_abort_and_reset (test.test_threading.BarrierTests.test_abort_and_reset)
Test that a barrier can be reset after being broken. ... ok
test_action (test.test_threading.BarrierTests.test_action)
Test the 'action' callback ... ok
test_barrier (test.test_threading.BarrierTests.test_barrier)
Test that a barrier is passed in lockstep ... ok
test_barrier_10 (test.test_threading.BarrierTests.test_barrier_10)
Test that a barrier works for 10 consecutive runs ... ok
test_constructor (test.test_threading.BarrierTests.test_constructor) ... ok
test_default_timeout (test.test_threading.BarrierTests.test_default_timeout)
Test the barrier's default timeout ... ok
test_repr (test.test_threading.BarrierTests.test_repr) ... ok
test_reset (test.test_threading.BarrierTests.test_reset)
Test that a 'reset' on a barrier frees the waiting threads ... ok
test_single_thread (test.test_threading.BarrierTests.test_single_thread) ... ok
test_timeout (test.test_threading.BarrierTests.test_timeout)
Test wait(timeout) ... ok
test_wait_return (test.test_threading.BarrierTests.test_wait_return)
test the return value from barrier.wait ... ok
test_acquire (test.test_threading.BoundedSemaphoreTests.test_acquire) ... ok
test_acquire_contended (test.test_threading.BoundedSemaphoreTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.BoundedSemaphoreTests.test_acquire_destroy) ... ok
test_acquire_timeout (test.test_threading.BoundedSemaphoreTests.test_acquire_timeout) ... ok
test_constructor (test.test_threading.BoundedSemaphoreTests.test_constructor) ... ok
test_default_value (test.test_threading.BoundedSemaphoreTests.test_default_value) ... ok
test_multirelease (test.test_threading.BoundedSemaphoreTests.test_multirelease) ... ok
test_release_unacquired (test.test_threading.BoundedSemaphoreTests.test_release_unacquired) ... ok
test_repr (test.test_threading.BoundedSemaphoreTests.test_repr) ... ok
test_try_acquire (test.test_threading.BoundedSemaphoreTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.BoundedSemaphoreTests.test_try_acquire_contended) ... ok
test_with (test.test_threading.BoundedSemaphoreTests.test_with) ... ok
test__is_owned (test.test_threading.CRLockTests.test__is_owned) ... ok
test_acquire_contended (test.test_threading.CRLockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.CRLockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.CRLockTests.test_acquire_release) ... ok
test_constructor (test.test_threading.CRLockTests.test_constructor) ... ok
test_different_thread (test.test_threading.CRLockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.CRLockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.CRLockTests.test_reacquire) ... ok
test_recursion_count (test.test_threading.CRLockTests.test_recursion_count) ... ok
test_release_save_unacquired (test.test_threading.CRLockTests.test_release_save_unacquired) ... ok
test_release_unacquired (test.test_threading.CRLockTests.test_release_unacquired) ... ok
test_repr (test.test_threading.CRLockTests.test_repr) ... ok
test_signature (test.test_threading.CRLockTests.test_signature) ... ok
test_thread_leak (test.test_threading.CRLockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.CRLockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.CRLockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.CRLockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.CRLockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.CRLockTests.test_weakref_exists) ... ok
test_with (test.test_threading.CRLockTests.test_with) ... ok
test__is_owned (test.test_threading.ConditionAsRLockTests.test__is_owned) ... ok
test_acquire_contended (test.test_threading.ConditionAsRLockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.ConditionAsRLockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.ConditionAsRLockTests.test_acquire_release) ... ok
test_constructor (test.test_threading.ConditionAsRLockTests.test_constructor) ... ok
test_different_thread (test.test_threading.ConditionAsRLockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.ConditionAsRLockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.ConditionAsRLockTests.test_reacquire) ... ok
test_recursion_count (test.test_threading.ConditionAsRLockTests.test_recursion_count) ... skipped 'Condition does not expose _recursion_count()'
test_release_save_unacquired (test.test_threading.ConditionAsRLockTests.test_release_save_unacquired) ... ok
test_release_unacquired (test.test_threading.ConditionAsRLockTests.test_release_unacquired) ... ok
test_repr (test.test_threading.ConditionAsRLockTests.test_repr) ... ok
test_thread_leak (test.test_threading.ConditionAsRLockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.ConditionAsRLockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.ConditionAsRLockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.ConditionAsRLockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.ConditionAsRLockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.ConditionAsRLockTests.test_weakref_exists) ... ok
test_with (test.test_threading.ConditionAsRLockTests.test_with) ... ok
test_acquire (test.test_threading.ConditionTests.test_acquire) ... ok
test_notify (test.test_threading.ConditionTests.test_notify) ... ok
test_timeout (test.test_threading.ConditionTests.test_timeout) ... ok
test_unacquired_notify (test.test_threading.ConditionTests.test_unacquired_notify) ... ok
test_unacquired_wait (test.test_threading.ConditionTests.test_unacquired_wait) ... ok
test_waitfor (test.test_threading.ConditionTests.test_waitfor) ... ok
test_waitfor_timeout (test.test_threading.ConditionTests.test_waitfor_timeout) ... ok
test_at_fork_reinit (test.test_threading.EventTests.test_at_fork_reinit) ... ok
test_is_set (test.test_threading.EventTests.test_is_set) ... ok
test_notify (test.test_threading.EventTests.test_notify) ... ok
test_repr (test.test_threading.EventTests.test_repr) ... ok
test_set_and_clear (test.test_threading.EventTests.test_set_and_clear) ... ok
test_timeout (test.test_threading.EventTests.test_timeout) ... ok
test_custom_excepthook (test.test_threading.ExceptHookTests.test_custom_excepthook) ... ok
test_custom_excepthook_fail (test.test_threading.ExceptHookTests.test_custom_excepthook_fail) ... ok
test_excepthook (test.test_threading.ExceptHookTests.test_excepthook) ... ok
test_excepthook_thread_None (test.test_threading.ExceptHookTests.test_excepthook_thread_None) ... ok
test_original_excepthook (test.test_threading.ExceptHookTests.test_original_excepthook) ... ok
test_system_exit (test.test_threading.ExceptHookTests.test_system_exit) ... ok
test_can_interrupt_tight_loops (test.test_threading.InterruptMainTests.test_can_interrupt_tight_loops) ... ok
test_interrupt_main_invalid_signal (test.test_threading.InterruptMainTests.test_interrupt_main_invalid_signal) ... ok
test_interrupt_main_mainthread (test.test_threading.InterruptMainTests.test_interrupt_main_mainthread) ... ok
test_interrupt_main_noerror (test.test_threading.InterruptMainTests.test_interrupt_main_noerror) ... ok
test_interrupt_main_subthread (test.test_threading.InterruptMainTests.test_interrupt_main_subthread) ... ok
test_interrupt_main_with_signal_handler (test.test_threading.InterruptMainTests.test_interrupt_main_with_signal_handler) ... ok
test_acquire_contended (test.test_threading.LockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.LockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.LockTests.test_acquire_release) ... ok
test_at_fork_reinit (test.test_threading.LockTests.test_at_fork_reinit) ... ok
test_constructor (test.test_threading.LockTests.test_constructor) ... ok
test_different_thread (test.test_threading.LockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.LockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.LockTests.test_reacquire) ... ok
test_repr (test.test_threading.LockTests.test_repr) ... ok
test_state_after_timeout (test.test_threading.LockTests.test_state_after_timeout) ... ok
test_thread_leak (test.test_threading.LockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.LockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.LockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.LockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.LockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.LockTests.test_weakref_exists) ... ok
test_with (test.test_threading.LockTests.test_with) ... ok
test__all__ (test.test_threading.MiscTestCase.test__all__) ... ok
test_change_name (test.test_threading.MiscTestCase.test_change_name) ... ok
test_set_name (test.test_threading.MiscTestCase.test_set_name) ... ok
test__is_owned (test.test_threading.PyRLockTests.test__is_owned) ... ok
test_acquire_contended (test.test_threading.PyRLockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.PyRLockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.PyRLockTests.test_acquire_release) ... ok
test_constructor (test.test_threading.PyRLockTests.test_constructor) ... ok
test_different_thread (test.test_threading.PyRLockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.PyRLockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.PyRLockTests.test_reacquire) ... ok
test_recursion_count (test.test_threading.PyRLockTests.test_recursion_count) ... ok
test_release_save_unacquired (test.test_threading.PyRLockTests.test_release_save_unacquired) ... ok
test_release_unacquired (test.test_threading.PyRLockTests.test_release_unacquired) ... ok
test_repr (test.test_threading.PyRLockTests.test_repr) ... ok
test_thread_leak (test.test_threading.PyRLockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.PyRLockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.PyRLockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.PyRLockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.PyRLockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.PyRLockTests.test_weakref_exists) ... ok
test_with (test.test_threading.PyRLockTests.test_with) ... ok
test_acquire (test.test_threading.SemaphoreTests.test_acquire) ... ok
test_acquire_contended (test.test_threading.SemaphoreTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.SemaphoreTests.test_acquire_destroy) ... ok
test_acquire_timeout (test.test_threading.SemaphoreTests.test_acquire_timeout) ... ok
test_constructor (test.test_threading.SemaphoreTests.test_constructor) ... ok
test_default_value (test.test_threading.SemaphoreTests.test_default_value) ... ok
test_multirelease (test.test_threading.SemaphoreTests.test_multirelease) ... ok
test_release_unacquired (test.test_threading.SemaphoreTests.test_release_unacquired) ... ok
test_repr (test.test_threading.SemaphoreTests.test_repr) ... ok
test_try_acquire (test.test_threading.SemaphoreTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.SemaphoreTests.test_try_acquire_contended) ... ok
test_with (test.test_threading.SemaphoreTests.test_with) ... ok
test_daemon_threads_fatal_error (test.test_threading.SubinterpThreadingTests.test_daemon_threads_fatal_error) ... ok
test_daemon_threads_not_allowed (test.test_threading.SubinterpThreadingTests.test_daemon_threads_not_allowed) ... ok
test_threads_join (test.test_threading.SubinterpThreadingTests.test_threads_join) ... ok
test_threads_join_2 (test.test_threading.SubinterpThreadingTests.test_threads_join_2) ... ok
test_threads_join_with_no_main (test.test_threading.SubinterpThreadingTests.test_threads_join_with_no_main) ... ok
test_threads_not_allowed (test.test_threading.SubinterpThreadingTests.test_threads_not_allowed) ... ok
test_1_join_on_shutdown (test.test_threading.ThreadJoinOnShutdown.test_1_join_on_shutdown) ... ok
test_2_join_in_forked_process (test.test_threading.ThreadJoinOnShutdown.test_2_join_in_forked_process) ... ok
test_3_join_in_forked_from_thread (test.test_threading.ThreadJoinOnShutdown.test_3_join_in_forked_from_thread) ... ok
test_4_daemon_threads (test.test_threading.ThreadJoinOnShutdown.test_4_daemon_threads) ... ok
test_clear_threads_states_after_fork (test.test_threading.ThreadJoinOnShutdown.test_clear_threads_states_after_fork) ...
ok
test_reinit_tls_after_fork (test.test_threading.ThreadJoinOnShutdown.test_reinit_tls_after_fork) ... ok
test_thread_from_thread (test.test_threading.ThreadJoinOnShutdown.test_thread_from_thread) ... ok
test_BoundedSemaphore_limit (test.test_threading.ThreadTests.test_BoundedSemaphore_limit) ... ok
test_PyThreadState_SetAsyncExc (test.test_threading.ThreadTests.test_PyThreadState_SetAsyncExc) ... started worker thread
trying nonsensical thread id
waiting for worker thread to get started
verifying worker hasn't exited
attempting to raise asynch exception in worker
waiting for worker to say it caught the exception
all OK -- joining worker
ok
test_args_argument (test.test_threading.ThreadTests.test_args_argument) ... ok
test_boolean_target (test.test_threading.ThreadTests.test_boolean_target) ... ok
test_daemon_param (test.test_threading.ThreadTests.test_daemon_param) ... ok
test_dummy_thread_after_fork (test.test_threading.ThreadTests.test_dummy_thread_after_fork) ... ok
test_enumerate_after_join (test.test_threading.ThreadTests.test_enumerate_after_join) ... ok
test_finalization_shutdown (test.test_threading.ThreadTests.test_finalization_shutdown) ... ok
test_finalize_daemon_thread_hang (test.test_threading.ThreadTests.test_finalize_daemon_thread_hang) ... ok
test_finalize_running_thread (test.test_threading.ThreadTests.test_finalize_running_thread) ... ok
test_finalize_with_trace (test.test_threading.ThreadTests.test_finalize_with_trace) ... ok
test_foreign_thread (test.test_threading.ThreadTests.test_foreign_thread) ... ok
test_frame_tstate_tracing (test.test_threading.ThreadTests.test_frame_tstate_tracing) ... ok
test_getprofile (test.test_threading.ThreadTests.test_getprofile) ... ok
test_getprofile_all_threads (test.test_threading.ThreadTests.test_getprofile_all_threads) ... ok
test_gettrace (test.test_threading.ThreadTests.test_gettrace) ... ok
test_gettrace_all_threads (test.test_threading.ThreadTests.test_gettrace_all_threads) ... ok
test_ident_of_no_threading_threads (test.test_threading.ThreadTests.test_ident_of_no_threading_threads) ... ok
test_import_from_another_thread (test.test_threading.ThreadTests.test_import_from_another_thread) ... ok
test_is_alive_after_fork (test.test_threading.ThreadTests.test_is_alive_after_fork) ... ok
test_join_from_multiple_threads (test.test_threading.ThreadTests.test_join_from_multiple_threads) ... ok
test_join_nondaemon_on_shutdown (test.test_threading.ThreadTests.test_join_nondaemon_on_shutdown) ... ok
test_join_with_timeout (test.test_threading.ThreadTests.test_join_with_timeout) ... ok
test_leak_without_join (test.test_threading.ThreadTests.test_leak_without_join) ... ok
test_limbo_cleanup (test.test_threading.ThreadTests.test_limbo_cleanup) ... ok
test_locals_at_exit (test.test_threading.ThreadTests.test_locals_at_exit) ... ok
test_lock_no_args (test.test_threading.ThreadTests.test_lock_no_args) ... ok
test_lock_no_subclass (test.test_threading.ThreadTests.test_lock_no_subclass) ... ok
test_lock_or_none (test.test_threading.ThreadTests.test_lock_or_none) ... ok
test_main_thread (test.test_threading.ThreadTests.test_main_thread) ... ok
test_main_thread_after_fork (test.test_threading.ThreadTests.test_main_thread_after_fork) ... ok
test_main_thread_after_fork_from_dummy_thread (test.test_threading.ThreadTests.test_main_thread_after_fork_from_dummy_thread) ... ok
test_main_thread_after_fork_from_foreign_thread (test.test_threading.ThreadTests.test_main_thread_after_fork_from_foreign_thread) ... ok
test_main_thread_after_fork_from_nonmain_thread (test.test_threading.ThreadTests.test_main_thread_after_fork_from_nonmain_thread) ... ok
test_main_thread_during_shutdown (test.test_threading.ThreadTests.test_main_thread_during_shutdown) ... ok
test_name (test.test_threading.ThreadTests.test_name) ... ok
test_no_refcycle_through_target (test.test_threading.ThreadTests.test_no_refcycle_through_target) ... ok
test_old_threading_api (test.test_threading.ThreadTests.test_old_threading_api) ... ok
test_repr_daemon (test.test_threading.ThreadTests.test_repr_daemon) ... ok
test_repr_stopped (test.test_threading.ThreadTests.test_repr_stopped) ... ok
test_start_new_thread_at_finalization (test.test_threading.ThreadTests.test_start_new_thread_at_finalization) ... ok
test_start_new_thread_failed (test.test_threading.ThreadTests.test_start_new_thread_failed) ... skipped 'RLIMIT_NPROC had no effect; probably superuser'
test_various_ops (test.test_threading.ThreadTests.test_various_ops) ... task <thread 0> will run for 88.0 usec
task <thread 1> will run for 34.8 usec
1 tasks are running
task <thread 2> will run for 52.4 usec
task <thread 4> will run for 58.5 usec
2 tasks are running
task <thread 5> will run for 22.7 usec
task <thread 6> will run for 54.6 usec
task <thread 3> will run for 55.7 usec
task <thread 7> will run for 35.8 usec
3 tasks are running
task <thread 8> will run for 71.6 usec
task <thread 9> will run for 91.9 usec
task <thread 1> done
task <thread 2> done
task <thread 0> done
waiting for all tasks to complete
<thread 1> is finished. 2 tasks are running
<thread 2> is finished. 1 tasks are running
<thread 0> is finished. 0 tasks are running
1 tasks are running
2 tasks are running
task <thread 4> done
task <thread 5> done
3 tasks are running
<thread 4> is finished. 2 tasks are running
<thread 5> is finished. 1 tasks are running
task <thread 6> done
2 tasks are running
3 tasks are running
task <thread 3> done
<thread 6> is finished. 2 tasks are running
task <thread 7> done
<thread 3> is finished. 1 tasks are running
<thread 7> is finished. 0 tasks are running
1 tasks are running
2 tasks are running
task <thread 8> done
task <thread 9> done
<thread 8> is finished. 1 tasks are running
<thread 9> is finished. 0 tasks are running
all tasks done
ok
test_various_ops_large_stack (test.test_threading.ThreadTests.test_various_ops_large_stack) ... with 1 MiB thread stack size...
task <thread 0> will run for 54.4 usec
1 tasks are running
task <thread 1> will run for 8.1 usec
task <thread 0> done
2 tasks are running
task <thread 3> will run for 58.1 usec
task <thread 4> will run for 10.1 usec
<thread 0> is finished. 1 tasks are running
task <thread 5> will run for 44.9 usec
task <thread 6> will run for 7.0 usec
task <thread 7> will run for 66.0 usec
2 tasks are running
3 tasks are running
task <thread 9> will run for 17.6 usec
task <thread 2> will run for 81.3 usec
task <thread 8> will run for 78.9 usec
waiting for all tasks to complete
task <thread 1> done
task <thread 3> done
<thread 1> is finished. 2 tasks are running
task <thread 4> done
<thread 3> is finished. 1 tasks are running
2 tasks are running
<thread 4> is finished. 1 tasks are running
task <thread 5> done
2 tasks are running
3 tasks are running
<thread 5> is finished. 2 tasks are running
task <thread 6> done
task <thread 7> done
3 tasks are running
<thread 6> is finished. 2 tasks are running
<thread 7> is finished. 1 tasks are running
task <thread 9> done
2 tasks are running
<thread 9> is finished. 1 tasks are running
task <thread 8> done
2 tasks are running
task <thread 2> done
<thread 8> is finished. 1 tasks are running
<thread 2> is finished. 0 tasks are running
all tasks done
ok
test_various_ops_small_stack (test.test_threading.ThreadTests.test_various_ops_small_stack) ... with 256 KiB thread stack size...
task <thread 0> will run for 58.9 usec
1 tasks are running
task <thread 1> will run for 55.7 usec
task <thread 0> done
2 tasks are running
task <thread 2> will run for 20.3 usec
task <thread 1> done
task <thread 3> will run for 78.7 usec
<thread 0> is finished. 1 tasks are running
task <thread 5> will run for 86.6 usec
task <thread 6> will run for 65.3 usec
2 tasks are running
task <thread 4> will run for 18.3 usec
task <thread 7> will run for 88.4 usec
<thread 1> is finished. 1 tasks are running
task <thread 2> done
task <thread 9> will run for 19.4 usec
waiting for all tasks to complete
task <thread 8> will run for 13.2 usec
2 tasks are running
3 tasks are running
task <thread 3> done
<thread 2> is finished. 2 tasks are running
task <thread 5> done
3 tasks are running
<thread 3> is finished. 2 tasks are running
task <thread 6> done
<thread 5> is finished. 1 tasks are running
2 tasks are running
<thread 6> is finished. 1 tasks are running
task <thread 7> done
2 tasks are running
task <thread 4> done
3 tasks are running
<thread 7> is finished. 2 tasks are running
task <thread 9> done
<thread 4> is finished. 1 tasks are running
2 tasks are running
task <thread 8> done
<thread 9> is finished. 1 tasks are running
<thread 8> is finished. 0 tasks are running
all tasks done
ok
test_bare_raise_in_brand_new_thread (test.test_threading.ThreadingExceptionTests.test_bare_raise_in_brand_new_thread) ... ok
test_daemonize_active_thread (test.test_threading.ThreadingExceptionTests.test_daemonize_active_thread) ... ok
test_joining_current_thread (test.test_threading.ThreadingExceptionTests.test_joining_current_thread) ... ok
test_joining_inactive_thread (test.test_threading.ThreadingExceptionTests.test_joining_inactive_thread) ... ok
test_multithread_modify_file_noerror (test.test_threading.ThreadingExceptionTests.test_multithread_modify_file_noerror) ... ok
test_print_exception (test.test_threading.ThreadingExceptionTests.test_print_exception) ... ok
test_print_exception_gh_102056 (test.test_threading.ThreadingExceptionTests.test_print_exception_gh_102056) ... ok
test_print_exception_stderr_is_none_1 (test.test_threading.ThreadingExceptionTests.test_print_exception_stderr_is_none_1) ... ok
test_print_exception_stderr_is_none_2 (test.test_threading.ThreadingExceptionTests.test_print_exception_stderr_is_none_2) ... ok
test_recursion_limit (test.test_threading.ThreadingExceptionTests.test_recursion_limit) ... ok
test_releasing_unacquired_lock (test.test_threading.ThreadingExceptionTests.test_releasing_unacquired_lock) ... ok
test_start_thread_again (test.test_threading.ThreadingExceptionTests.test_start_thread_again) ... ok
test_init_immutable_default_args (test.test_threading.TimerTests.test_init_immutable_default_args) ... ok
----------------------------------------------------------------------
Ran 213 tests in 52.328s
OK (skipped=2)
0:00:53 load avg: 0.93 [1/1] test_threading passed in 53.1 sec
== Tests result: SUCCESS ==
1 test OK.
Total duration: 53.5 sec
Total tests: run=213 skipped=2
Total test files: run=1/1
Result: SUCCESS
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
repeated with high bit set:
rpi-fc79b4:/var/tmp/cpython# ./python -c "import threading; print(hex(threading.current_thread().ident))"
0xf6f0cf34
rpi-fc79b4:/var/tmp/cpython# ./python -c "import threading; print(threading.current_thread() is threading.main_thread())"
True
rpi-fc79b4:/var/tmp/cpython# ./python -m test test_threading
Using random seed: 79905904
0:00:00 load avg: 0.46 Run 1 test sequentially in a single process
0:00:00 load avg: 0.46 [1/1] test_threading
0:00:51 load avg: 0.87 [1/1] test_threading passed in 51.1 sec
== Tests result: SUCCESS ==
1 test OK.
Total duration: 51.1 sec
Total tests: run=213 skipped=2
Total test files: run=1/1
Result: SUCCESS
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
@mpage It's been a while since we discussed #130115 so I'm interested in your thoughts here.
This PR addresses the primary issue, can be backported, and, as far as I can tell, doesn't introduce any regressions.
I took a peek at some other changes last night and I'm curious if you think it would be worthwhile to reuse a similar data structure as the min heap for thread identifiers long term. It would recycle them faster, but so long as callers aren't assuming a specific lifetime maybe that's OK. I guess other assumptions are that thread IDs are tied to a specific interpreter instance and can't be passed around between them since they're no longer system level identifiers
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks mostly ok to me, but the use of _pthread_t_to_ident in PyThread_get_thread_ident_ex() makes me a little nervous when SIZEOF_PTHREAD_T > SIZEOF_LONG (see inline comment). I would probably take the approach of adding a second field to PyRuntime (even though it's gross) or potentially reverting to the previous behavior of using set_ident for the main thread. I'd like to see what others think. I've added a couple of folks who have worked in this area in the past as reviewers.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
|
threadid = pthread_self();
|
|
|
assert(threadid == (pthread_t) (PyThread_ident_t) threadid);
|
|
|
return (PyThread_ident_t) threadid;
|
|
|
return _pthread_t_to_ident(threadid);
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is potentially lossy compared to the previous version if SIZEOF_PTHREAD_T > SIZEOF_LONG. Previously we would cast directly to a PyThread_ident_t (an unsigned long long), whereas we now cast through an unsigned long.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
vfazio
Mar 31, 2025
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I understand your concern.
This was an edge case in PyThread_start_new_thread, my guess is that it was there for the situations where pthread_t was not a ulong and was, instead, a pointer or struct.
#if SIZEOF_PTHREAD_T <= SIZEOF_LONG
return (unsigned long) th;
#else
return (unsigned long) *(unsigned long *) &th;
It's probably fine to drop the condition in _pthread_t_to_ident and let the caller here truncate it since this API is specifically returning a ULONG and not a PyThread_ident_t.
Looking at the history:
2565bff
635f6fb
The cast through ulong* was for Alpha OSF which was dropped from PEP 11 a few years ago (CPython 3.3) https://bugs.python.org/issue8606. My guess is it was also defined as a pointer or struct type and this was a way to work around it by returning at least long bytes.
I think if we find other platforms where we need to support this workaround, they can be chained to the MUSL #ifdef I think. The only time it would maybe be a problem is if sizeof(uintptr_t) < sizeof(ulong).
If others agree, I can drop the condition so the function looks like so:
static PyThread_ident_t
_pthread_t_to_ident(pthread_t value) {
PyThread_ident_t ident;
#if defined(__linux__) && !defined(__GLIBC__)
ident = (PyThread_ident_t) (uintptr_t) value;
assert(pthread_equal(value, (pthread_t) (uintptr_t) ident));
#else
ident = (PyThread_ident_t) value;
assert(pthread_equal(value, (pthread_t) ident));
#endif
return ident;
}
I do not suggest this as a long term solution; I do think we need to work towards making this opaque. I'm just trying to find something that is a stop-gap that can be ported back with relative ease that doesn't cause a regression.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
mpage reacted with thumbs up emoji
I took a peek at some other changes last night and I'm curious if you think it would be worthwhile to reuse a similar data structure as the min heap for thread identifiers long term.
Yes, something like that could make sense.
It would recycle them faster, but so long as callers aren't assuming a specific lifetime maybe that's OK.
I think that is already true now (you can't make assumptions about the lifetime of pthread identifiers).
I guess other assumptions are that thread IDs are tied to a specific interpreter instance and can't be passed around between them since they're no longer system level identifiers
Yeah, depending on how we chose to manage thread IDs that could be true. We could choose to manage the the pool of thread IDs per runtime, which would preserve the existing namespacing.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
|
static PyThread_ident_t
|
|
|
_pthread_t_to_ident(pthread_t value) {
|
|
|
#if SIZEOF_PTHREAD_T > SIZEOF_LONG
|
|
|
return (PyThread_ident_t) *(unsigned long *) &value;
|
pitrou
Apr 1, 2025
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why go through a pointer? This is cryptic and also uncomfortably fragile (what about big-endian platforms?).
I would suggest perhaps something like:
static PyThread_ident_t
_pthread_t_to_ident(pthread_t value) {
// Avoid sign-extension when converting to a larger int type
#if SIZEOF_PTHREAD_T == SIZEOF_VOID_P
return (uintptr_t) value;
#elif SIZEOF_PTHREAD_T == SIZEOF_LONG
return (unsigned long) value;
#elif SIZEOF_PTHREAD_T == SIZEOF_INT
return (unsigned int) value;
#elif SIZEOF_PTHREAD_T == SIZEOF_LONG_LONG
return (unsigned long long) value;
#else
#error "Unsupported SIZEOF_PTHREAD_T value"
#endif
}
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was legacy code that I can certainly drop (as mentioned above) . This is definitely simpler, I'll give it a spin and push it if tests pass.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated the branch to this implementation. The unit tests continue to pass AFAICT.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
vfazio
commented
Apr 2, 2025
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
Testing on musl + armv7 on RPi passes with the implementation suggested with and without the high bit set
localhost:~/development/cpython# ./python -m test -v test_threading
== CPython 3.14.0a6+ (heads/vfazio-thread_get_ident:4ae6d7e83d6, Apr 3 2025, 07:02:00) [GCC 14.2.0]
== Linux-6.12.13-0-rpi-armv7l-with-musl1.2.5 little-endian
== Python build: release
== cwd: /root/development/cpython/build/test_python_worker_27815æ
== CPU count: 4
== encodings: locale=UTF-8 FS=utf-8
== resources: all test resources are disabled, use -u option to unskip tests
Using random seed: 2337934113
0:00:00 load avg: 0.55 Run 1 test sequentially in a single process
0:00:00 load avg: 0.55 [1/1] test_threading
test_atexit_after_shutdown (test.test_threading.AtexitTests.test_atexit_after_shutdown) ... ok
test_atexit_called_once (test.test_threading.AtexitTests.test_atexit_called_once) ... ok
test_atexit_output (test.test_threading.AtexitTests.test_atexit_output) ... ok
test_abort (test.test_threading.BarrierTests.test_abort)
Test that an abort will put the barrier in a broken state ... ok
test_abort_and_reset (test.test_threading.BarrierTests.test_abort_and_reset)
Test that a barrier can be reset after being broken. ... ok
test_action (test.test_threading.BarrierTests.test_action)
Test the 'action' callback ... ok
test_barrier (test.test_threading.BarrierTests.test_barrier)
Test that a barrier is passed in lockstep ... ok
test_barrier_10 (test.test_threading.BarrierTests.test_barrier_10)
Test that a barrier works for 10 consecutive runs ... ok
test_constructor (test.test_threading.BarrierTests.test_constructor) ... ok
test_default_timeout (test.test_threading.BarrierTests.test_default_timeout)
Test the barrier's default timeout ... ok
test_repr (test.test_threading.BarrierTests.test_repr) ... ok
test_reset (test.test_threading.BarrierTests.test_reset)
Test that a 'reset' on a barrier frees the waiting threads ... ok
test_single_thread (test.test_threading.BarrierTests.test_single_thread) ... ok
test_timeout (test.test_threading.BarrierTests.test_timeout)
Test wait(timeout) ... ok
test_wait_return (test.test_threading.BarrierTests.test_wait_return)
test the return value from barrier.wait ... ok
test_acquire (test.test_threading.BoundedSemaphoreTests.test_acquire) ... ok
test_acquire_contended (test.test_threading.BoundedSemaphoreTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.BoundedSemaphoreTests.test_acquire_destroy) ... ok
test_acquire_timeout (test.test_threading.BoundedSemaphoreTests.test_acquire_timeout) ... ok
test_constructor (test.test_threading.BoundedSemaphoreTests.test_constructor) ... ok
test_default_value (test.test_threading.BoundedSemaphoreTests.test_default_value) ... ok
test_multirelease (test.test_threading.BoundedSemaphoreTests.test_multirelease) ... ok
test_release_unacquired (test.test_threading.BoundedSemaphoreTests.test_release_unacquired) ... ok
test_repr (test.test_threading.BoundedSemaphoreTests.test_repr) ... ok
test_try_acquire (test.test_threading.BoundedSemaphoreTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.BoundedSemaphoreTests.test_try_acquire_contended) ... ok
test_with (test.test_threading.BoundedSemaphoreTests.test_with) ... ok
test__is_owned (test.test_threading.CRLockTests.test__is_owned) ... ok
test_acquire_contended (test.test_threading.CRLockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.CRLockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.CRLockTests.test_acquire_release) ... ok
test_constructor (test.test_threading.CRLockTests.test_constructor) ... ok
test_different_thread (test.test_threading.CRLockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.CRLockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.CRLockTests.test_reacquire) ... ok
test_recursion_count (test.test_threading.CRLockTests.test_recursion_count) ... ok
test_release_save_unacquired (test.test_threading.CRLockTests.test_release_save_unacquired) ... ok
test_release_unacquired (test.test_threading.CRLockTests.test_release_unacquired) ... ok
test_repr (test.test_threading.CRLockTests.test_repr) ... ok
test_signature (test.test_threading.CRLockTests.test_signature) ... ok
test_thread_leak (test.test_threading.CRLockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.CRLockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.CRLockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.CRLockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.CRLockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.CRLockTests.test_weakref_exists) ... ok
test_with (test.test_threading.CRLockTests.test_with) ... ok
test__is_owned (test.test_threading.ConditionAsRLockTests.test__is_owned) ... ok
test_acquire_contended (test.test_threading.ConditionAsRLockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.ConditionAsRLockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.ConditionAsRLockTests.test_acquire_release) ... ok
test_constructor (test.test_threading.ConditionAsRLockTests.test_constructor) ... ok
test_different_thread (test.test_threading.ConditionAsRLockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.ConditionAsRLockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.ConditionAsRLockTests.test_reacquire) ... ok
test_recursion_count (test.test_threading.ConditionAsRLockTests.test_recursion_count) ... skipped 'Condition does not expose _recursion_count()'
test_release_save_unacquired (test.test_threading.ConditionAsRLockTests.test_release_save_unacquired) ... ok
test_release_unacquired (test.test_threading.ConditionAsRLockTests.test_release_unacquired) ... ok
test_repr (test.test_threading.ConditionAsRLockTests.test_repr) ... ok
test_thread_leak (test.test_threading.ConditionAsRLockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.ConditionAsRLockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.ConditionAsRLockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.ConditionAsRLockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.ConditionAsRLockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.ConditionAsRLockTests.test_weakref_exists) ... ok
test_with (test.test_threading.ConditionAsRLockTests.test_with) ... ok
test_acquire (test.test_threading.ConditionTests.test_acquire) ... ok
test_notify (test.test_threading.ConditionTests.test_notify) ... ok
test_timeout (test.test_threading.ConditionTests.test_timeout) ... ok
test_unacquired_notify (test.test_threading.ConditionTests.test_unacquired_notify) ... ok
test_unacquired_wait (test.test_threading.ConditionTests.test_unacquired_wait) ... ok
test_waitfor (test.test_threading.ConditionTests.test_waitfor) ... ok
test_waitfor_timeout (test.test_threading.ConditionTests.test_waitfor_timeout) ... ok
test_at_fork_reinit (test.test_threading.EventTests.test_at_fork_reinit) ... ok
test_is_set (test.test_threading.EventTests.test_is_set) ... ok
test_notify (test.test_threading.EventTests.test_notify) ... ok
test_repr (test.test_threading.EventTests.test_repr) ... ok
test_set_and_clear (test.test_threading.EventTests.test_set_and_clear) ... ok
test_timeout (test.test_threading.EventTests.test_timeout) ... ok
test_custom_excepthook (test.test_threading.ExceptHookTests.test_custom_excepthook) ... ok
test_custom_excepthook_fail (test.test_threading.ExceptHookTests.test_custom_excepthook_fail) ... ok
test_excepthook (test.test_threading.ExceptHookTests.test_excepthook) ... ok
test_excepthook_thread_None (test.test_threading.ExceptHookTests.test_excepthook_thread_None) ... ok
test_original_excepthook (test.test_threading.ExceptHookTests.test_original_excepthook) ... ok
test_system_exit (test.test_threading.ExceptHookTests.test_system_exit) ... ok
test_can_interrupt_tight_loops (test.test_threading.InterruptMainTests.test_can_interrupt_tight_loops) ... ok
test_interrupt_main_invalid_signal (test.test_threading.InterruptMainTests.test_interrupt_main_invalid_signal) ... ok
test_interrupt_main_mainthread (test.test_threading.InterruptMainTests.test_interrupt_main_mainthread) ... ok
test_interrupt_main_noerror (test.test_threading.InterruptMainTests.test_interrupt_main_noerror) ... ok
test_interrupt_main_subthread (test.test_threading.InterruptMainTests.test_interrupt_main_subthread) ... ok
test_interrupt_main_with_signal_handler (test.test_threading.InterruptMainTests.test_interrupt_main_with_signal_handler) ... ok
test_acquire_contended (test.test_threading.LockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.LockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.LockTests.test_acquire_release) ... ok
test_at_fork_reinit (test.test_threading.LockTests.test_at_fork_reinit) ... ok
test_constructor (test.test_threading.LockTests.test_constructor) ... ok
test_different_thread (test.test_threading.LockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.LockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.LockTests.test_reacquire) ... ok
test_repr (test.test_threading.LockTests.test_repr) ... ok
test_state_after_timeout (test.test_threading.LockTests.test_state_after_timeout) ... ok
test_thread_leak (test.test_threading.LockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.LockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.LockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.LockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.LockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.LockTests.test_weakref_exists) ... ok
test_with (test.test_threading.LockTests.test_with) ... ok
test__all__ (test.test_threading.MiscTestCase.test__all__) ... ok
test_change_name (test.test_threading.MiscTestCase.test_change_name) ... ok
test_set_name (test.test_threading.MiscTestCase.test_set_name) ... ok
test__is_owned (test.test_threading.PyRLockTests.test__is_owned) ... ok
test_acquire_contended (test.test_threading.PyRLockTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.PyRLockTests.test_acquire_destroy) ... ok
test_acquire_release (test.test_threading.PyRLockTests.test_acquire_release) ... ok
test_constructor (test.test_threading.PyRLockTests.test_constructor) ... ok
test_different_thread (test.test_threading.PyRLockTests.test_different_thread) ... ok
test_locked_repr (test.test_threading.PyRLockTests.test_locked_repr) ... ok
test_reacquire (test.test_threading.PyRLockTests.test_reacquire) ... ok
test_recursion_count (test.test_threading.PyRLockTests.test_recursion_count) ... ok
test_release_save_unacquired (test.test_threading.PyRLockTests.test_release_save_unacquired) ... ok
test_release_unacquired (test.test_threading.PyRLockTests.test_release_unacquired) ... ok
test_repr (test.test_threading.PyRLockTests.test_repr) ... ok
test_thread_leak (test.test_threading.PyRLockTests.test_thread_leak) ... ok
test_timeout (test.test_threading.PyRLockTests.test_timeout) ... ok
test_try_acquire (test.test_threading.PyRLockTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.PyRLockTests.test_try_acquire_contended) ... ok
test_weakref_deleted (test.test_threading.PyRLockTests.test_weakref_deleted) ... ok
test_weakref_exists (test.test_threading.PyRLockTests.test_weakref_exists) ... ok
test_with (test.test_threading.PyRLockTests.test_with) ... ok
test_acquire (test.test_threading.SemaphoreTests.test_acquire) ... ok
test_acquire_contended (test.test_threading.SemaphoreTests.test_acquire_contended) ... ok
test_acquire_destroy (test.test_threading.SemaphoreTests.test_acquire_destroy) ... ok
test_acquire_timeout (test.test_threading.SemaphoreTests.test_acquire_timeout) ... ok
test_constructor (test.test_threading.SemaphoreTests.test_constructor) ... ok
test_default_value (test.test_threading.SemaphoreTests.test_default_value) ... ok
test_multirelease (test.test_threading.SemaphoreTests.test_multirelease) ... ok
test_release_unacquired (test.test_threading.SemaphoreTests.test_release_unacquired) ... ok
test_repr (test.test_threading.SemaphoreTests.test_repr) ... ok
test_try_acquire (test.test_threading.SemaphoreTests.test_try_acquire) ... ok
test_try_acquire_contended (test.test_threading.SemaphoreTests.test_try_acquire_contended) ... ok
test_with (test.test_threading.SemaphoreTests.test_with) ... ok
test_daemon_threads_fatal_error (test.test_threading.SubinterpThreadingTests.test_daemon_threads_fatal_error) ... ok
test_daemon_threads_not_allowed (test.test_threading.SubinterpThreadingTests.test_daemon_threads_not_allowed) ... ok
test_threads_join (test.test_threading.SubinterpThreadingTests.test_threads_join) ... ok
test_threads_join_2 (test.test_threading.SubinterpThreadingTests.test_threads_join_2) ... ok
test_threads_join_with_no_main (test.test_threading.SubinterpThreadingTests.test_threads_join_with_no_main) ... ok
test_threads_not_allowed (test.test_threading.SubinterpThreadingTests.test_threads_not_allowed) ... ok
test_1_join_on_shutdown (test.test_threading.ThreadJoinOnShutdown.test_1_join_on_shutdown) ... ok
test_2_join_in_forked_process (test.test_threading.ThreadJoinOnShutdown.test_2_join_in_forked_process) ... ok
test_3_join_in_forked_from_thread (test.test_threading.ThreadJoinOnShutdown.test_3_join_in_forked_from_thread) ... ok
test_4_daemon_threads (test.test_threading.ThreadJoinOnShutdown.test_4_daemon_threads) ... ok
test_clear_threads_states_after_fork (test.test_threading.ThreadJoinOnShutdown.test_clear_threads_states_after_fork) ... ok
test_reinit_tls_after_fork (test.test_threading.ThreadJoinOnShutdown.test_reinit_tls_after_fork) ... ok
test_thread_from_thread (test.test_threading.ThreadJoinOnShutdown.test_thread_from_thread) ... ok
test_BoundedSemaphore_limit (test.test_threading.ThreadTests.test_BoundedSemaphore_limit) ... ok
test_PyThreadState_SetAsyncExc (test.test_threading.ThreadTests.test_PyThreadState_SetAsyncExc) ... started worker thread
trying nonsensical thread id
waiting for worker thread to get started
verifying worker hasn't exited
attempting to raise asynch exception in worker
waiting for worker to say it caught the exception
all OK -- joining worker
ok
test_args_argument (test.test_threading.ThreadTests.test_args_argument) ... ok
test_boolean_target (test.test_threading.ThreadTests.test_boolean_target) ... ok
test_daemon_param (test.test_threading.ThreadTests.test_daemon_param) ... ok
test_dummy_thread_after_fork (test.test_threading.ThreadTests.test_dummy_thread_after_fork) ... ok
test_enumerate_after_join (test.test_threading.ThreadTests.test_enumerate_after_join) ... ok
test_finalization_shutdown (test.test_threading.ThreadTests.test_finalization_shutdown) ... ok
test_finalize_daemon_thread_hang (test.test_threading.ThreadTests.test_finalize_daemon_thread_hang) ... ok
test_finalize_running_thread (test.test_threading.ThreadTests.test_finalize_running_thread) ... ok
test_finalize_with_trace (test.test_threading.ThreadTests.test_finalize_with_trace) ... ok
test_foreign_thread (test.test_threading.ThreadTests.test_foreign_thread) ... ok
test_frame_tstate_tracing (test.test_threading.ThreadTests.test_frame_tstate_tracing) ... ok
test_getprofile (test.test_threading.ThreadTests.test_getprofile) ... ok
test_getprofile_all_threads (test.test_threading.ThreadTests.test_getprofile_all_threads) ... ok
test_gettrace (test.test_threading.ThreadTests.test_gettrace) ... ok
test_gettrace_all_threads (test.test_threading.ThreadTests.test_gettrace_all_threads) ... ok
test_ident_of_no_threading_threads (test.test_threading.ThreadTests.test_ident_of_no_threading_threads) ... ok
test_import_from_another_thread (test.test_threading.ThreadTests.test_import_from_another_thread) ... ok
test_is_alive_after_fork (test.test_threading.ThreadTests.test_is_alive_after_fork) ... ok
test_join_from_multiple_threads (test.test_threading.ThreadTests.test_join_from_multiple_threads) ... ok
test_join_nondaemon_on_shutdown (test.test_threading.ThreadTests.test_join_nondaemon_on_shutdown) ... ok
test_join_with_timeout (test.test_threading.ThreadTests.test_join_with_timeout) ... ok
test_leak_without_join (test.test_threading.ThreadTests.test_leak_without_join) ... ok
test_limbo_cleanup (test.test_threading.ThreadTests.test_limbo_cleanup) ... ok
test_locals_at_exit (test.test_threading.ThreadTests.test_locals_at_exit) ... ok
test_lock_no_args (test.test_threading.ThreadTests.test_lock_no_args) ... ok
test_lock_no_subclass (test.test_threading.ThreadTests.test_lock_no_subclass) ... ok
test_lock_or_none (test.test_threading.ThreadTests.test_lock_or_none) ... ok
test_main_thread (test.test_threading.ThreadTests.test_main_thread) ... ok
test_main_thread_after_fork (test.test_threading.ThreadTests.test_main_thread_after_fork) ... ok
test_main_thread_after_fork_from_dummy_thread (test.test_threading.ThreadTests.test_main_thread_after_fork_from_dummy_thread) ... ok
test_main_thread_after_fork_from_foreign_thread (test.test_threading.ThreadTests.test_main_thread_after_fork_from_foreign_thread) ... ok
test_main_thread_after_fork_from_nonmain_thread (test.test_threading.ThreadTests.test_main_thread_after_fork_from_nonmain_thread) ... ok
test_main_thread_during_shutdown (test.test_threading.ThreadTests.test_main_thread_during_shutdown) ... ok
test_name (test.test_threading.ThreadTests.test_name) ... ok
test_no_refcycle_through_target (test.test_threading.ThreadTests.test_no_refcycle_through_target) ... ok
test_old_threading_api (test.test_threading.ThreadTests.test_old_threading_api) ... ok
test_repr_daemon (test.test_threading.ThreadTests.test_repr_daemon) ... ok
test_repr_stopped (test.test_threading.ThreadTests.test_repr_stopped) ... ok
test_start_new_thread_at_finalization (test.test_threading.ThreadTests.test_start_new_thread_at_finalization) ... ok
test_start_new_thread_failed (test.test_threading.ThreadTests.test_start_new_thread_failed) ... skipped 'RLIMIT_NPROC had no effect; probably superuser'
test_various_ops (test.test_threading.ThreadTests.test_various_ops) ... task <thread 0> will run for 36.1 usec
1 tasks are running
task <thread 1> will run for 59.3 usec
task <thread 0> done
2 tasks are running
<thread 0> is finished. 1 tasks are running
task <thread 2> will run for 14.8 usec
task <thread 1> done
2 tasks are running
task <thread 4> will run for 80.2 usec
task <thread 5> will run for 26.3 usec
<thread 1> is finished. 1 tasks are running
task <thread 6> will run for 48.8 usec
task <thread 7> will run for 43.6 usec
task <thread 8> will run for 12.1 usec
task <thread 3> will run for 26.5 usec
task <thread 2> done
2 tasks are running
task <thread 9> will run for 96.7 usec
3 tasks are running
<thread 2> is finished. 2 tasks are running
task <thread 4> done
task <thread 5> done
3 tasks are running
waiting for all tasks to complete
<thread 4> is finished. 2 tasks are running
task <thread 6> done
<thread 5> is finished. 1 tasks are running
2 tasks are running
<thread 6> is finished. 1 tasks are running
2 tasks are running
task <thread 8> done
3 tasks are running
task <thread 3> done
<thread 3> is finished. 2 tasks are running
task <thread 7> done
3 tasks are running
<thread 8> is finished. 2 tasks are running
task <thread 9> done
<thread 7> is finished. 1 tasks are running
<thread 9> is finished. 0 tasks are running
all tasks done
ok
test_various_ops_large_stack (test.test_threading.ThreadTests.test_various_ops_large_stack) ... with 1 MiB thread stack size...
task <thread 0> will run for 36.5 usec
1 tasks are running
task <thread 1> will run for 24.0 usec
task <thread 2> will run for 41.1 usec
2 tasks are running
task <thread 0> done
task <thread 3> will run for 75.6 usec
task <thread 4> will run for 3.5 usec
task <thread 1> done
task <thread 6> will run for 5.7 usec
task <thread 7> will run for 16.7 usec
task <thread 8> will run for 22.0 usec
task <thread 9> will run for 27.7 usec
waiting for all tasks to complete
task <thread 5> will run for 42.2 usec
3 tasks are running
<thread 0> is finished. 2 tasks are running
task <thread 2> done
<thread 1> is finished. 1 tasks are running
2 tasks are running
<thread 2> is finished. 1 tasks are running
task <thread 3> done
2 tasks are running
3 tasks are running
task <thread 6> done
<thread 3> is finished. 2 tasks are running
task <thread 4> done
<thread 6> is finished. 1 tasks are running
2 tasks are running
task <thread 7> done
<thread 4> is finished. 1 tasks are running
2 tasks are running
<thread 7> is finished. 1 tasks are running
task <thread 8> done
2 tasks are running
<thread 8> is finished. 1 tasks are running
2 tasks are running
task <thread 9> done
<thread 9> is finished. 1 tasks are running
task <thread 5> done
<thread 5> is finished. 0 tasks are running
all tasks done
ok
test_various_ops_small_stack (test.test_threading.ThreadTests.test_various_ops_small_stack) ... with 256 KiB thread stack size...
task <thread 0> will run for 40.6 usec
1 tasks are running
task <thread 1> will run for 24.1 usec
task <thread 0> done
2 tasks are running
task <thread 2> will run for 38.9 usec
task <thread 4> will run for 96.8 usec
task <thread 3> will run for 62.9 usec
task <thread 1> done
task <thread 6> will run for 99.5 usec
task <thread 7> will run for 99.7 usec
task <thread 8> will run for 62.9 usec
task <thread 5> will run for 53.1 usec
task <thread 9> will run for 10.8 usec
<thread 0> is finished. 1 tasks are running
waiting for all tasks to complete
2 tasks are running
<thread 1> is finished. 1 tasks are running
task <thread 2> done
2 tasks are running
3 tasks are running
<thread 2> is finished. 2 tasks are running
task <thread 3> done
task <thread 4> done
3 tasks are running
<thread 3> is finished. 2 tasks are running
task <thread 6> done
3 tasks are running
<thread 4> is finished. 2 tasks are running
task <thread 7> done
<thread 6> is finished. 1 tasks are running
<thread 7> is finished. 0 tasks are running
1 tasks are running
2 tasks are running
task <thread 8> done
<thread 8> is finished. 1 tasks are running
task <thread 5> done
2 tasks are running
task <thread 9> done
<thread 5> is finished. 1 tasks are running
<thread 9> is finished. 0 tasks are running
all tasks done
ok
test_bare_raise_in_brand_new_thread (test.test_threading.ThreadingExceptionTests.test_bare_raise_in_brand_new_thread) ... ok
test_daemonize_active_thread (test.test_threading.ThreadingExceptionTests.test_daemonize_active_thread) ... ok
test_joining_current_thread (test.test_threading.ThreadingExceptionTests.test_joining_current_thread) ... ok
test_joining_inactive_thread (test.test_threading.ThreadingExceptionTests.test_joining_inactive_thread) ... ok
test_multithread_modify_file_noerror (test.test_threading.ThreadingExceptionTests.test_multithread_modify_file_noerror) ... ok
test_print_exception (test.test_threading.ThreadingExceptionTests.test_print_exception) ... ok
test_print_exception_gh_102056 (test.test_threading.ThreadingExceptionTests.test_print_exception_gh_102056) ... ok
test_print_exception_stderr_is_none_1 (test.test_threading.ThreadingExceptionTests.test_print_exception_stderr_is_none_1) ... ok
test_print_exception_stderr_is_none_2 (test.test_threading.ThreadingExceptionTests.test_print_exception_stderr_is_none_2) ... ok
test_recursion_limit (test.test_threading.ThreadingExceptionTests.test_recursion_limit) ... ok
test_releasing_unacquired_lock (test.test_threading.ThreadingExceptionTests.test_releasing_unacquired_lock) ... ok
test_start_thread_again (test.test_threading.ThreadingExceptionTests.test_start_thread_again) ... ok
test_init_immutable_default_args (test.test_threading.TimerTests.test_init_immutable_default_args) ... ok
----------------------------------------------------------------------
Ran 213 tests in 47.094s
OK (skipped=2)
0:00:48 load avg: 0.76 [1/1] test_threading passed in 47.8 sec
== Tests result: SUCCESS ==
1 test OK.
Total duration: 48.6 sec
Total tests: run=213 skipped=2
Total test files: run=1/1
Result: SUCCESS
localhost:~/development/cpython# ./python -c "import threading; print(hex(threading.current_thread().ident))"
0xf6f0cf34
localhost:~/development/cpython# ./python -m test test_threading
Using random seed: 3415034556
0:00:00 load avg: 0.00 Run 1 test sequentially in a single process
0:00:00 load avg: 0.00 [1/1] test_threading
0:00:46 load avg: 0.47 [1/1] test_threading passed in 46.9 sec
== Tests result: SUCCESS ==
1 test OK.
Total duration: 46.9 sec
Total tests: run=213 skipped=2
Total test files: run=1/1
Result: SUCCESS
localhost:~/development/cpython#
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, LGTM, but we should run some extended CI checks on this
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
vfazio reacted with hooray emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
@gpshead Do you think this is a candidate for backporting?
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
vfazio
commented
Apr 3, 2025
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
@gpshead Do you think this is a candidate for backporting?
I'm secretly hoping so, I plan on porting the patch for Buildroot, but backporting to 3.13 would allow BR to drop it once we hit 3.13.3 hopefully?
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Thanks @vfazio for the PR, and @pitrou for merging it 🌮🎉.. I'm working now to backport this PR to: 3.12.
🐍🍒⛏🤖
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Thanks @vfazio for the PR, and @pitrou for merging it 🌮🎉.. I'm working now to backport this PR to: 3.13.
🐍🍒⛏🤖
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Sorry, @vfazio and @pitrou, I could not cleanly backport this to 3.12 due to a conflict.
Please backport using cherry_picker on command line.
cherry_picker 72123063ddee84bb2c9d591a23f420997e35af5a 3.12
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
miss-islington
pushed a commit
to miss-islington/cpython
that referenced
this pull request
Apr 4, 2025
)
CPython's pthread-based thread identifier relies on pthread_t being able
to be represented as an unsigned integer type.
This is true in most Linux libc implementations where it's defined as an
unsigned long, however musl typedefs it as a struct *.
If the pointer has the high bit set and is cast to PyThread_ident_t, the
resultant value can be sign-extended [0]. This can cause issues when
comparing against threading._MainThread's identifier. The main thread's
identifier value is retrieved via _get_main_thread_ident which is backed
by an unsigned long which truncates sign extended bits.
>>> hex(threading.main_thread().ident)
'0xb6f33f3c'
>>> hex(threading.current_thread().ident)
'0xffffffffb6f33f3c'
Work around this by conditionally compiling in some code for non-glibc
based Linux platforms that are at risk of sign-extension to return a
PyLong based on the main thread's unsigned long thread identifier if the
current thread is the main thread.
[0]: https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Arrays-and-pointers-implementation.html
---------
(cherry picked from commit 7212306)
Co-authored-by: Vincent Fazio <[email protected]>
Signed-off-by: Vincent Fazio <[email protected]>
gpshead reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
pitrou
commented
Apr 4, 2025
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
The threading code base has probably diverged too much from 3.12 to make an automatic backport feasible. You may want to backport an equivalent fix manually @vfazio , or we can limit ourselves to 3.13 if you prefer.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
The threading code base has probably diverged too much from 3.12 to make an automatic backport feasible. You may want to backport an equivalent fix manually @vfazio , or we can limit ourselves to 3.13 if you prefer.
Thanks for porting this back! I think back to 3.13 is fine since that's when we first noticed the problem. It was probably an artifact of the change from UL -> ULL (PyThread_ident_t) which only occurred in 3.13+
|
gpshead reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
pitrou
pushed a commit
that referenced
this pull request
Apr 4, 2025
…H-132089)
CPython's pthread-based thread identifier relies on pthread_t being able
to be represented as an unsigned integer type.
This is true in most Linux libc implementations where it's defined as an
unsigned long, however musl typedefs it as a struct *.
If the pointer has the high bit set and is cast to PyThread_ident_t, the
resultant value can be sign-extended [0]. This can cause issues when
comparing against threading._MainThread's identifier. The main thread's
identifier value is retrieved via _get_main_thread_ident which is backed
by an unsigned long which truncates sign extended bits.
>>> hex(threading.main_thread().ident)
'0xb6f33f3c'
>>> hex(threading.current_thread().ident)
'0xffffffffb6f33f3c'
Work around this by conditionally compiling in some code for non-glibc
based Linux platforms that are at risk of sign-extension to return a
PyLong based on the main thread's unsigned long thread identifier if the
current thread is the main thread.
[0]: https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Arrays-and-pointers-implementation.html
---------
(cherry picked from commit 7212306)
Signed-off-by: Vincent Fazio <[email protected]>
Co-authored-by: Vincent Fazio <[email protected]>
Uh oh!
There was an error while loading. Please reload this page.
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
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.
|