[PATCH v3 2/2] nptl: Do not use pthread set_tid_address as state synchronization (BZ #19951)
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Fri Jul 25 12:44:20 GMT 2025
On 25/07/25 07:16, Florian Weimer wrote:
> * Adhemerval Zanella:
>
>> diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c
>> index b838273881..f0828ea870 100644
>> --- a/nptl/pthread_cancel.c
>> +++ b/nptl/pthread_cancel.c
>> @@ -60,7 +60,8 @@ __pthread_cancel (pthread_t th)
>> {
>> volatile struct pthread *pd = (volatile struct pthread *) th;
>>
>> - if (pd->tid == 0)
>> + int state = atomic_load_acquire (&pd->joinstate);
>> + if (state == THREAD_STATE_EXITED || state == THREAD_STATE_EXITING)
>> /* The thread has already exited on the kernel side. Its outcome
>> (regular exit, other cancelation) has already been
>> determined. */
>
> The “has already exited on the kernel side” part is not true anymore.
I think this comments holds, if joinstate is THREAD_STATE_EXITED it means
the kernel already acted upon the defined set_tid_addres; and
THREAD_STATE_EXITING is set atomically *before* exiting.
I think maybe I can remove the THREAD_STATE_EXITING and rely on
__pthread_kill_implementation instead (since it will always take
the 'exit_lock' lock).
>
> In pthread_kill, we have this comment for a similar situation:
>
> /* The thread is about to exit (or has exited). Sending the
> signal is either not observable (the target thread has already
> blocked signals at this point), or it will fail, or it might be
> delivered to a new, unrelated thread that has reused the TID.
> So do not actually send the signal. */
>
>> if (__glibc_unlikely (atomic_fetch_add_relaxed (&__nptl_nthreads, -1) == 1))
>> /* This was the last thread. */
>> exit (0);
>> @@ -574,20 +592,21 @@ start_thread (void *arg)
>> pd->setxid_futex = 0;
>> }
>>
>> - /* If the thread is detached free the TCB. */
>> - if (IS_DETACHED (pd))
>> + if (prevstate == THREAD_STATE_DETACHED)
>> /* Free the TCB. */
>> __nptl_free_tcb (pd);
>>
>> /* Remove the associated name from the thread stack. */
>> name_stack_maps (pd, false);
>>
>> + pd->tid = 0;
>
> We have this:
>
> int
> __pthread_getcpuclockid (pthread_t threadid, clockid_t *clockid)
> {
> struct pthread *pd = (struct pthread *) threadid;
>
> /* Make sure the descriptor is valid. */
> if (INVALID_TD_P (pd))
> /* Not a valid thread handle. */
> return ESRCH;
> …
> }
>
> I asume the pd->tid assignment amove ensures that pthread_getcpuclockid
> can still be used t probe whether a thread is running? Do we have a
> test for this? There should be a comment on the assignment.
The tid will be continue to be set by pthread_create at the end of
thread execution, it won't done atomically anymore and it will not
consider the window between setting the 'tid' and the 'exit' syscall
issue as valid.
>
> We anticipated a change as far as pthread_gettid_np is concerned and
> mentioned the possible behaviors in the manual.
>
> From a debuggability point of view, it may be beneficial not to do the
> pd->tid assignment, so that it's possible to see previous TIDs in
> coredumps. We can look at pd->joinstate in pthread_getcpuclockid
> and keep the current behavior, I think.
I think to allow it we would need to remove INVALID_TD_P, which I
had in my previous version of this fix; and use 'joinstate' otherwise.
More information about the Libc-alpha
mailing list