nptl: Add pthread_thread_number_np function

Message ID 82837141-5baa-bb34-607b-fe8de3199c9c@redhat.com
State Superseded
Headers

Commit Message

Florian Weimer Dec. 15, 2017, 7:47 a.m. UTC
  On 12/15/2017 05:08 AM, Carlos O'Donell wrote:
> On 12/14/2017 10:56 AM, Florian Weimer wrote:
>> The implementation is actually in libc.so.  With a full implementation
>> of pthread_self in libc.so, pthread_thread_number_np is completely
>> usable without libpthread.
> 
> High level:
> 
> The addition of a new API to libc.so needs a strong justification.
> 
> Do you have a use in mind for this API?
> 
> When would users use this number?
> 
> Why would they use this number?

Logging, and discriminating between threads, for example in random bit 
generators.

> Do any of the uses you envision overlap with the longstanding
> requests for gettid()?

Barely, because gettid does not return a unique number over time, and 
the number returned from pthread_thread_number_np cannot be used with 
existing interfaces.

> Design level:
> 
> We make claims that the thread number is never reused, but then the
> implementation wraps the uint64_t global counter, and reuses the
> number. If we really don't allow reuse, then we limit the implementation
> to only ever starting 2^64-1 threads, similar to the discussions around
> dlopen and void* cookies, I don't know if this kind of limit is a good
> idea or not. My instinct tells me we should not limit the implementation
> in such ways.

The implementation of condition variables uses a 64-bit counter in a 
similar way.  I assumed that we had consensus that we can assume that a 
simple 64-bit counter would never overflow.

> We could allocate the thread numbers lazily, and that would certainly
> avoid limiting ourselves to only allocating 2^64-1 threads. On top of
> that if the function could return an error then we could return such
> an error at overflow:
> 
>   int pthread_thread_number_np (uint64_t* thread_number, pthread_t @var{thread})
> 
> Returns 0 if the thread has a unique number, otherwise -1 if it does not.

That's a bad interface.  If you are worried about 64-bit overflow, we 
should use a 128-bit counter instead, but I'm not convinced this is 
necessary.

Lazy allocation would make the function not safe for use in signal handlers.

>> +This function returns a number that uniquely identifies @var{thread}
>> +among all past, current, and future running threads.  This number does
>> +not change during the life-time of the thread.  Once returned by this
>> +function, a number will not be reused after the thread terminates.
> 
> We should be clear here that uint64_t (the return type) only has 64-bits,
> and while theoretically difficult to reach, this has a limit. The only way
> not to reuse the numbers is to limit the implementation to only allowing
> uint64_t worth of threads to ever be started. That's a limit that never
> existed before the existence of this API.

This  restriction already existed:

    We use two 64b counters: __wseq and __g1_start.  They are
    monotonically increasing and single-writer-multiple-readers
    counters, so we can implement load, fetch-and-add, and
    fetch-and-xor operations even when we just have 32b atomics.
    Values we add or xor are less than or equal to 1<<31 (*), so we
    only have to make overflow-and-addition atomic wrt. to concurrent
    load operations and xor operations.  To do that, we split each
    counter into two 32b values of which we reserve the MSB of each to
    represent an overflow from the lower-order half to the higher-order
    half.

> My worry here is that once you open pandoras box and say that they thread
> number *might* be reused after 2^64-1 pthread_create's, then users will
> start writing code *not* to treat it as unique.

I'm going to put in a __libc_fatal, just to be on the safe side.

> Thus, see my notes above about changing the interface slightly.
> 
>> +
>> +The returned number is only unique with regards to the current process.
>> +It may be shared by subprocesses and other processes in the system.
>> +
>> +The initial (main) thread has number 1.  Thread numbers are not
>> +necessarily assigned in a consecutive fashion.  They bear no
>> +relationship to process IDs or thread IDs assigned by the kernel.
> 
> Suggest:
> 
> They bear no relationship to POSIX thread IDs (pthread_t), process IDs,
> or Linux kernel thread IDs.

“
They bear no relationship to POSIX thread IDs (@code{pthread_t} values), 
process IDs or thread IDs assigned by the kernel.
”

(This is in the generic part of the manual, so we shouldn't reference 
Linux unless absolutely necessary.)

>> +    pthread_thread_number_np;
>> +  }
>>     GLIBC_PRIVATE {
>>       __libc_alloca_cutoff;
>>       # Internal libc interface to libpthread
>> diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
>> index 1cc7893195..0e1cf3939e 100644
>> --- a/nptl/allocatestack.c
>> +++ b/nptl/allocatestack.c
>> @@ -413,16 +413,21 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
>>     assert (powerof2 (pagesize_m1 + 1));
>>     assert (TCB_ALIGNMENT >= STACK_ALIGN);
>>   
>> -  /* Get the stack size from the attribute if it is set.  Otherwise we
>> -     use the default we determined at start time.  */
>> -  if (attr->stacksize != 0)
>> -    size = attr->stacksize;
>> -  else
>> -    {
>> -      lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
>> +  uint64_t thread_number;
>> +  lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
> 
> You need to add some P&C comments explaining that __default_pthread_attr_lock
> is now *also* protecting the global_thread_number global and why.

I now have:

   uint64_t thread_number;
   lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
   {
     /* Number 1 is reserved for the initial thread.  Reuse
        __default_pthread_attr_lock to avoid concurrent updates of this
        counter.  */
     static uint64_t global_thread_number = 1;
     thread_number = ++global_thread_number;

     /* Check for counter wrap-around.  This should never happen
        because 2**64 is such a large value.  */
     if (thread_number == 0)
       __libc_fatal ("Fatal glibc error: maximum number of threads 
exceeded\n");

>> +static int
>> +do_test (void)
>> +{
>> +  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
>> +  support_isolate_in_subprocess (subprocess, NULL);
> 
> Why do we isolate this in a subprocess?

To check that the main thread has thread number 1 in a subprocess.  I'll 
add a comment.

New patch attached.

Thanks,
Florian
  

Comments

Carlos O'Donell Dec. 20, 2017, 8:06 a.m. UTC | #1
On 12/14/2017 11:47 PM, Florian Weimer wrote:
> On 12/15/2017 05:08 AM, Carlos O'Donell wrote:
>> On 12/14/2017 10:56 AM, Florian Weimer wrote:
>>> The implementation is actually in libc.so.  With a full implementation
>>> of pthread_self in libc.so, pthread_thread_number_np is completely
>>> usable without libpthread.
>>
>> High level:
>>
>> The addition of a new API to libc.so needs a strong justification.
>>
>> Do you have a use in mind for this API?
>>
>> When would users use this number?
>>
>> Why would they use this number?
> 
> Logging, and discriminating between threads, for example in random
> bit generators.

Good. You should mention these uses in the NEWS entry for the new
function. Please add a NEWS entry in v2.

If by "random bit generators" we are talking about arc4random, we could
add this as an internal interface for direct use by arc4random? That
wouldn't be a problem. I would not want this review to block arc4random.

>> Do any of the uses you envision overlap with the longstanding
>> requests for gettid()?
> 
> Barely, because gettid does not return a unique number over time, and
> the number returned from pthread_thread_number_np cannot be used with
> existing interfaces.

I like that answer. Thanks.
 
>> Design level:
>>
>> We make claims that the thread number is never reused, but then the
>> implementation wraps the uint64_t global counter, and reuses the
>> number. If we really don't allow reuse, then we limit the implementation
>> to only ever starting 2^64-1 threads, similar to the discussions around
>> dlopen and void* cookies, I don't know if this kind of limit is a good
>> idea or not. My instinct tells me we should not limit the implementation
>> in such ways.
> 
> The implementation of condition variables uses a 64-bit counter in a
> similar way.  I assumed that we had consensus that we can assume that
> a simple 64-bit counter would never overflow.

The comparison is not valid.

One can create and destroy an infinite number of condition variables.

The design of the pthread_thread_number_np API immediately limits the
number of creatable threads to the size of the return type. If we allow
creating more threads than that then we break the API.

My objection is not with the internal implementation of a 64-bit counter.

My objection is to the external exposing of a limit on number of threads.

This is the same problem with using an integer cookie for dlopen returns.
 
>> We could allocate the thread numbers lazily, and that would certainly
>> avoid limiting ourselves to only allocating 2^64-1 threads. On top of
>> that if the function could return an error then we could return such
>> an error at overflow:
>>
>>   int pthread_thread_number_np (uint64_t* thread_number, pthread_t @var{thread})
>>
>> Returns 0 if the thread has a unique number, otherwise -1 if it does not.
> 
> That's a bad interface.  If you are worried about 64-bit overflow, we
> should use a 128-bit counter instead, but I'm not convinced this is
> necessary.

I *might* be convinced a 128-bit return is enough.

I would like to see a quick discussion of why we can't make an interface
that doesn't limit the number of threads?

We should not impose arbitrary limits on our interfaces.

Anything that uniquely identifies an object is going to, by the nature of
the requirement, be a variable-sized opaque value.

The implementation can have an internal 64-bit limit, that's fine, but the
external interface would then support an unlimited number of threads.

We can even provide a convenience function to turn the opaque value into
an ASCII string, and such an implementation can just print a 64-bit value
to the string buffer.

Would such an interface be truly terrible?

> Lazy allocation would make the function not safe for use in signal handlers.

Are you going to mark this function as AS-safe in the documentation?

Yes, you used '@assafe{}' in the manual, OK.

Note that your '@acsafe{}' markup in the manual is wrong, since this function
holds locks and is therefore not AS-safe.

>> My worry here is that once you open pandoras box and say that they thread
>> number *might* be reused after 2^64-1 pthread_create's, then users will
>> start writing code *not* to treat it as unique.
> 
> I'm going to put in a __libc_fatal, just to be on the safe side.

That's a good idea.

>> Thus, see my notes above about changing the interface slightly.
>>
>>> +
>>> +The returned number is only unique with regards to the current process.
>>> +It may be shared by subprocesses and other processes in the system.
>>> +
>>> +The initial (main) thread has number 1.  Thread numbers are not
>>> +necessarily assigned in a consecutive fashion.  They bear no
>>> +relationship to process IDs or thread IDs assigned by the kernel.
>>
>> Suggest:
>>
>> They bear no relationship to POSIX thread IDs (pthread_t), process IDs,
>> or Linux kernel thread IDs.
> 
> “
> They bear no relationship to POSIX thread IDs (@code{pthread_t} values), process IDs or thread IDs assigned by the kernel.
> ”
> 
> (This is in the generic part of the manual, so we shouldn't reference Linux unless absolutely necessary.)

Looks good.

>>> +    pthread_thread_number_np;
>>> +  }
>>>     GLIBC_PRIVATE {
>>>       __libc_alloca_cutoff;
>>>       # Internal libc interface to libpthread
>>> diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
>>> index 1cc7893195..0e1cf3939e 100644
>>> --- a/nptl/allocatestack.c
>>> +++ b/nptl/allocatestack.c
>>> @@ -413,16 +413,21 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
>>>     assert (powerof2 (pagesize_m1 + 1));
>>>     assert (TCB_ALIGNMENT >= STACK_ALIGN);
>>>   -  /* Get the stack size from the attribute if it is set.  Otherwise we
>>> -     use the default we determined at start time.  */
>>> -  if (attr->stacksize != 0)
>>> -    size = attr->stacksize;
>>> -  else
>>> -    {
>>> -      lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
>>> +  uint64_t thread_number;
>>> +  lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
>>
>> You need to add some P&C comments explaining that __default_pthread_attr_lock
>> is now *also* protecting the global_thread_number global and why.
> 
> I now have:
> 
>   uint64_t thread_number;
>   lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
>   {
>     /* Number 1 is reserved for the initial thread.  Reuse
>        __default_pthread_attr_lock to avoid concurrent updates of this
>        counter.  */

OK.

>     static uint64_t global_thread_number = 1;
>     thread_number = ++global_thread_number;
> 
>     /* Check for counter wrap-around.  This should never happen
>        because 2**64 is such a large value.  */
>     if (thread_number == 0)
>       __libc_fatal ("Fatal glibc error: maximum number of threads exceeded\n");
> 
>>> +static int
>>> +do_test (void)
>>> +{
>>> +  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
>>> +  support_isolate_in_subprocess (subprocess, NULL);
>>
>> Why do we isolate this in a subprocess?
> 
> To check that the main thread has thread number 1 in a subprocess.  I'll add a comment.
> 
> New patch attached.

Changes requested:

- Please add another comment in nptl/vars.c adding notes that show that 
  __default_pthread_attr_lock is being used to protect the thread number.

- Explore an interface that doesn't limit the number of threads we can
  create, or continue to convince me I'm crazy. Yes, with a 128-bit counter
  you could have zeptsecond thread creation times and still not overflow
  before the sun expires. That is to say that I'm looking to be convinced
  that a variable sized cookie interface for external uses is a bad idea.
  
Florian Weimer Dec. 20, 2017, 2:34 p.m. UTC | #2
On 12/20/2017 09:06 AM, Carlos O'Donell wrote:

> If by "random bit generators" we are talking about arc4random, we could
> add this as an internal interface for direct use by arc4random? That
> wouldn't be a problem. I would not want this review to block arc4random.

I need to split the patch into manageable pieces anyway.

> One can create and destroy an infinite number of condition variables.

That's true.  But you cannot perform an arbitrary number of operations 
on a single condition variable.  Eventually, the sequence counter will 
overflow.  Which is why I think the comparison is still valid, as far as 
the implementation is concerned.

> The design of the pthread_thread_number_np API immediately limits the
> number of creatable threads to the size of the return type. If we allow
> creating more threads than that then we break the API.

> My objection is not with the internal implementation of a 64-bit counter.
> 
> My objection is to the external exposing of a limit on number of threads.

Well, the counterargument is that 2**64 is so large that by the time we 
have such machines which create as many threads within the life-time of 
a single process, we will have a new ABI (for off128_t) and can switch 
the return value to uint128_t.

The problem is that the 128-bit number actually *reduces* the usefulness 
of this interface because you cannot assume that a register-wide number 
confers uniqueness.  So using uint64_t here may not be entirely 
future-proof, but it will be good enough for currently-existing machines.

Current hardware can do around 200 million shared counter increments per 
second.  And that's just the counter, with single-threaded atomic 
increments.  As soon as multiple threads are involved, the performance 
drops.  Even if we assume a hundred-fold speed increase for the 
single-threaded case, we still end up with thirty years of run time 
until the wraparound happens.

I started to implement an alternative with two 64-bit counters in them, 
but to be honest, I don't think this interface has much value.

> The implementation can have an internal 64-bit limit, that's fine, but the
> external interface would then support an unlimited number of threads.
> 
> We can even provide a convenience function to turn the opaque value into
> an ASCII string, and such an implementation can just print a 64-bit value
> to the string buffer.
> 
> Would such an interface be truly terrible?

It would be pretty much useless for anything but logging.  Random bit 
stream generators typically need a fixed-size personalization string, 
and concurrency algorithms really need a register-sized thread number.

>> Lazy allocation would make the function not safe for use in signal handlers.
> 
> Are you going to mark this function as AS-safe in the documentation?
> 
> Yes, you used '@assafe{}' in the manual, OK.
> 
> Note that your '@acsafe{}' markup in the manual is wrong, since this function
> holds locks and is therefore not AS-safe.

The actual function doesn't use locking.  The only locking occurs during 
thread creation.

Thanks,
Florian
  
Carlos O'Donell Dec. 20, 2017, 5:58 p.m. UTC | #3
On 12/20/2017 06:34 AM, Florian Weimer wrote:
>> The design of the pthread_thread_number_np API immediately limits the
>> number of creatable threads to the size of the return type. If we allow
>> creating more threads than that then we break the API.
> 
>> My objection is not with the internal implementation of a 64-bit counter.
>>
>> My objection is to the external exposing of a limit on number of threads.
> 
> Well, the counterargument is that 2**64 is so large that by the time
> we have such machines which create as many threads within the
> life-time of a single process, we will have a new ABI (for off128_t)
> and can switch the return value to uint128_t.

OK, so you argue that the uint64_t is temporary, and we will provide
larger types as the computing infrastructure changes, with the ability
to handle more unique thread creation events in the future?

I am happier with this line of reasoning. Let me review the patch again
with this in mind and make some suggestions.
  
Carlos O'Donell Dec. 21, 2017, 9:26 a.m. UTC | #4
On 12/14/2017 11:47 PM, Florian Weimer wrote:
> Subject: [PATCH] nptl: Add pthread_thread_number_np function
> To: libc-alpha@sourceware.org

I am re-reviewing this v2 of the patch, and in light of our discussion
downstream in this same thread. The intent is not to create an artificial
limit on the number of threads, but to provide vitally useful functions
for logging and uniquely identifying threads.

It is currently accepted, by both yourself, and Andrew Pinski, that the
64-bit thread identifier is large enough that no such overflow would be
probable within the lifetime of the hardware this code would run upon.
Even then we might extend this to 128-bit at a future date, which would
last until the sun burned out (unless we get massive parallelism, but
by then I hope pthreads is not be the interface in use)

I also understand that using a default of 128-bit, which would certainly
be future-proof, would be less useful because the type is harder to
manipulate efficiently, and makes the interface less useful.

We need a v3.

- See my suggestions for the manual.
- See my questions about preventing user abuse of monotonically increasing
  values from 1 for purposes we do not intend.

> The implementation is actually in libc.so.  With a full implementation
> of pthread_self in libc.so, pthread_thread_number_np is completely
> usable without libpthread.
> 
> 2017-12-14  Florian Weimer  <fweimer@redhat.com>
> 
> 	nptl: Add pthread_thread_number_np function.
> 	* csu/libc-tls.c (__libc_setup_tls): Call __dl_inittcb.
> 	* elf/Makefile (dl-routines): Add dl-inittcb.
> 	* elf/dl-inittcb.c: New file.
> 	* elf/rtld.c (init_tls): Call __dl_inittcb.
> 	* manual/threads.texi (Non-POSIX Extensions): Reference Non-POSIX
> 	Extensions.
> 	(Non-POSIX Extensions): New node.
> 	* nptl/Makefile (routines): Add thread_number.
> 	(tests): Add tst-thread_number-single, tst-thread_number-multi,
> 	tst-thread_number-single-static, tst-thread_number-multi-static.
> 	(tests-nolibpthread): Add tst-thread_number-single,
> 	tst-thread_number-single-static.
> 	(tests-static): Add tst-thread_number-single-static,
> 	tst-thread_number-multi-static.
> 	* nptl/Versions (GLIBC_2.27): Export pthread_thread_number_np.
> 	* nptl/allocatestack.c (allocate_stack): Increment
> 	global_thread_number under__default_pthread_attr_lock and use its
> 	value to set the new thread number.
> 	* nptl/descr.h (struct pthread): Add number member.
> 	* nptl/thread_number.c: New file.
> 	* nptl/tst-thread_number-multi.c: Likewise.
> 	* nptl/tst-thread_number-single.c: Likewise.
> 	* nptl/tst-thread_number-multi-static.c: Likewise.
> 	* nptl/tst-thread_number-single-static.c: Likewise.
> 	* sysdeps/nptl/pthread.h (pthread_thread_number_np): Declare.
> 	* sysdeps/unix/sysv/linux/libc**.abilist: Update.
> 
> diff --git a/NEWS b/NEWS
> index c5607c855f..257b252a63 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -46,7 +46,8 @@ Major new features:
>    _Float32x types, as defined by ISO/IEC TS 18661-3:2015.  These are
>    corresponding interfaces to those supported for _Float128.
>  
> -* glibc now implements the memfd_create and mlock2 functions on Linux.
> +* glibc now implements the memfd_create, mlock2, and
> +  pthread_thread_number_np functions on Linux.

OK.

>  
>  * Support for memory protection keys was added.  The <sys/mman.h> header now
>    declares the functions pkey_alloc, pkey_free, pkey_mprotect, pkey_set,
> diff --git a/csu/libc-tls.c b/csu/libc-tls.c
> index 00138eb43a..af81cba12c 100644
> --- a/csu/libc-tls.c
> +++ b/csu/libc-tls.c
> @@ -195,6 +195,7 @@ __libc_setup_tls (void)
>  #endif
>    if (__builtin_expect (lossage != NULL, 0))
>      _startup_fatal (lossage);
> +  __dl_inittcb ();

OK.

>  
>    /* Update the executable's link map with enough information to make
>       the TLS routines happy.  */
> diff --git a/elf/Makefile b/elf/Makefile
> index 8563555079..66ce2953a4 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -30,7 +30,7 @@ routines	= $(all-dl-routines) dl-support dl-iteratephdr \
>  # profiled libraries.
>  dl-routines	= $(addprefix dl-,load lookup object reloc deps hwcaps \
>  				  runtime init fini debug misc \
> -				  version profile tls origin scope \
> +				  version profile tls inittcb origin scope \

OK.

>  				  execstack caller open close trampoline \
>  				  exception sort-maps)
>  ifeq (yes,$(use-ldconfig))
> diff --git a/elf/dl-inittcb.c b/elf/dl-inittcb.c
> new file mode 100644
> index 0000000000..5a70e9775d
> --- /dev/null
> +++ b/elf/dl-inittcb.c
> @@ -0,0 +1,22 @@
> +/* Initialize TCB contents.  Generic version.
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +void
> +__dl_inittcb (void)
> +{
> +}

OK.

> diff --git a/elf/rtld.c b/elf/rtld.c
> index cfd3729b8e..214e2312fa 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -740,6 +740,7 @@ cannot allocate TLS data structures for initial thread\n");
>    const char *lossage = TLS_INIT_TP (tcbp);
>    if (__glibc_unlikely (lossage != NULL))
>      _dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage);
> +  __dl_inittcb ();

OK.

>    tls_init_tp_called = true;
>  
>    return tcbp;
> diff --git a/manual/threads.texi b/manual/threads.texi
> index 769d974d50..6aa08f398a 100644
> --- a/manual/threads.texi
> +++ b/manual/threads.texi
> @@ -80,6 +80,7 @@ the standard.
>  @menu
>  * Default Thread Attributes::             Setting default attributes for
>  					  threads in a process.
> +* Identifying Threads::                   Unique identifiers for threads.

OK.

>  @end menu
>  
>  @node Default Thread Attributes
> @@ -124,6 +125,30 @@ The system does not have sufficient memory.
>  @end table
>  @end deftypefun
>  
> +@node Identifying Threads
> +@subsection Unique identifiers for threads
> +
> +@Theglibc{} provides a non-standard function to obtain a unique thread
> +identifier.
> +
> +@deftypefun uint64_t pthread_thread_number_np (pthread_t @var{thread})
> +@standards{GNU, pthread.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> +
> +This function returns a number that uniquely identifies @var{thread}
> +among all past, current, and future running threads.  This number does
> +not change during the life-time of the thread.  Once returned by this
> +function, a number will not be reused after the thread terminates.
> +
> +The returned number is only unique with regards to the current process.
> +It may be shared by subprocesses and other processes in the system.
> +
> +The initial (main) thread has number 1.  Thread numbers are not
> +necessarily assigned in a consecutive fashion.  They bear no
> +relationship to POSIX thread IDs (@code{pthread_t} values), process IDs
> +or thread IDs assigned by the kernel.

I would like us to add something like this:
~~~
While the return type of this function is only 64-bits wide, the intent
is not to impose an artificial limit on the number of threads that can be
created by the runtime. In the future this interface may be extended
to 128-bits to support creating as many threads as a user may need
for the lifetime of the process.
~~~

That way the intent of the interface and future changes are clear.

I figure it's implied that this function cannot fail because it has no
error code, and therefore any internal failure aborts the process, as
you added with the assert.

> +@end deftypefun
> +
>  @c FIXME these are undocumented:
>  @c pthread_atfork
>  @c pthread_attr_destroy
> diff --git a/nptl/Makefile b/nptl/Makefile
> index ae388d1112..087fb6a8b5 100644
> --- a/nptl/Makefile
> +++ b/nptl/Makefile
> @@ -30,7 +30,7 @@ install-lib-ldscripts := libpthread.so
>  
>  routines = alloca_cutoff forward libc-lowlevellock libc-cancellation \
>  	   libc-cleanup libc_pthread_init libc_multiple_threads \
> -	   register-atfork unregister-atfork pthread_self
> +	   register-atfork unregister-atfork pthread_self thread_number

OK.

>  shared-only-routines = forward
>  
>  # We need to provide certain routines for compatibility with existing
> @@ -302,7 +302,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
>  			    c89 gnu89 c99 gnu99 c11 gnu11) \
>  	tst-bad-schedattr \
>  	tst-thread_local1 tst-mutex-errorcheck tst-robust10 \
> -	tst-robust-fork tst-create-detached tst-memstream
> +	tst-robust-fork tst-create-detached tst-memstream \
> +	tst-thread_number-single tst-thread_number-multi \

OK.

>  
>  tests-internal := tst-rwlock19 tst-rwlock20 \
>  		  tst-sem11 tst-sem12 tst-sem13 \
> @@ -318,7 +319,9 @@ test-srcs = tst-oddstacklimit
>  test-xfail-tst-once5 = yes
>  
>  # Files which must not be linked with libpthread.
> -tests-nolibpthread = tst-unload
> +tests-nolibpthread = tst-unload \
> +  tst-thread_number-single \
> +  tst-thread_number-single-static \
>  
>  gen-as-const-headers = pthread-errnos.sym \
>  		       unwindbuf.sym \
> @@ -433,9 +436,13 @@ link-libc-static := $(common-objpfx)libc.a $(static-gnulib) \
>  tests-static += tst-locale1 tst-locale2 tst-stackguard1-static \
>  		tst-cancel21-static tst-cancel24-static tst-cond8-static \
>  		tst-mutex8-static tst-mutexpi8-static tst-sem11-static \
> -		tst-sem12-static
> +		tst-sem12-static tst-thread_number-single-static \
> +		tst-thread_number-multi-static \

OK.

> +
>  tests += tst-cancel21-static tst-cancel24-static \
> -	 tst-cond8-static
> +  tst-cond8-static tst-thread_number-single-static \
> +  tst-thread_number-multi-static \
> +

OK.

>  tests-internal += tst-sem11-static tst-sem12-static tst-stackguard1-static
>  xtests-static += tst-setuid1-static
>  
> diff --git a/nptl/Versions b/nptl/Versions
> index 0ae5def464..a7204912a8 100644
> --- a/nptl/Versions
> +++ b/nptl/Versions
> @@ -28,6 +28,9 @@ libc {
>      pthread_cond_wait; pthread_cond_signal;
>      pthread_cond_broadcast; pthread_cond_timedwait;
>    }
> +  GLIBC_2.27 {
> +    pthread_thread_number_np;
> +  }

OK. Typo fixed.

>    GLIBC_PRIVATE {
>      __libc_alloca_cutoff;
>      # Internal libc interface to libpthread
> diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
> index 1cc7893195..454df7740b 100644
> --- a/nptl/allocatestack.c
> +++ b/nptl/allocatestack.c
> @@ -413,16 +413,28 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
>    assert (powerof2 (pagesize_m1 + 1));
>    assert (TCB_ALIGNMENT >= STACK_ALIGN);
>  
> -  /* Get the stack size from the attribute if it is set.  Otherwise we
> -     use the default we determined at start time.  */
> -  if (attr->stacksize != 0)
> -    size = attr->stacksize;
> -  else
> -    {
> -      lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
> +  uint64_t thread_number;
> +  lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
> +  {
> +    /* Number 1 is reserved for the initial thread.  Reuse
> +       __default_pthread_attr_lock to avoid concurrent updates of this
> +       counter.  */

OK.

> +    static uint64_t global_thread_number = 1;
> +    thread_number = ++global_thread_number;

Alright, here comes serious worry #1.

If we say "Thread numbers are not necessarily assigned in a consecutive fashion.",
and we assign them in a consecutive fashion, users will ignore this statement
and use what empirically appears to be true.

People start relying on this counter incrementing from 1 upwards.

People start using this monotonic property for indexing.

Soon we can't change it because it's implied API behaviour.

I think we should disabuse them from doing something low cost to roll the value:

* Do nothing for thread 1, leaving it 1.
* Check global_thread_number for overflow instead.
* Pick a random number of bits to roll between 0-63 (picked at process startup)
* Roll the value by some that number of bits.
* Use the rolled value as the thread_number

Alternatively just pick __GLIBC_MINOR__ as the number of bits 
to roll so this changes with each major release.

> +
> +    /* Check for counter wrap-around.  This should never happen
> +       because 2**64 is such a large value.  */
> +    if (thread_number == 0)
> +      __libc_fatal ("Fatal glibc error: maximum number of threads exceeded\n");

OK. Perfect, overflow has defined fatal behaviour. This is good.

> +
> +    /* Get the stack size from the attribute if it is set.  Otherwise
> +       we use the default we determined at start time.  */
> +    if (attr->stacksize != 0)
> +      size = attr->stacksize;
> +    else
>        size = __default_pthread_attr.stacksize;
> -      lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE);
> -    }
> +  }
> +  lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE);

OK.

>  
>    /* Get memory for the stack.  */
>    if (__glibc_unlikely (attr->flags & ATTR_FLAG_STACKADDR))
> @@ -758,6 +770,8 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
>  #endif
>    pd->robust_head.list = &pd->robust_head;
>  
> +  pd->number = thread_number;

OK.

> +
>    /* We place the thread descriptor at the end of the stack.  */
>    *pdp = pd;
>  
> diff --git a/nptl/descr.h b/nptl/descr.h
> index c83b17b674..49e266139e 100644
> --- a/nptl/descr.h
> +++ b/nptl/descr.h
> @@ -395,6 +395,9 @@ struct pthread
>    /* Resolver state.  */
>    struct __res_state res;
>  
> +  /* Unique number assigned to this thread.  */
> +  uint64_t number;

OK.

> +
>    /* This member must be last.  */
>    char end_padding[];
>  
> diff --git a/nptl/thread_number.c b/nptl/thread_number.c
> new file mode 100644
> index 0000000000..a9fdaa508b
> --- /dev/null
> +++ b/nptl/thread_number.c
> @@ -0,0 +1,26 @@
> +/* Unique numbers for threads.
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include "pthreadP.h"
> +
> +__uint64_t
> +pthread_thread_number_np (pthread_t threadid)
> +{
> +  struct pthread *pd = (struct pthread *) threadid;
> +  return pd->number;
> +}

OK.

> diff --git a/nptl/tst-thread_number-multi-static.c b/nptl/tst-thread_number-multi-static.c
> new file mode 100644
> index 0000000000..658928cfd5
> --- /dev/null
> +++ b/nptl/tst-thread_number-multi-static.c
> @@ -0,0 +1,19 @@
> +/* Test unique numbers for threads, static multi-threaded version.
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include "tst-thread_number-multi.c"

OK.

> diff --git a/nptl/tst-thread_number-multi.c b/nptl/tst-thread_number-multi.c
> new file mode 100644
> index 0000000000..881fe6b097
> --- /dev/null
> +++ b/nptl/tst-thread_number-multi.c
> @@ -0,0 +1,101 @@
> +/* Test unique numbers for threads, non-static multi-threaded version.
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <pthread.h>
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <support/check.h>
> +#include <support/namespace.h>
> +#include <support/xthread.h>
> +
> +/* Used to check that the main thread still has thread number 1 in a
> +   subprocess.  */
> +static void
> +subprocess (void *closure)
> +{
> +  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
> +}

OK.

> +
> +static void *
> +subthread (void *closure)
> +{
> +  if (closure != NULL)
> +    xpthread_barrier_wait (closure);
> +  return NULL;
> +}
> +
> +static int
> +compare (const void *pleft, const void *pright)
> +{
> +  uint64_t left = *(const uint64_t *)pleft;
> +  uint64_t right = *(const uint64_t *)pright;
> +  if (left < right)
> +    return -1;
> +  if (left > right)
> +    return 1;
> +  return 0;
> +}
> +
> +static int
> +do_test (void)
> +{
> +  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
> +  support_isolate_in_subprocess (subprocess, NULL);
> +
> +  /* Create thread_count threads, half of which are joined
> +     immediately, have of which stay around.  */
> +  enum { thread_count = 10 };
> +  pthread_barrier_t barrier;
> +  xpthread_barrier_init (&barrier, NULL, thread_count / 2 + 1);
> +  uint64_t ids[thread_count];
> +  pthread_t threads[thread_count]; /* Only even-numbered entries are valid.  */
> +  for (int i = 0; i < thread_count; ++i)
> +    {
> +      bool stay_around = (i % 2) == 0;
> +      threads[i] = xpthread_create (NULL, subthread,
> +                                    stay_around ? &barrier : NULL);
> +      ids[i] = pthread_thread_number_np (threads[i]);
> +      TEST_VERIFY (ids[i] != 1);

OK.

> +      if (!stay_around)
> +        xpthread_join (threads[i]);
> +    }
> +
> +  /* Check that the IDs are all distinct.  */
> +  qsort (ids, thread_count, sizeof (ids[0]), compare);
> +  for (int i = 1; i < thread_count; ++i)
> +    TEST_VERIFY (ids[i - 1] < ids[i]);

OK.

> +
> +  /* Main thread ID should remain at 1.  */
> +  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
> +  support_isolate_in_subprocess (subprocess, NULL);

OK.

> +
> +  /* Clean up.  */
> +  xpthread_barrier_wait (&barrier);
> +  for (int i = 0; i < thread_count; ++i)
> +    if ((i % 2) == 0)
> +      xpthread_join (threads[i]);
> +
> +  /* Main thread ID should still remain at 1.  */
> +  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
> +  support_isolate_in_subprocess (subprocess, NULL);
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/nptl/tst-thread_number-single-static.c b/nptl/tst-thread_number-single-static.c
> new file mode 100644
> index 0000000000..5c21063c36
> --- /dev/null
> +++ b/nptl/tst-thread_number-single-static.c
> @@ -0,0 +1,19 @@
> +/* Test unique numbers for threads, static single-threaded version.
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include "tst-thread_number-single.c"

OK.

> diff --git a/nptl/tst-thread_number-single.c b/nptl/tst-thread_number-single.c
> new file mode 100644
> index 0000000000..7d3e7ee1dd
> --- /dev/null
> +++ b/nptl/tst-thread_number-single.c
> @@ -0,0 +1,40 @@
> +/* Test unique numbers for threads, non-static single-threaded version.
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <pthread.h>
> +#include <support/check.h>
> +#include <support/namespace.h>
> +
> +/* Used to check that the main thread still has thread number 1 in a
> +   subprocess.  */
> +static void
> +subprocess (void *closure)
> +{
> +  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
> +}
> +
> +static int
> +do_test (void)
> +{
> +  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
> +  support_isolate_in_subprocess (subprocess, NULL);
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 7a65dc641c..99f2e58875 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -1056,6 +1056,10 @@ void __libc_setup_tls (void);
>  void __pthread_initialize_minimal (void) weak_function;
>  #endif
>  
> +/* Initialize the already-existing TCB for the main thread.  Called
> +   during dynamic linker startup or from __libc_setup_tls.  */
> +void __dl_inittcb (void) attribute_hidden;

OK.

> +
>  /* Allocate memory for static TLS block (unless MEM is nonzero) and dtv.  */
>  extern void *_dl_allocate_tls (void *mem);
>  rtld_hidden_proto (_dl_allocate_tls)
> diff --git a/sysdeps/nptl/dl-inittcb.c b/sysdeps/nptl/dl-inittcb.c
> new file mode 100644
> index 0000000000..c25424dfa6
> --- /dev/null
> +++ b/sysdeps/nptl/dl-inittcb.c
> @@ -0,0 +1,27 @@
> +/* Initialize TCB contents.  NPTL version.
> +   Copyright (C) 2017 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <ldsodefs.h>
> +#include <tls.h>
> +
> +void
> +__dl_inittcb (void)
> +{
> +  /* The main thread has number 1.  */
> +  THREAD_SELF->number = 1;
> +}

OK.

> diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
> index 2b2b386ab3..e0714ed951 100644
> --- a/sysdeps/nptl/pthread.h
> +++ b/sysdeps/nptl/pthread.h
> @@ -1148,6 +1148,10 @@ extern int pthread_atfork (void (*__prepare) (void),
>  			   void (*__child) (void)) __THROW;
>  
>  
> +/* Return a number uniquely identifying THREAD, even after its
> +   termination.  */
> +__uint64_t pthread_thread_number_np (pthread_t __thread_id) __THROW;
> +

OK.

>  #ifdef __USE_EXTERN_INLINES
>  /* Optimizations.  */
>  __extern_inline int
> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index ec0ead15dd..e9ba69cd43 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2113,6 +2113,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf128 F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index 5355769974..71258b014b 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2024,6 +2024,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf128 F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index 9bafe71b51..df5eb357f8 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -114,6 +114,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 90aa8d034f..3c37cf48ec 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -1878,6 +1878,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index 4d44c30c64..aba40e2119 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2043,6 +2043,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index 112fc57634..6ef38f5150 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -1907,6 +1907,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index 2e8b6a4586..63cda3a92e 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -115,6 +115,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index 3c33400f67..4b0d923e1c 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1992,6 +1992,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> index e1b1a579d2..68c927f54e 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2113,6 +2113,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index c1550323f3..71f3785cdd 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1967,6 +1967,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index 3b3a172e4f..09d7344ce9 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1965,6 +1965,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 101ca7a241..fea609bd3a 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1963,6 +1963,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf128 F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 2d129f7170..3cd9e072a3 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1958,6 +1958,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf128 F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index 8bc350aff8..b8b2941999 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2154,6 +2154,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 127c426e1c..60f5c63cc9 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1996,6 +1996,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index a9411318e2..f538a91cb2 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -2001,6 +2001,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> index d7bf5db601..f83d43c8cb 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
> @@ -2208,6 +2208,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> index a3415a72ac..e61ce85a68 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> @@ -115,6 +115,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index 414338f9a2..ae74cb8184 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -1996,6 +1996,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf128 F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index f0f7a69b64..fffbcb3d3e 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1897,6 +1897,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf128 F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index 9f95aba898..cb8eb05b2e 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1882,6 +1882,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index 83fbdf2d7e..4184dcff17 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1989,6 +1989,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf128 F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index ee84ad10bc..cb9a0ae97f 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1926,6 +1926,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf128 F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> index dcbfbc05ac..c305690b5a 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> @@ -2120,6 +2120,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> index 53dc99c45a..be6507ee22 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> @@ -2120,6 +2120,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> index dcbfbc05ac..c305690b5a 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> @@ -2120,6 +2120,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index ae4dcaa47e..f418549369 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1884,6 +1884,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index 0dbda14796..6bc513a7ed 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2127,6 +2127,7 @@ GLIBC_2.27 pkey_free F
>  GLIBC_2.27 pkey_get F
>  GLIBC_2.27 pkey_mprotect F
>  GLIBC_2.27 pkey_set F
> +GLIBC_2.27 pthread_thread_number_np F
>  GLIBC_2.27 strfromf32 F
>  GLIBC_2.27 strfromf32x F
>  GLIBC_2.27 strfromf64 F

OK.
  
Florian Weimer Dec. 21, 2017, 11:03 a.m. UTC | #5
On 12/21/2017 10:26 AM, Carlos O'Donell wrote:

>> +The returned number is only unique with regards to the current process.
>> +It may be shared by subprocesses and other processes in the system.
>> +
>> +The initial (main) thread has number 1.  Thread numbers are not
>> +necessarily assigned in a consecutive fashion.  They bear no
>> +relationship to POSIX thread IDs (@code{pthread_t} values), process IDs
>> +or thread IDs assigned by the kernel.
> 
> I would like us to add something like this:
> ~~~
> While the return type of this function is only 64-bits wide, the intent
> is not to impose an artificial limit on the number of threads that can be
> created by the runtime. In the future this interface may be extended
> to 128-bits to support creating as many threads as a user may need
> for the lifetime of the process.
> ~~~
> 
> That way the intent of the interface and future changes are clear.

So how would a programmer use this interface in a future-proof way?  I 
think such a statement would raise more questions than it answers.

>> diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
>> index 1cc7893195..454df7740b 100644
>> --- a/nptl/allocatestack.c
>> +++ b/nptl/allocatestack.c
>> @@ -413,16 +413,28 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
>>     assert (powerof2 (pagesize_m1 + 1));
>>     assert (TCB_ALIGNMENT >= STACK_ALIGN);
>>   
>> -  /* Get the stack size from the attribute if it is set.  Otherwise we
>> -     use the default we determined at start time.  */
>> -  if (attr->stacksize != 0)
>> -    size = attr->stacksize;
>> -  else
>> -    {
>> -      lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
>> +  uint64_t thread_number;
>> +  lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
>> +  {
>> +    /* Number 1 is reserved for the initial thread.  Reuse
>> +       __default_pthread_attr_lock to avoid concurrent updates of this
>> +       counter.  */
> 
> OK.
> 
>> +    static uint64_t global_thread_number = 1;
>> +    thread_number = ++global_thread_number;
> 
> Alright, here comes serious worry #1.
> 
> If we say "Thread numbers are not necessarily assigned in a consecutive fashion.",
> and we assign them in a consecutive fashion, users will ignore this statement
> and use what empirically appears to be true.

The above does not actually assign thread numbers in a consecutive 
fashion, from an application perspective because the implementation can 
create its own threads for its own internal use.  (librt and libanl do 
this.)

> People start relying on this counter incrementing from 1 upwards.
> 
> People start using this monotonic property for indexing.
> 
> Soon we can't change it because it's implied API behaviour.
> 
> I think we should disabuse them from doing something low cost to roll the value:
> 
> * Do nothing for thread 1, leaving it 1.
> * Check global_thread_number for overflow instead.
> * Pick a random number of bits to roll between 0-63 (picked at process startup)
> * Roll the value by some that number of bits.
> * Use the rolled value as the thread_number

Not sure if I understand this.  Do you want us to start at a random 
value?  Or assign IDs randomly?  The latter will have a collision much 
sooner.

I can switch the thread numbers to a fixed, but random-looking 
permutation of the integers in [0, 2**64), but this looks excessive.

In my opinion, we need to assume at one point that programmers read the 
documentation.

Thanks,
Florian
  
Carlos O'Donell Dec. 21, 2017, 7:19 p.m. UTC | #6
On 12/21/2017 03:03 AM, Florian Weimer wrote:
> On 12/21/2017 10:26 AM, Carlos O'Donell wrote:
> 
>>> +The returned number is only unique with regards to the current process.
>>> +It may be shared by subprocesses and other processes in the system.
>>> +
>>> +The initial (main) thread has number 1.  Thread numbers are not
>>> +necessarily assigned in a consecutive fashion.  They bear no
>>> +relationship to POSIX thread IDs (@code{pthread_t} values), process IDs
>>> +or thread IDs assigned by the kernel.
>>
>> I would like us to add something like this:
>> ~~~
>> While the return type of this function is only 64-bits wide, the intent
>> is not to impose an artificial limit on the number of threads that can be
>> created by the runtime. In the future this interface may be extended
>> to 128-bits to support creating as many threads as a user may need
>> for the lifetime of the process.
>> ~~~
>>
>> That way the intent of the interface and future changes are clear.
> 
> So how would a programmer use this interface in a future-proof way?
> I think such a statement would raise more questions than it answers.

I went to bed thinking much the same thing and worried that perhaps this
text was not appropriate for the manual, but could serve as a comment in
the source code for future maintainers. Since this is really a question
about GNU ethos and avoiding artificial limits.

Would you be opposed to adding the comment to the new function sources?

>>> diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
>>> index 1cc7893195..454df7740b 100644
>>> --- a/nptl/allocatestack.c
>>> +++ b/nptl/allocatestack.c
>>> @@ -413,16 +413,28 @@ allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
>>>     assert (powerof2 (pagesize_m1 + 1));
>>>     assert (TCB_ALIGNMENT >= STACK_ALIGN);
>>>   -  /* Get the stack size from the attribute if it is set.  Otherwise we
>>> -     use the default we determined at start time.  */
>>> -  if (attr->stacksize != 0)
>>> -    size = attr->stacksize;
>>> -  else
>>> -    {
>>> -      lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
>>> +  uint64_t thread_number;
>>> +  lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
>>> +  {
>>> +    /* Number 1 is reserved for the initial thread.  Reuse
>>> +       __default_pthread_attr_lock to avoid concurrent updates of this
>>> +       counter.  */
>>
>> OK.
>>
>>> +    static uint64_t global_thread_number = 1;
>>> +    thread_number = ++global_thread_number;
>>
>> Alright, here comes serious worry #1.
>>
>> If we say "Thread numbers are not necessarily assigned in a consecutive fashion.",
>> and we assign them in a consecutive fashion, users will ignore this statement
>> and use what empirically appears to be true.
> 
> The above does not actually assign thread numbers in a consecutive
> fashion, from an application perspective because the implementation
> can create its own threads for its own internal use.  (librt and
> libanl do this.)

That's a good point.

I know that aio has a helper thraed, so does the notification, so does some
of the related timer routines.

All of those would help break this notion, but these are also rarer features.
 
>> People start relying on this counter incrementing from 1 upwards.
>>
>> People start using this monotonic property for indexing.
>>
>> Soon we can't change it because it's implied API behaviour.
>>
>> I think we should disabuse them from doing something low cost to roll the value:
>>
>> * Do nothing for thread 1, leaving it 1.
>> * Check global_thread_number for overflow instead.
>> * Pick a random number of bits to roll between 0-63 (picked at process startup)
>> * Roll the value by some that number of bits.
>> * Use the rolled value as the thread_number
> 
> Not sure if I understand this.  Do you want us to start at a random
> value?  Or assign IDs randomly?  The latter will have a collision
> much sooner.
> 
> I can switch the thread numbers to a fixed, but random-looking
> permutation of the integers in [0, 2**64), but this looks excessive.

I want a low cost solution that avoids abuses of the interface for
indexing into arrays, or other issues that would break when we change
this in the future. We do not want users to make assumptions about the
values we hand out.

The lowest cost option I can think of is to circular shift the global
counter value by some number of bits.

I don't care for it to be random.

> In my opinion, we need to assume at one point that programmers read
> the documentation.

The implementation should be robust against abuse. That's what makes
it high quality.
  
Florian Weimer Dec. 22, 2017, 4:24 p.m. UTC | #7
On 12/21/2017 08:19 PM, Carlos O'Donell wrote:
> On 12/21/2017 03:03 AM, Florian Weimer wrote:
>> On 12/21/2017 10:26 AM, Carlos O'Donell wrote:
>>
>>>> +The returned number is only unique with regards to the current process.
>>>> +It may be shared by subprocesses and other processes in the system.
>>>> +
>>>> +The initial (main) thread has number 1.  Thread numbers are not
>>>> +necessarily assigned in a consecutive fashion.  They bear no
>>>> +relationship to POSIX thread IDs (@code{pthread_t} values), process IDs
>>>> +or thread IDs assigned by the kernel.
>>>
>>> I would like us to add something like this:
>>> ~~~
>>> While the return type of this function is only 64-bits wide, the intent
>>> is not to impose an artificial limit on the number of threads that can be
>>> created by the runtime. In the future this interface may be extended
>>> to 128-bits to support creating as many threads as a user may need
>>> for the lifetime of the process.
>>> ~~~
>>>
>>> That way the intent of the interface and future changes are clear.
>>
>> So how would a programmer use this interface in a future-proof way?
>> I think such a statement would raise more questions than it answers.
> 
> I went to bed thinking much the same thing and worried that perhaps this
> text was not appropriate for the manual, but could serve as a comment in
> the source code for future maintainers. Since this is really a question
> about GNU ethos and avoiding artificial limits.
> 
> Would you be opposed to adding the comment to the new function sources?

What about this?

/* This function should ideally return an integer wider than uint64_t,
    so that the thread number can never-ever overflow.  We may have to
    switch to a 128-bit return value for new architectures
    (particularly if those provide atomic operations on 128-bit
    integers).  But with current architectures, the baked-in limit of
    2**64 threads ever created by a process is not a problem because
    architectural constraints result in a thread creation rate far
    below one billion threads per second, and even at that rate, a
    64-bit counter lasts for hundreds of years.  */

>>> People start relying on this counter incrementing from 1 upwards.
>>>
>>> People start using this monotonic property for indexing.
>>>
>>> Soon we can't change it because it's implied API behaviour.
>>>
>>> I think we should disabuse them from doing something low cost to roll the value:
>>>
>>> * Do nothing for thread 1, leaving it 1.
>>> * Check global_thread_number for overflow instead.
>>> * Pick a random number of bits to roll between 0-63 (picked at process startup)
>>> * Roll the value by some that number of bits.
>>> * Use the rolled value as the thread_number
>>
>> Not sure if I understand this.  Do you want us to start at a random
>> value?  Or assign IDs randomly?  The latter will have a collision
>> much sooner.
>>
>> I can switch the thread numbers to a fixed, but random-looking
>> permutation of the integers in [0, 2**64), but this looks excessive.
> 
> I want a low cost solution that avoids abuses of the interface for
> indexing into arrays, or other issues that would break when we change
> this in the future. We do not want users to make assumptions about the
> values we hand out.

But the result will be that the numbers are no longer short and easily 
compared for logging/debugging purposes.  I think the value of that is 
higher than trying to punish developers who do not read the manual.

Thanks,
Florian
  
Carlos O'Donell Dec. 22, 2017, 5:09 p.m. UTC | #8
On 12/22/2017 08:24 AM, Florian Weimer wrote:
> On 12/21/2017 08:19 PM, Carlos O'Donell wrote:
>> On 12/21/2017 03:03 AM, Florian Weimer wrote:
>>> On 12/21/2017 10:26 AM, Carlos O'Donell wrote:
>>>
>>>>> +The returned number is only unique with regards to the current process.
>>>>> +It may be shared by subprocesses and other processes in the system.
>>>>> +
>>>>> +The initial (main) thread has number 1.  Thread numbers are not
>>>>> +necessarily assigned in a consecutive fashion.  They bear no
>>>>> +relationship to POSIX thread IDs (@code{pthread_t} values), process IDs
>>>>> +or thread IDs assigned by the kernel.
>>>>
>>>> I would like us to add something like this:
>>>> ~~~
>>>> While the return type of this function is only 64-bits wide, the intent
>>>> is not to impose an artificial limit on the number of threads that can be
>>>> created by the runtime. In the future this interface may be extended
>>>> to 128-bits to support creating as many threads as a user may need
>>>> for the lifetime of the process.
>>>> ~~~
>>>>
>>>> That way the intent of the interface and future changes are clear.
>>>
>>> So how would a programmer use this interface in a future-proof way?
>>> I think such a statement would raise more questions than it answers.
>>
>> I went to bed thinking much the same thing and worried that perhaps this
>> text was not appropriate for the manual, but could serve as a comment in
>> the source code for future maintainers. Since this is really a question
>> about GNU ethos and avoiding artificial limits.
>>
>> Would you be opposed to adding the comment to the new function sources?
> 
> What about this?
> 
> /* This function should ideally return an integer wider than uint64_t,
>    so that the thread number can never-ever overflow.  We may have to
>    switch to a 128-bit return value for new architectures
>    (particularly if those provide atomic operations on 128-bit
>    integers).  But with current architectures, the baked-in limit of
>    2**64 threads ever created by a process is not a problem because
>    architectural constraints result in a thread creation rate far
>    below one billion threads per second, and even at that rate, a
>    64-bit counter lasts for hundreds of years.  */

This looks good to me. Thanks.

>>>> People start relying on this counter incrementing from 1 upwards.
>>>>
>>>> People start using this monotonic property for indexing.
>>>>
>>>> Soon we can't change it because it's implied API behaviour.
>>>>
>>>> I think we should disabuse them from doing something low cost to roll the value:
>>>>
>>>> * Do nothing for thread 1, leaving it 1.
>>>> * Check global_thread_number for overflow instead.
>>>> * Pick a random number of bits to roll between 0-63 (picked at process startup)
>>>> * Roll the value by some that number of bits.
>>>> * Use the rolled value as the thread_number
>>>
>>> Not sure if I understand this.  Do you want us to start at a random
>>> value?  Or assign IDs randomly?  The latter will have a collision
>>> much sooner.
>>>
>>> I can switch the thread numbers to a fixed, but random-looking
>>> permutation of the integers in [0, 2**64), but this looks excessive.
>>
>> I want a low cost solution that avoids abuses of the interface for
>> indexing into arrays, or other issues that would break when we change
>> this in the future. We do not want users to make assumptions about the
>> values we hand out.
> 
> But the result will be that the numbers are no longer short and
> easily compared for logging/debugging purposes.  I think the value of
> that is higher than trying to punish developers who do not read the
> manual.

The numbers are meaningless and should be handled by computers, and
compared by log analyzers, just like how PIDs today are meaningless
and not that small. Even PIDs in logs are large enough that I can't
easily remember them, though I agree that 34519 vs. 431234789734231
is really a big difference.

My objection remains that I think we should have some kind of
perturbation logic in the number generation, but my objection is
*not* sustained.

I do not block consensus, perturbation can continue to be discussed
and applied at a later point. Therefore between you and I there is
consensus that this patch looks good for addition as a new POSIX
thread API.

Please place your patches on the "Release blockers" for this release:
https://sourceware.org/glibc/wiki/Release/2.27#Release_blockers.3F,
so we get them checked in fully.

I say this because I want someone else other than me to ACK these
before we consider the new API OK. I'm hesitant to pursue a model
where we add new APIs without at least two senior reivewer's
consent. I would like Siddhesh to just look over the idea, and give
a +1 to the new API, knowing that I have reviewed the technical
details I want a high-level acceptance of the direction the API
is going. Siddhesh was the last to add a new POSIX thread API with
the default thread attribute settings API being his invention.

Siddhesh, Could you review the high-level details of the new API?
  
Joseph Myers Dec. 22, 2017, 5:43 p.m. UTC | #9
On Fri, 22 Dec 2017, Carlos O'Donell wrote:

> > /* This function should ideally return an integer wider than uint64_t,
> >    so that the thread number can never-ever overflow.  We may have to
> >    switch to a 128-bit return value for new architectures
> >    (particularly if those provide atomic operations on 128-bit
> >    integers).  But with current architectures, the baked-in limit of
> >    2**64 threads ever created by a process is not a problem because
> >    architectural constraints result in a thread creation rate far
> >    below one billion threads per second, and even at that rate, a
> >    64-bit counter lasts for hundreds of years.  */
> 
> This looks good to me. Thanks.

I wonder about representing the intent to allow for a larger type by 
defining and documenting the API to use a pthread_thread_number_t type 
rather than uint64_t.

However, if pthread_thread_number_t is to be conveniently usable, it 
should be specified to be an unsigned integer type, meaning no wider than 
uintmax_t (so you can cast to uintmax_t to print with %ju, for example), 
meaning it can't be unsigned __int128 for any existing glibc ABI as all 
such ABIs have 64-bit uintmax_t.

As with any patch changing the installed pthread.h, the separate hppa 
version of that header needs the same change applied.
  
Rich Felker March 2, 2018, 6:04 p.m. UTC | #10
On Fri, Dec 15, 2017 at 08:47:54AM +0100, Florian Weimer wrote:
> >We could allocate the thread numbers lazily, and that would certainly
> >avoid limiting ourselves to only allocating 2^64-1 threads. On top of
> >that if the function could return an error then we could return such
> >an error at overflow:
> >
> >  int pthread_thread_number_np (uint64_t* thread_number, pthread_t @var{thread})
> >
> >Returns 0 if the thread has a unique number, otherwise -1 if it does not.
> 
> That's a bad interface.  If you are worried about 64-bit overflow,
> we should use a 128-bit counter instead, but I'm not convinced this
> is necessary.
> 
> Lazy allocation would make the function not safe for use in signal handlers.

This is trivially false. You just have to block and restore signal
mask around taking and releasing the lock to make it AS-safe.

I'm also rather unhappy with an API whose external interface is an
assumption that you can never create more than 2^64 threads, even
though from a practical standpoint is seems impossible for the
forseeable future. Just documenting a special return value
(UINT64_MAX? 0?) that means the result was not meaningful would be a
decent fix, given that this whole interface is of dubious utility..

Rich
  
Rich Felker March 2, 2018, 6:08 p.m. UTC | #11
On Fri, Mar 02, 2018 at 01:04:16PM -0500, Rich Felker wrote:
> On Fri, Dec 15, 2017 at 08:47:54AM +0100, Florian Weimer wrote:
> > >We could allocate the thread numbers lazily, and that would certainly
> > >avoid limiting ourselves to only allocating 2^64-1 threads. On top of
> > >that if the function could return an error then we could return such
> > >an error at overflow:
> > >
> > >  int pthread_thread_number_np (uint64_t* thread_number, pthread_t @var{thread})
> > >
> > >Returns 0 if the thread has a unique number, otherwise -1 if it does not.
> > 
> > That's a bad interface.  If you are worried about 64-bit overflow,
> > we should use a 128-bit counter instead, but I'm not convinced this
> > is necessary.
> > 
> > Lazy allocation would make the function not safe for use in signal handlers.
> 
> This is trivially false. You just have to block and restore signal
> mask around taking and releasing the lock to make it AS-safe.
> 
> I'm also rather unhappy with an API whose external interface is an
> assumption that you can never create more than 2^64 threads, even
> though from a practical standpoint is seems impossible for the
> forseeable future. Just documenting a special return value
> (UINT64_MAX? 0?) that means the result was not meaningful would be a
> decent fix, given that this whole interface is of dubious utility..

Further, making it lazy also improves the latter aspect if you're
unwilling to make a dedicated "failure" value. "Libc has to abort if
you ever create more than 2^64 threads" is an awful constraint to be
tied to. "Libc aborts if you ever call this random nonstandard
function from more than 2^64 unique threads" seems tolerable since the
simple mitigation is "don't use that function".

Rich
  
Florian Weimer March 9, 2018, 5:23 p.m. UTC | #12
On 03/02/2018 07:08 PM, Rich Felker wrote:
> Further, making it lazy also improves the latter aspect if you're
> unwilling to make a dedicated "failure" value. "Libc has to abort if
> you ever create more than 2^64 threads" is an awful constraint to be
> tied to. "Libc aborts if you ever call this random nonstandard
> function from more than 2^64 unique threads" seems tolerable since the
> simple mitigation is "don't use that function".

Currently glibc policy is to assume that 2**60 counters never overflow 
(without any checks).  We have functionality which underwent extensive 
peer review with this property.

However, I will come up with something else since there appears to be a 
strong dislike for this interface.

Thanks,
Florian
  
Carlos O'Donell March 9, 2018, 11:28 p.m. UTC | #13
On 03/09/2018 11:23 AM, Florian Weimer wrote:
> On 03/02/2018 07:08 PM, Rich Felker wrote:
>> Further, making it lazy also improves the latter aspect if you're 
>> unwilling to make a dedicated "failure" value. "Libc has to abort
>> if you ever create more than 2^64 threads" is an awful constraint
>> to be tied to. "Libc aborts if you ever call this random
>> nonstandard function from more than 2^64 unique threads" seems
>> tolerable since the simple mitigation is "don't use that
>> function".
> 
> Currently glibc policy is to assume that 2**60 counters never
> overflow (without any checks).  We have functionality which underwent
> extensive peer review with this property.

They were internal hidden API details though?

Like pthread cond signal groups, and fork counters.
 
> However, I will come up with something else since there appears to be
> a strong dislike for this interface.

Either lazy allocation, or a dedicated overflow value are both useful
improvements to a "thread identification" API that has inherent limits
on the size of the identification name space (64-bits).

I don't object because we have lots of space-limited objects like inodes,
etc, and all we've done is make new APIs with wider width types to
resolve these problems.

Cheers,
Carlos.
  

Patch

Subject: [PATCH] nptl: Add pthread_thread_number_np function
To: libc-alpha@sourceware.org

The implementation is actually in libc.so.  With a full implementation
of pthread_self in libc.so, pthread_thread_number_np is completely
usable without libpthread.

2017-12-14  Florian Weimer  <fweimer@redhat.com>

	nptl: Add pthread_thread_number_np function.
	* csu/libc-tls.c (__libc_setup_tls): Call __dl_inittcb.
	* elf/Makefile (dl-routines): Add dl-inittcb.
	* elf/dl-inittcb.c: New file.
	* elf/rtld.c (init_tls): Call __dl_inittcb.
	* manual/threads.texi (Non-POSIX Extensions): Reference Non-POSIX
	Extensions.
	(Non-POSIX Extensions): New node.
	* nptl/Makefile (routines): Add thread_number.
	(tests): Add tst-thread_number-single, tst-thread_number-multi,
	tst-thread_number-single-static, tst-thread_number-multi-static.
	(tests-nolibpthread): Add tst-thread_number-single,
	tst-thread_number-single-static.
	(tests-static): Add tst-thread_number-single-static,
	tst-thread_number-multi-static.
	* nptl/Versions (GLIBC_2.27): Export pthread_thread_number_np.
	* nptl/allocatestack.c (allocate_stack): Increment
	global_thread_number under__default_pthread_attr_lock and use its
	value to set the new thread number.
	* nptl/descr.h (struct pthread): Add number member.
	* nptl/thread_number.c: New file.
	* nptl/tst-thread_number-multi.c: Likewise.
	* nptl/tst-thread_number-single.c: Likewise.
	* nptl/tst-thread_number-multi-static.c: Likewise.
	* nptl/tst-thread_number-single-static.c: Likewise.
	* sysdeps/nptl/pthread.h (pthread_thread_number_np): Declare.
	* sysdeps/unix/sysv/linux/libc**.abilist: Update.

diff --git a/NEWS b/NEWS
index c5607c855f..257b252a63 100644
--- a/NEWS
+++ b/NEWS
@@ -46,7 +46,8 @@  Major new features:
   _Float32x types, as defined by ISO/IEC TS 18661-3:2015.  These are
   corresponding interfaces to those supported for _Float128.
 
-* glibc now implements the memfd_create and mlock2 functions on Linux.
+* glibc now implements the memfd_create, mlock2, and
+  pthread_thread_number_np functions on Linux.
 
 * Support for memory protection keys was added.  The <sys/mman.h> header now
   declares the functions pkey_alloc, pkey_free, pkey_mprotect, pkey_set,
diff --git a/csu/libc-tls.c b/csu/libc-tls.c
index 00138eb43a..af81cba12c 100644
--- a/csu/libc-tls.c
+++ b/csu/libc-tls.c
@@ -195,6 +195,7 @@  __libc_setup_tls (void)
 #endif
   if (__builtin_expect (lossage != NULL, 0))
     _startup_fatal (lossage);
+  __dl_inittcb ();
 
   /* Update the executable's link map with enough information to make
      the TLS routines happy.  */
diff --git a/elf/Makefile b/elf/Makefile
index 8563555079..66ce2953a4 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -30,7 +30,7 @@  routines	= $(all-dl-routines) dl-support dl-iteratephdr \
 # profiled libraries.
 dl-routines	= $(addprefix dl-,load lookup object reloc deps hwcaps \
 				  runtime init fini debug misc \
-				  version profile tls origin scope \
+				  version profile tls inittcb origin scope \
 				  execstack caller open close trampoline \
 				  exception sort-maps)
 ifeq (yes,$(use-ldconfig))
diff --git a/elf/dl-inittcb.c b/elf/dl-inittcb.c
new file mode 100644
index 0000000000..5a70e9775d
--- /dev/null
+++ b/elf/dl-inittcb.c
@@ -0,0 +1,22 @@ 
+/* Initialize TCB contents.  Generic version.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+void
+__dl_inittcb (void)
+{
+}
diff --git a/elf/rtld.c b/elf/rtld.c
index cfd3729b8e..214e2312fa 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -740,6 +740,7 @@  cannot allocate TLS data structures for initial thread\n");
   const char *lossage = TLS_INIT_TP (tcbp);
   if (__glibc_unlikely (lossage != NULL))
     _dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage);
+  __dl_inittcb ();
   tls_init_tp_called = true;
 
   return tcbp;
diff --git a/manual/threads.texi b/manual/threads.texi
index 769d974d50..6aa08f398a 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -80,6 +80,7 @@  the standard.
 @menu
 * Default Thread Attributes::             Setting default attributes for
 					  threads in a process.
+* Identifying Threads::                   Unique identifiers for threads.
 @end menu
 
 @node Default Thread Attributes
@@ -124,6 +125,30 @@  The system does not have sufficient memory.
 @end table
 @end deftypefun
 
+@node Identifying Threads
+@subsection Unique identifiers for threads
+
+@Theglibc{} provides a non-standard function to obtain a unique thread
+identifier.
+
+@deftypefun uint64_t pthread_thread_number_np (pthread_t @var{thread})
+@standards{GNU, pthread.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+
+This function returns a number that uniquely identifies @var{thread}
+among all past, current, and future running threads.  This number does
+not change during the life-time of the thread.  Once returned by this
+function, a number will not be reused after the thread terminates.
+
+The returned number is only unique with regards to the current process.
+It may be shared by subprocesses and other processes in the system.
+
+The initial (main) thread has number 1.  Thread numbers are not
+necessarily assigned in a consecutive fashion.  They bear no
+relationship to POSIX thread IDs (@code{pthread_t} values), process IDs
+or thread IDs assigned by the kernel.
+@end deftypefun
+
 @c FIXME these are undocumented:
 @c pthread_atfork
 @c pthread_attr_destroy
diff --git a/nptl/Makefile b/nptl/Makefile
index ae388d1112..087fb6a8b5 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -30,7 +30,7 @@  install-lib-ldscripts := libpthread.so
 
 routines = alloca_cutoff forward libc-lowlevellock libc-cancellation \
 	   libc-cleanup libc_pthread_init libc_multiple_threads \
-	   register-atfork unregister-atfork pthread_self
+	   register-atfork unregister-atfork pthread_self thread_number
 shared-only-routines = forward
 
 # We need to provide certain routines for compatibility with existing
@@ -302,7 +302,8 @@  tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
 			    c89 gnu89 c99 gnu99 c11 gnu11) \
 	tst-bad-schedattr \
 	tst-thread_local1 tst-mutex-errorcheck tst-robust10 \
-	tst-robust-fork tst-create-detached tst-memstream
+	tst-robust-fork tst-create-detached tst-memstream \
+	tst-thread_number-single tst-thread_number-multi \
 
 tests-internal := tst-rwlock19 tst-rwlock20 \
 		  tst-sem11 tst-sem12 tst-sem13 \
@@ -318,7 +319,9 @@  test-srcs = tst-oddstacklimit
 test-xfail-tst-once5 = yes
 
 # Files which must not be linked with libpthread.
-tests-nolibpthread = tst-unload
+tests-nolibpthread = tst-unload \
+  tst-thread_number-single \
+  tst-thread_number-single-static \
 
 gen-as-const-headers = pthread-errnos.sym \
 		       unwindbuf.sym \
@@ -433,9 +436,13 @@  link-libc-static := $(common-objpfx)libc.a $(static-gnulib) \
 tests-static += tst-locale1 tst-locale2 tst-stackguard1-static \
 		tst-cancel21-static tst-cancel24-static tst-cond8-static \
 		tst-mutex8-static tst-mutexpi8-static tst-sem11-static \
-		tst-sem12-static
+		tst-sem12-static tst-thread_number-single-static \
+		tst-thread_number-multi-static \
+
 tests += tst-cancel21-static tst-cancel24-static \
-	 tst-cond8-static
+  tst-cond8-static tst-thread_number-single-static \
+  tst-thread_number-multi-static \
+
 tests-internal += tst-sem11-static tst-sem12-static tst-stackguard1-static
 xtests-static += tst-setuid1-static
 
diff --git a/nptl/Versions b/nptl/Versions
index 0ae5def464..a7204912a8 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -28,6 +28,9 @@  libc {
     pthread_cond_wait; pthread_cond_signal;
     pthread_cond_broadcast; pthread_cond_timedwait;
   }
+  GLIBC_2.27 {
+    pthread_thread_number_np;
+  }
   GLIBC_PRIVATE {
     __libc_alloca_cutoff;
     # Internal libc interface to libpthread
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
index 1cc7893195..454df7740b 100644
--- a/nptl/allocatestack.c
+++ b/nptl/allocatestack.c
@@ -413,16 +413,28 @@  allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
   assert (powerof2 (pagesize_m1 + 1));
   assert (TCB_ALIGNMENT >= STACK_ALIGN);
 
-  /* Get the stack size from the attribute if it is set.  Otherwise we
-     use the default we determined at start time.  */
-  if (attr->stacksize != 0)
-    size = attr->stacksize;
-  else
-    {
-      lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
+  uint64_t thread_number;
+  lll_lock (__default_pthread_attr_lock, LLL_PRIVATE);
+  {
+    /* Number 1 is reserved for the initial thread.  Reuse
+       __default_pthread_attr_lock to avoid concurrent updates of this
+       counter.  */
+    static uint64_t global_thread_number = 1;
+    thread_number = ++global_thread_number;
+
+    /* Check for counter wrap-around.  This should never happen
+       because 2**64 is such a large value.  */
+    if (thread_number == 0)
+      __libc_fatal ("Fatal glibc error: maximum number of threads exceeded\n");
+
+    /* Get the stack size from the attribute if it is set.  Otherwise
+       we use the default we determined at start time.  */
+    if (attr->stacksize != 0)
+      size = attr->stacksize;
+    else
       size = __default_pthread_attr.stacksize;
-      lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE);
-    }
+  }
+  lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE);
 
   /* Get memory for the stack.  */
   if (__glibc_unlikely (attr->flags & ATTR_FLAG_STACKADDR))
@@ -758,6 +770,8 @@  allocate_stack (const struct pthread_attr *attr, struct pthread **pdp,
 #endif
   pd->robust_head.list = &pd->robust_head;
 
+  pd->number = thread_number;
+
   /* We place the thread descriptor at the end of the stack.  */
   *pdp = pd;
 
diff --git a/nptl/descr.h b/nptl/descr.h
index c83b17b674..49e266139e 100644
--- a/nptl/descr.h
+++ b/nptl/descr.h
@@ -395,6 +395,9 @@  struct pthread
   /* Resolver state.  */
   struct __res_state res;
 
+  /* Unique number assigned to this thread.  */
+  uint64_t number;
+
   /* This member must be last.  */
   char end_padding[];
 
diff --git a/nptl/thread_number.c b/nptl/thread_number.c
new file mode 100644
index 0000000000..a9fdaa508b
--- /dev/null
+++ b/nptl/thread_number.c
@@ -0,0 +1,26 @@ 
+/* Unique numbers for threads.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "pthreadP.h"
+
+__uint64_t
+pthread_thread_number_np (pthread_t threadid)
+{
+  struct pthread *pd = (struct pthread *) threadid;
+  return pd->number;
+}
diff --git a/nptl/tst-thread_number-multi-static.c b/nptl/tst-thread_number-multi-static.c
new file mode 100644
index 0000000000..658928cfd5
--- /dev/null
+++ b/nptl/tst-thread_number-multi-static.c
@@ -0,0 +1,19 @@ 
+/* Test unique numbers for threads, static multi-threaded version.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "tst-thread_number-multi.c"
diff --git a/nptl/tst-thread_number-multi.c b/nptl/tst-thread_number-multi.c
new file mode 100644
index 0000000000..881fe6b097
--- /dev/null
+++ b/nptl/tst-thread_number-multi.c
@@ -0,0 +1,101 @@ 
+/* Test unique numbers for threads, non-static multi-threaded version.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/xthread.h>
+
+/* Used to check that the main thread still has thread number 1 in a
+   subprocess.  */
+static void
+subprocess (void *closure)
+{
+  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
+}
+
+static void *
+subthread (void *closure)
+{
+  if (closure != NULL)
+    xpthread_barrier_wait (closure);
+  return NULL;
+}
+
+static int
+compare (const void *pleft, const void *pright)
+{
+  uint64_t left = *(const uint64_t *)pleft;
+  uint64_t right = *(const uint64_t *)pright;
+  if (left < right)
+    return -1;
+  if (left > right)
+    return 1;
+  return 0;
+}
+
+static int
+do_test (void)
+{
+  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  /* Create thread_count threads, half of which are joined
+     immediately, have of which stay around.  */
+  enum { thread_count = 10 };
+  pthread_barrier_t barrier;
+  xpthread_barrier_init (&barrier, NULL, thread_count / 2 + 1);
+  uint64_t ids[thread_count];
+  pthread_t threads[thread_count]; /* Only even-numbered entries are valid.  */
+  for (int i = 0; i < thread_count; ++i)
+    {
+      bool stay_around = (i % 2) == 0;
+      threads[i] = xpthread_create (NULL, subthread,
+                                    stay_around ? &barrier : NULL);
+      ids[i] = pthread_thread_number_np (threads[i]);
+      TEST_VERIFY (ids[i] != 1);
+      if (!stay_around)
+        xpthread_join (threads[i]);
+    }
+
+  /* Check that the IDs are all distinct.  */
+  qsort (ids, thread_count, sizeof (ids[0]), compare);
+  for (int i = 1; i < thread_count; ++i)
+    TEST_VERIFY (ids[i - 1] < ids[i]);
+
+  /* Main thread ID should remain at 1.  */
+  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  /* Clean up.  */
+  xpthread_barrier_wait (&barrier);
+  for (int i = 0; i < thread_count; ++i)
+    if ((i % 2) == 0)
+      xpthread_join (threads[i]);
+
+  /* Main thread ID should still remain at 1.  */
+  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/nptl/tst-thread_number-single-static.c b/nptl/tst-thread_number-single-static.c
new file mode 100644
index 0000000000..5c21063c36
--- /dev/null
+++ b/nptl/tst-thread_number-single-static.c
@@ -0,0 +1,19 @@ 
+/* Test unique numbers for threads, static single-threaded version.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "tst-thread_number-single.c"
diff --git a/nptl/tst-thread_number-single.c b/nptl/tst-thread_number-single.c
new file mode 100644
index 0000000000..7d3e7ee1dd
--- /dev/null
+++ b/nptl/tst-thread_number-single.c
@@ -0,0 +1,40 @@ 
+/* Test unique numbers for threads, non-static single-threaded version.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <pthread.h>
+#include <support/check.h>
+#include <support/namespace.h>
+
+/* Used to check that the main thread still has thread number 1 in a
+   subprocess.  */
+static void
+subprocess (void *closure)
+{
+  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
+}
+
+static int
+do_test (void)
+{
+  TEST_COMPARE (pthread_thread_number_np (pthread_self ()), 1U);
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 7a65dc641c..99f2e58875 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1056,6 +1056,10 @@  void __libc_setup_tls (void);
 void __pthread_initialize_minimal (void) weak_function;
 #endif
 
+/* Initialize the already-existing TCB for the main thread.  Called
+   during dynamic linker startup or from __libc_setup_tls.  */
+void __dl_inittcb (void) attribute_hidden;
+
 /* Allocate memory for static TLS block (unless MEM is nonzero) and dtv.  */
 extern void *_dl_allocate_tls (void *mem);
 rtld_hidden_proto (_dl_allocate_tls)
diff --git a/sysdeps/nptl/dl-inittcb.c b/sysdeps/nptl/dl-inittcb.c
new file mode 100644
index 0000000000..c25424dfa6
--- /dev/null
+++ b/sysdeps/nptl/dl-inittcb.c
@@ -0,0 +1,27 @@ 
+/* Initialize TCB contents.  NPTL version.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <ldsodefs.h>
+#include <tls.h>
+
+void
+__dl_inittcb (void)
+{
+  /* The main thread has number 1.  */
+  THREAD_SELF->number = 1;
+}
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
index 2b2b386ab3..e0714ed951 100644
--- a/sysdeps/nptl/pthread.h
+++ b/sysdeps/nptl/pthread.h
@@ -1148,6 +1148,10 @@  extern int pthread_atfork (void (*__prepare) (void),
 			   void (*__child) (void)) __THROW;
 
 
+/* Return a number uniquely identifying THREAD, even after its
+   termination.  */
+__uint64_t pthread_thread_number_np (pthread_t __thread_id) __THROW;
+
 #ifdef __USE_EXTERN_INLINES
 /* Optimizations.  */
 __extern_inline int
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index ec0ead15dd..e9ba69cd43 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2113,6 +2113,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf128 F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 5355769974..71258b014b 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2024,6 +2024,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf128 F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index 9bafe71b51..df5eb357f8 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -114,6 +114,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 90aa8d034f..3c37cf48ec 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -1878,6 +1878,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 4d44c30c64..aba40e2119 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2043,6 +2043,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 112fc57634..6ef38f5150 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -1907,6 +1907,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 2e8b6a4586..63cda3a92e 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -115,6 +115,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 3c33400f67..4b0d923e1c 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1992,6 +1992,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index e1b1a579d2..68c927f54e 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2113,6 +2113,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index c1550323f3..71f3785cdd 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1967,6 +1967,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 3b3a172e4f..09d7344ce9 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1965,6 +1965,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 101ca7a241..fea609bd3a 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1963,6 +1963,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf128 F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 2d129f7170..3cd9e072a3 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1958,6 +1958,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf128 F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 8bc350aff8..b8b2941999 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2154,6 +2154,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 127c426e1c..60f5c63cc9 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1996,6 +1996,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index a9411318e2..f538a91cb2 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2001,6 +2001,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
index d7bf5db601..f83d43c8cb 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist
@@ -2208,6 +2208,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index a3415a72ac..e61ce85a68 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -115,6 +115,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 414338f9a2..ae74cb8184 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1996,6 +1996,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf128 F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index f0f7a69b64..fffbcb3d3e 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1897,6 +1897,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf128 F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index 9f95aba898..cb8eb05b2e 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1882,6 +1882,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 83fbdf2d7e..4184dcff17 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1989,6 +1989,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf128 F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index ee84ad10bc..cb9a0ae97f 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1926,6 +1926,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf128 F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
index dcbfbc05ac..c305690b5a 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2120,6 +2120,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
index 53dc99c45a..be6507ee22 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2120,6 +2120,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
index dcbfbc05ac..c305690b5a 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2120,6 +2120,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index ae4dcaa47e..f418549369 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1884,6 +1884,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 0dbda14796..6bc513a7ed 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2127,6 +2127,7 @@  GLIBC_2.27 pkey_free F
 GLIBC_2.27 pkey_get F
 GLIBC_2.27 pkey_mprotect F
 GLIBC_2.27 pkey_set F
+GLIBC_2.27 pthread_thread_number_np F
 GLIBC_2.27 strfromf32 F
 GLIBC_2.27 strfromf32x F
 GLIBC_2.27 strfromf64 F