diff mbox series

Reimplement GNU threads library on native Windows

Message ID 2175092.5hV0XgF4mA@polaris
State New
Headers show
Series Reimplement GNU threads library on native Windows | expand

Commit Message

Eric Botcazou June 28, 2019, 10:46 a.m. UTC
Hi,

this reimplements the GNU threads library on native Windows (except for the 
Objective-C specific subset) using direct Win32 API calls, in lieu of the 
implementation based on semaphores.  This base implementations requires 
Windows XP/Server 2003, which is the default minimal setting of MinGW-W64.
This also adds the support required for the C++11 threads, using again direct 
Win32 API calls; this additional layer requires Windows Vista/Server 2008 and 
is enabled only if _GTHREADS_USE_COND is defined to 1.

This also changes libstdc++ to setting _GTHREADS_USE_COND to 1 when the switch 
--enable-libstdcxx-threads is passed, which means that C++11 threads are still 
disabled by default on native Windows and that you need to explicitly pass the 
switch to enable them.  The 30_threads chapter of the testsuite is clean.

Tested on i686-pc-mingw32 and x86_64-pc-mingw32, OK for the mainline?


2019-06-28  Eric Botcazou  <ebotcazou@adacore.com>

libgcc/
	* config.host (i[34567]86-*-mingw*): Add thread fragment after EH one
	as well as new i386/t-slibgcc-mingw fragment.
	(x86_64-*-mingw*): Likewise.
	* config/i386/gthr-win32.h: If _GTHREADS_USE_COND is 1, define both
	__GTHREAD_HAS_COND & __GTHREADS_CXX0X to 1 and _WIN32_WINNT to 0x0600.
	Error out if _GTHREAD_USE_MUTEX_TIMEDLOCK is 1.
	Include stdlib.h instead of errno.h and do not include _mingw.h.
	(CONST_CAST2): Add specific definition for C++.
	(ATTRIBUTE_UNUSED): New macro.
	(__UNUSED_PARAM): Delete.
	Define WIN32_LEAN_AND_MEAN before including windows.h.
	(__gthread_objc_data_tls): Use TLS_OUT_OF_INDEXES instead of (DWORD)-1.
	(__gthread_objc_init_thread_system): Likewise.
	(__gthread_objc_thread_get_data): Minor tweak.
	(__gthread_objc_condition_allocate): Use ATTRIBUTE_UNUSED.
	(__gthread_objc_condition_deallocate): Likewise.
	(__gthread_objc_condition_wait): Likewise.
	(__gthread_objc_condition_broadcast): Likewise.
	(__gthread_objc_condition_signal): Likewise.
	Include sys/time.h.
	(__gthr_win32_DWORD): New typedef.
	(__gthr_win32_HANDLE): Likewise.
	(__gthr_win32_CRITICAL_SECTION): Likewise.
	(__gthr_win32_CONDITION_VARIABLE): Likewise.
	(__gthread_t): Adjust.
	(__gthread_key_t): Likewise.
	(__gthread_mutex_t): Likewise.
	(__gthread_recursive_mutex_t): Likewise.
	(__gthread_cond_t): New typedef.
	(__gthread_time_t): Likewise.
	(__GTHREAD_MUTEX_INIT_DEFAULT): Delete.
	(__GTHREAD_RECURSIVE_MUTEX_INIT_DEFAULT): Likewise.
	(__GTHREAD_COND_INIT_FUNCTION): Define.
	(__GTHREAD_TIME_INIT): Likewise.
	(__gthr_i486_lock_cmp_xchg): Delete.
	(__gthr_win32_create): Declare.
	(__gthr_win32_join): Likewise.
	(__gthr_win32_self): Likewise.
	(__gthr_win32_detach): Likewise.
	(__gthr_win32_equal): Likewise.
	(__gthr_win32_yield): Likewise.
	(__gthr_win32_mutex_destroy): Likewise.
	(__gthr_win32_cond_init_function): Likewise if _GTHREADS_USE_COND is 1.
	(__gthr_win32_cond_broadcast): Likewise.
	(__gthr_win32_cond_signal): Likewise.
	(__gthr_win32_cond_wait): Likewise.
	(__gthr_win32_cond_timedwait): Likewise.
	(__gthr_win32_recursive_mutex_init_function): Delete.
	(__gthr_win32_recursive_mutex_lock): Likewise.
	(__gthr_win32_recursive_mutex_unlock): Likewise.
	(__gthr_win32_recursive_mutex_destroy): Likewise.
	(__gthread_create): New inline function.
	(__gthread_join): Likewise.
	(__gthread_self): Likewise.
	(__gthread_detach): Likewise.
	(__gthread_equal): Likewise.
	(__gthread_yield): Likewise.
	(__gthread_cond_init_function): Likewise if _GTHREADS_USE_COND is 1.
	(__gthread_cond_broadcast): Likewise.
	(__gthread_cond_signal): Likewise.
	(__gthread_cond_wait): Likewise.
	(__gthread_cond_timedwait): Likewise.
	(__GTHREAD_WIN32_INLINE): New macro.
	(__GTHREAD_WIN32_COND_INLINE): Likewise.
	(__GTHREAD_WIN32_ACTIVE_P): Likewise.
	Define WIN32_LEAN_AND_MEAN before including windows.h.
	(__gthread_once): Minor tweaks.
	(__gthread_key_create): Use ATTRIBUTE_UNUSED and TLS_OUT_OF_INDEXES.
	(__gthread_key_delete): Minor tweak.
	(__gthread_getspecific): Likewise.
	(__gthread_setspecific): Likewise.
	(__gthread_mutex_init_function): Reimplement.
	(__gthread_mutex_destroy): Likewise.
	(__gthread_mutex_lock): Likewise.
	(__gthread_mutex_trylock): Likewise.
	(__gthread_mutex_unlock): Likewise.
	(__gthr_win32_abs_to_rel_time): Declare.
	(__gthread_recursive_mutex_init_function): Reimplement.
	(__gthread_recursive_mutex_destroy): Likewise.
	(__gthread_recursive_mutex_lock): Likewise.
	(__gthread_recursive_mutex_trylock): Likewise.
	(__gthread_recursive_mutex_unlock): Likewise.
	(__gthread_cond_destroy): New inline function.
	(__gthread_cond_wait_recursive): Likewise.
	* config/i386/gthr-win32.c: Delete everything.
	Include gthr-win32.h to get the out-of-line version of inline routines.
	Add compile-time checks for the local version of the Win32 types.
	* config/i386/gthr-win32-cond.c: New file.
	* config/i386/gthr-win32-thread.c: Likewise.
	* config/i386/t-gthr-win32: Add config/i386/gthr-win32-thread.c to the
	EH part, config/i386/gthr-win32-cond.c and config/i386/gthr-win32.c to
	the static version of libgcc.
	* config/i386/t-mingw-pthread: Add config/i386/gthr-win32-thread.c to
	the EH part of libgcc.
	* config/i386/t-slibgcc-mingw: New file.
	* config/i386/libgcc-mingw.ver: Likewise.
libstdc++-v3/
	* acinclude.m4 (GLIBCXX_ENABLE_LIBSTDCXX_TIME): Set ac_has_sched_yield
	and ac_has_win32_sleep to yes for MinGW.  Change HAVE_WIN32_SLEEP into
	_GLIBCXX_USE_WIN32_SLEEP.
	(GLIBCXX_CHECK_GTHREADS): Add _WIN32_THREADS to compilation flags for
	Win32 threads and force _GTHREAD_USE_MUTEX_TIMEDLOCK to 0 for them.
	Add _GTHREADS_USE_COND to compilation flags if yes was configured and
	define it to 1 on success.
	* config.h.in: Regenerate.
	* configure: Likewise.
	* config/os/mingw32-w64/os_defines.h (_GLIBCXX_USE_GET_NPROCS_WIN32):
	Define to 1.
	* config/os/mingw32/os_defines.h (_GLIBCXX_THREAD_ATEXIT_WIN32): Likewise.
	(_GLIBCXX_USE_GET_NPROCS_WIN32): Likewise.
	* src/c++11/thread.cc (get_nprocs): Provide Win32 implementation if
	_GLIBCXX_USE_GET_NPROCS_WIN32 is defined.  Replace HAVE_WIN32_SLEEP
	with USE_WIN32_SLEEP.
	* testsuite/19_diagnostics/headers/system_error/errc_std_c++0x.cc: Add
	missing conditional compilation.
	* testsuite/lib/libstdc++.exp (check_v3_target_sleep): Add support for
	_GLIBCXX_USE_WIN32_SLEEP.
	(check_v3_target_nprocs): Likewise for _GLIBCXX_USE_GET_NPROCS_WIN32.
gcc/testsuite/
	* lib/target-supports.exp (check_effective_target_pthread): Add
	#include <pthread.h> directive to the test.

Comments

Jacek Caban June 28, 2019, 4:10 p.m. UTC | #1
Hi Eric,

On 6/28/19 3:42 PM, NightStrike wrote:
> FYI, Eric posted this today to the GCC patches list.  This may be of
> great interest to many who would prefer native threads instead of the
> winpthreads posix style interface.
>
> Great work, Eric!  I look forward to trying this out!
>
> ---------- Forwarded message ---------
> From: Eric Botcazou <ebotcazou@adacore.com>
> Date: Fri, Jun 28, 2019 at 6:51 AM
> Subject: [patch] Reimplement GNU threads library on native Windows
> To: <gcc-patches@gcc.gnu.org>
> Cc: <libstdc++@gcc.gnu.org>
>
>
> Hi,
>
> this reimplements the GNU threads library on native Windows (except for the
> Objective-C specific subset) using direct Win32 API calls, in lieu of the
> implementation based on semaphores.  This base implementations requires
> Windows XP/Server 2003, which is the default minimal setting of MinGW-W64.
> This also adds the support required for the C++11 threads, using again direct
> Win32 API calls; this additional layer requires Windows Vista/Server 2008 and
> is enabled only if _GTHREADS_USE_COND is defined to 1.
>
> This also changes libstdc++ to setting _GTHREADS_USE_COND to 1 when the switch
> --enable-libstdcxx-threads is passed, which means that C++11 threads are still
> disabled by default on native Windows and that you need to explicitly pass the
> switch to enable them.  The 30_threads chapter of the testsuite is clean.
>
> Tested on i686-pc-mingw32 and x86_64-pc-mingw32, OK for the mainline?


It's indeed great to see this. Thank you!


> +/* The implementation strategy for the c++0x thread support is as follows.
> +
> +   A GNU thread is represented by a Win32 HANDLE that is obtained when the
> +   Win32 thread is created, except of course for the initial thread.  This
> +   Win32 HANDLE is stored in a descriptor keyed from TLS memory for every
> +   thread, so the self routine can return it instead of having to duplicate
> +   the pseudo-handle returned by GetCurrentThread each time it is invoked.
> +   For the initial thread, this Win32 HANDLE is created during the first
> +   call to the self routine using the aforementioned technique.
> +
> +   Note that the equal routine compares the identifier of threads instead
> +   of their Win32 HANDLE, which will give the correct positive answer even
> +   in the case where distinct Win32 HANDLEs have been created for the same
> +   thread by multiple instances of libgcc included in the link.  */

Note that this will cause handle leaks if used across multiple libgcc instances, through.

> +#include "gthr-win32.h"
> +
> +/* The thread descriptor keyed from TLS memory.  */
> +struct __gthr_win32_thr_desc
> +{
> +  void *(*func) (void*);
> +  void *args;
> +  HANDLE h;
> +};
> +
> +/* The TLS key used by one instance of the library.  */
> +static __gthread_key_t __gthr_win32_tls = TLS_OUT_OF_INDEXES;
> +
> +/* The initialization device for the TLS key.  */
> +static __gthread_once_t __gthr_win32_tls_once = __GTHREAD_ONCE_INIT;
> +
> +/* Initialize the TLS key.  */
> +
> +static void
> +__gthr_win32_tls_init (void)
> +{
> +  if (__gthread_key_create (&__gthr_win32_tls, free))
> +    abort ();
> +}


You don't really need to store the whole __gthr_win32_thr_desc in TLS. If you stored just the handle, this wouldn't need a destructor.


Thanks,
Jacek
LIU Hao June 29, 2019, 2:56 a.m. UTC | #2
在 2019/6/29 上午12:10, Jacek Caban 写道:
> 
> You don't really need to store the whole __gthr_win32_thr_desc in TLS.
> If you stored just the handle, this wouldn't need a destructor.
> 
> 

The handle to be stored in the TLS ('the Handle' for short hereinafter)
should be a real handle, so there are a few scenarios that we should
consider:

0) the Handle should be closed upon the spawned thread's exit; in this
   case a destructor is still necessary to prevent handle leaks.
1) the Handle is closed by the creator via either `*_join()` or
   `*_detach()`; in the latter case the Handle becomes invalid while
   the thread is running, so `*_self()` would return an invalid handle.

It seems inappropriate to use handles as thread identifiers (as handles
imply resource ownership and are not unique identifiers); thread IDs (as
`DWORD` or `unsigned long`) would be a better alternative.
Jonathan Wakely June 29, 2019, 12:14 p.m. UTC | #3
On 28/06/19 12:46 +0200, Eric Botcazou wrote:
>+/* The implementation strategy for the c++0x thread support is as follows.

s/c++0x/c++11/ please, it hasn't been 0x for eight years now :-)

I haven't reviewed the rest yet, that just jumped out at me during a
quick skim of the patch.
Eric Botcazou July 2, 2019, 9:19 a.m. UTC | #4
> It seems inappropriate to use handles as thread identifiers (as handles
> imply resource ownership and are not unique identifiers); thread IDs (as
> `DWORD` or `unsigned long`) would be a better alternative.

This was considered but ultimately rejected, as you can do nothing with a 
thread Id, i.e. you need a handle for everything.  But the __gthread_equal 
routine does compare the Ids and not the handles.
Eric Botcazou July 2, 2019, 9:23 a.m. UTC | #5
> s/c++0x/c++11/ please, it hasn't been 0x for eight years now :-)

I suppose I didn't invent it though, so you'll probably find more with grep...
Jonathan Wakely July 2, 2019, 9:45 a.m. UTC | #6
On 02/07/19 11:23 +0200, Eric Botcazou wrote:
>> s/c++0x/c++11/ please, it hasn't been 0x for eight years now :-)
>
>I suppose I didn't invent it though, so you'll probably find more with grep...

Yes there are definitely still references to C++0x elsewhere in
libstdc++, especially in the testsuite, but let's not add new ones.

That was the only one I found in your patch.
Eric Botcazou July 2, 2019, 9:54 a.m. UTC | #7
> Yes there are definitely still references to C++0x elsewhere in
> libstdc++, especially in the testsuite, but let's not add new ones.

It's libgcc though, not libstdc++.  And it's a bit inconvenient to have c++0x 
on the one hand (gthr.h) and c++11 on the other hand (gthr-win32-thread.c); in 
other words, consistency matters too.
LIU Hao July 2, 2019, 9:57 a.m. UTC | #8
在 2019/7/2 下午5:19, Eric Botcazou 写道:
>> It seems inappropriate to use handles as thread identifiers (as handles
>> imply resource ownership and are not unique identifiers); thread IDs (as
>> `DWORD` or `unsigned long`) would be a better alternative.
> 
> This was considered but ultimately rejected, as you can do nothing with a 
> thread Id, i.e. you need a handle for everything.  But the __gthread_equal 
> routine does compare the Ids and not the handles.
> 

The `OpenThread()` function can obtain a handle by thread ID. It returns
a real handle that has to be closed when it is out of use. Using the
pseudo handle returned by `GetCurrentThread()` may be more efficient if
the target thread ID is equal to `GetCurrentThreadId()`.
Jacek Caban July 2, 2019, 10:12 a.m. UTC | #9
On 02/07/2019 11:57, Liu Hao wrote:

> 在 2019/7/2 下午5:19, Eric Botcazou 写道:
>>> It seems inappropriate to use handles as thread identifiers (as handles
>>> imply resource ownership and are not unique identifiers); thread IDs (as
>>> `DWORD` or `unsigned long`) would be a better alternative.
>> This was considered but ultimately rejected, as you can do nothing with a
>> thread Id, i.e. you need a handle for everything.  But the __gthread_equal
>> routine does compare the Ids and not the handles.
>>
> The `OpenThread()` function can obtain a handle by thread ID. It returns
> a real handle that has to be closed when it is out of use. Using the
> pseudo handle returned by `GetCurrentThread()` may be more efficient if
> the target thread ID is equal to `GetCurrentThreadId()`.


The problem with thread id is that it's not valid nor guaranteed to be 
identical after the thread is terminated. A handle needs to be used for 
that.


Jacek
Jacek Caban July 2, 2019, 10:15 a.m. UTC | #10
On 02/07/2019 12:12, Jacek Caban wrote:
> On 02/07/2019 11:57, Liu Hao wrote:
>
>> 在 2019/7/2 下午5:19, Eric Botcazou 写道:
>>>> It seems inappropriate to use handles as thread identifiers (as 
>>>> handles
>>>> imply resource ownership and are not unique identifiers); thread 
>>>> IDs (as
>>>> `DWORD` or `unsigned long`) would be a better alternative.
>>> This was considered but ultimately rejected, as you can do nothing 
>>> with a
>>> thread Id, i.e. you need a handle for everything.  But the 
>>> __gthread_equal
>>> routine does compare the Ids and not the handles.
>>>
>> The `OpenThread()` function can obtain a handle by thread ID. It returns
>> a real handle that has to be closed when it is out of use. Using the
>> pseudo handle returned by `GetCurrentThread()` may be more efficient if
>> the target thread ID is equal to `GetCurrentThreadId()`.
>
>
> The problem with thread id is that it's not valid nor guaranteed to be 
> identical after the thread is terminated. A handle needs to be used 
> for that.


I meant unique, not identical.


Jacek
Jonathan Wakely July 2, 2019, 11:56 a.m. UTC | #11
On 02/07/19 11:54 +0200, Eric Botcazou wrote:
>> Yes there are definitely still references to C++0x elsewhere in
>> libstdc++, especially in the testsuite, but let's not add new ones.
>
>It's libgcc though, not libstdc++.  And it's a bit inconvenient to have c++0x
>on the one hand (gthr.h) and c++11 on the other hand (gthr-win32-thread.c); in
>other words, consistency matters too.

Then I think we go with the attached patch to rename everything. This
could break out-of-tree ports that define __GTHREADS_CXX0X, if there
are any. We could consider making gthr.h do this after including
gthr-default.h:

#if defined __GTHREADS_CXX0X && ! defined __GTHREADS_CXX11
# warning "Define __GTHREADS_CXX11 instead of __GTHREADS_CXX0X"
# define __GTHREADS_CXX11 1
#endif
Jonathan Wakely July 2, 2019, noon UTC | #12
On 02/07/19 12:15 +0200, Jacek Caban wrote:
>
>On 02/07/2019 12:12, Jacek Caban wrote:
>>On 02/07/2019 11:57, Liu Hao wrote:
>>
>>>在 2019/7/2 下午5:19, Eric Botcazou 写道:
>>>>>It seems inappropriate to use handles as thread identifiers 
>>>>>(as handles
>>>>>imply resource ownership and are not unique identifiers); 
>>>>>thread IDs (as
>>>>>`DWORD` or `unsigned long`) would be a better alternative.
>>>>This was considered but ultimately rejected, as you can do 
>>>>nothing with a
>>>>thread Id, i.e. you need a handle for everything.  But the 
>>>>__gthread_equal
>>>>routine does compare the Ids and not the handles.
>>>>
>>>The `OpenThread()` function can obtain a handle by thread ID. It returns
>>>a real handle that has to be closed when it is out of use. Using the
>>>pseudo handle returned by `GetCurrentThread()` may be more efficient if
>>>the target thread ID is equal to `GetCurrentThreadId()`.
>>
>>
>>The problem with thread id is that it's not valid nor guaranteed to 
>>be identical after the thread is terminated. A handle needs to be 
>>used for that.
>
>
>I meant unique, not identical.

The C++ standard says:

"The library may reuse the value of a thread::id of a terminated
thread that can no longer be joined."

So that's not a reason to use a handle.
Jonathan Wakely July 2, 2019, 12:02 p.m. UTC | #13
On 02/07/19 12:56 +0100, Jonathan Wakely wrote:
>On 02/07/19 11:54 +0200, Eric Botcazou wrote:
>>>Yes there are definitely still references to C++0x elsewhere in
>>>libstdc++, especially in the testsuite, but let's not add new ones.
>>
>>It's libgcc though, not libstdc++.  And it's a bit inconvenient to have c++0x
>>on the one hand (gthr.h) and c++11 on the other hand (gthr-win32-thread.c); in
>>other words, consistency matters too.
>
>Then I think we go with the attached patch to rename everything. This
>could break out-of-tree ports that define __GTHREADS_CXX0X, if there
>are any. We could consider making gthr.h do this after including
>gthr-default.h:
>
>#if defined __GTHREADS_CXX0X && ! defined __GTHREADS_CXX11
># warning "Define __GTHREADS_CXX11 instead of __GTHREADS_CXX0X"
># define __GTHREADS_CXX11 1
>#endif

And with the patch this time ...
LIU Hao July 2, 2019, 12:14 p.m. UTC | #14
在 2019/7/2 下午8:00, Jonathan Wakely 写道:
> The C++ standard says:
> 
> "The library may reuse the value of a thread::id of a terminated
> thread that can no longer be joined."
> 
> So that's not a reason to use a handle.

According to MSDN [1] a thread ID is valid 'until the thread has been
terminated' so I presume a terminated but unclosed thread does not have
a thread ID.

This could also mean that there is no effect way to denote a thread
uniquely. As a consequence libstdc++ may have to its own bookkeeping
mechanism.


[1]
https://docs.microsoft.com/en-us/windows/desktop/ProcThread/thread-handles-and-identifiers
Jonathan Wakely July 2, 2019, 12:27 p.m. UTC | #15
On 02/07/19 20:14 +0800, Liu Hao wrote:
>在 2019/7/2 下午8:00, Jonathan Wakely 写道:
>> The C++ standard says:
>>
>> "The library may reuse the value of a thread::id of a terminated
>> thread that can no longer be joined."
>>
>> So that's not a reason to use a handle.
>
>According to MSDN [1] a thread ID is valid 'until the thread has been
>terminated' so I presume a terminated but unclosed thread does not have
>a thread ID.

What do you mean by "unclosed thread"? If I read it correctly, the MSDN page
refers to closing a handle (which makes sense), not closing a thread.

>This could also mean that there is no effect way to denote a thread
>uniquely. As a consequence libstdc++ may have to its own bookkeeping
>mechanism.

As I said in my last mail, libstdc++ does not need a way to denote a
thread uniquely.

I'm not objecting to Eric's use of a HANDLE for a thread::id, as
his justification makes sense. I'm just saying we don't need a unique
ID that outlives the thread, because that's not a requirement.

>
>[1]
>https://docs.microsoft.com/en-us/windows/desktop/ProcThread/thread-handles-and-identifiers
>
>-- 
>Best regards,
>LH_Mouse
>
LIU Hao July 3, 2019, 12:55 p.m. UTC | #16
在 2019/7/2 下午8:27, Jonathan Wakely 写道:
> 
> What do you mean by "unclosed thread"? If I read it correctly, the MSDN
> page
> refers to closing a handle (which makes sense), not closing a thread.
> 

Yes, it meant a thread which has terminated but not deleted due to some
handles left open.


>> This could also mean that there is no effect way to denote a thread
>> uniquely. As a consequence libstdc++ may have to its own bookkeeping
>> mechanism.
> 
> As I said in my last mail, libstdc++ does not need a way to denote a
> thread uniquely.
> 

At my last glance at the `__gthread_` interfaces, libstdc++ requires
thread IDs to be LessThanComparable, which would require retrieval of
thread IDs by handle, as in `__gthread_equal()`.

More than that, if my previous vision was correct (a terminated thread
has no ID associated) then `GetThreadId()` on a thread that has
terminated would not return a valid thread ID. Fortunately, this seems
not the case:

```c
#include <windows.h>
#include <stdio.h>

DWORD __stdcall ThreadProc(void* pParam)
  {
    printf("thread %lu running\n", GetCurrentThreadId());
    return 0;
  }

int main(void)
  {
    HANDLE hThread = CreateThread(0, 0, ThreadProc, 0, CREATE_SUSPENDED, 0);
    printf("thread %lu created\n", GetThreadId(hThread));

    ResumeThread(hThread);
    WaitForSingleObject(hThread, INFINITE);
    printf("thread %lu terminated\n", GetThreadId(hThread));

    CloseHandle(hThread);
    // `hThread` is now invalid; DO NOT PLAY WITH THIS AT HOME!
    printf("thread %lu closed\n", GetThreadId(hThread));
  }
```

This program outputs

```text
E:\Desktop>gcc test.c -Wall -Wextra -Wpedantic && a.exe
test.c: In function 'ThreadProc':
test.c:4:34: warning: unused parameter 'pParam' [-Wunused-parameter]
    4 | DWORD __stdcall ThreadProc(void* pParam)
      |                            ~~~~~~^~~~~~
thread 9172 created
thread 9172 running
thread 9172 terminated
thread 0 closed

E:\Desktop>
```

Despite Microsoft's documentation, the identifier of a thread seems
uncollected as long as there are still handles to the thread. So it
might be safe to assume that the identifier of an `std::thread` *cannot*
be reused before it is `join()`'d or `detach()`'d which closes the
handle stored in the `std::thread` object.
Jonathan Yong July 29, 2019, 2:43 p.m. UTC | #17
On 7/3/19 12:55 PM, Liu Hao wrote:
> 在 2019/7/2 下午8:27, Jonathan Wakely 写道:
>>
>> What do you mean by "unclosed thread"? If I read it correctly, the MSDN
>> page
>> refers to closing a handle (which makes sense), not closing a thread.
>>
> 
> Yes, it meant a thread which has terminated but not deleted due to some
> handles left open.
> 
> 
>>> This could also mean that there is no effect way to denote a thread
>>> uniquely. As a consequence libstdc++ may have to its own bookkeeping
>>> mechanism.
>>
>> As I said in my last mail, libstdc++ does not need a way to denote a
>> thread uniquely.
>>
> 
> At my last glance at the `__gthread_` interfaces, libstdc++ requires
> thread IDs to be LessThanComparable, which would require retrieval of
> thread IDs by handle, as in `__gthread_equal()`.
> 
> More than that, if my previous vision was correct (a terminated thread
> has no ID associated) then `GetThreadId()` on a thread that has
> terminated would not return a valid thread ID. Fortunately, this seems
> not the case:
> 
> ```c
> #include <windows.h>
> #include <stdio.h>
> 
> DWORD __stdcall ThreadProc(void* pParam)
>   {
>     printf("thread %lu running\n", GetCurrentThreadId());
>     return 0;
>   }
> 
> int main(void)
>   {
>     HANDLE hThread = CreateThread(0, 0, ThreadProc, 0, CREATE_SUSPENDED, 0);
>     printf("thread %lu created\n", GetThreadId(hThread));
> 
>     ResumeThread(hThread);
>     WaitForSingleObject(hThread, INFINITE);
>     printf("thread %lu terminated\n", GetThreadId(hThread));
> 
>     CloseHandle(hThread);
>     // `hThread` is now invalid; DO NOT PLAY WITH THIS AT HOME!
>     printf("thread %lu closed\n", GetThreadId(hThread));
>   }
> ```
> 
> This program outputs
> 
> ```text
> E:\Desktop>gcc test.c -Wall -Wextra -Wpedantic && a.exe
> test.c: In function 'ThreadProc':
> test.c:4:34: warning: unused parameter 'pParam' [-Wunused-parameter]
>     4 | DWORD __stdcall ThreadProc(void* pParam)
>       |                            ~~~~~~^~~~~~
> thread 9172 created
> thread 9172 running
> thread 9172 terminated
> thread 0 closed
> 
> E:\Desktop>
> ```
> 
> Despite Microsoft's documentation, the identifier of a thread seems
> uncollected as long as there are still handles to the thread. So it
> might be safe to assume that the identifier of an `std::thread` *cannot*
> be reused before it is `join()`'d or `detach()`'d which closes the
> handle stored in the `std::thread` object.
> 
> 

Any updates?
LIU Hao July 29, 2019, 3:05 p.m. UTC | #18
在 2019/7/29 22:43, JonY 写道:
> 
> Any updates?
> 

No. I am still under the impression that using thread handles as
`std::thread::id`s:

0) complexifies comparison of thread IDs without obvious benefits, and
1) does not work reliably because handles can be duplicated, and
2) makes `__gthread_self()` return invalid handles in detached threads.
Eric Botcazou July 30, 2019, 8:50 a.m. UTC | #19
> 0) complexifies comparison of thread IDs without obvious benefits, and

The reverse argument is also true: using IDs would complexify everything else 
with the only benefit of simplifying the equal primitive.

> 1) does not work reliably because handles can be duplicated, and

That's pure FUD.

> 2) makes `__gthread_self()` return invalid handles in detached threads.

Admittedly, but this can be fixed if this is deemed necessary by clearing the 
thread descriptor when detaching the thread.
diff mbox series

Patch

Index: libgcc/config/i386/gthr-win32-cond.c
===================================================================
--- libgcc/config/i386/gthr-win32-cond.c	(nonexistent)
+++ libgcc/config/i386/gthr-win32-cond.c	(working copy)
@@ -0,0 +1,85 @@ 
+/* Implementation of threads compatibility routines for libgcc2.  */
+
+/* Copyright (C) 1999-2019 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC 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 General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/* This module is separate from the rest of the implementation because it
+   references symbols in system libraries that are only available on Vista
+   and Server 2008 or later versions.  */
+
+/* Get the out-of-line version of the inline routines.  */
+
+#define _GTHREADS_USE_COND 1
+#define __GTHREAD_WIN32_COND_INLINE
+
+#define __gthread_cond_init_function __gthr_win32_cond_init_function
+#define __gthread_cond_broadcast __gthr_win32_cond_broadcast
+#define __gthread_cond_signal __gthr_win32_cond_signal
+#define __gthread_cond_wait __gthr_win32_cond_wait
+#define __gthread_cond_timedwait __gthr_win32_cond_timedwait
+
+#include "gthr-win32.h"
+
+/* The number of 100-nanoseconds between 1/1/1601 and 1/1/1970. */
+#define FILETIME_1970 116444736000000000ULL
+
+/* The number of 100-nanoseconds per second.  */
+#define NSEC100_PER_SEC (1000000000ULL / 100)
+
+/* The number of 100-nanoseconds per millisecond.  */
+#define NSEC100_PER_MSEC (NSEC100_PER_SEC / 1000)
+
+/* The ceiling division of X by Y.  */
+#define CEIL_DIV(X, Y) (((X) + (Y) - 1) / (Y))
+
+/* Convert absolute thread time to relative time in millisecond.  */
+
+DWORD
+__gthr_win32_abs_to_rel_time (const __gthread_time_t *abs_time)
+{
+  union {
+    ULONGLONG nsec100;
+    FILETIME ft;
+  } now;
+  ULONGLONG abs_time_nsec100;
+
+  /* The Windows epoch is 1/1/1601 while the Unix epoch is 1/1/1970.  */
+  GetSystemTimeAsFileTime (&now.ft);
+  now.nsec100 -= FILETIME_1970;
+
+  abs_time_nsec100
+    = (ULONGLONG) abs_time->tv_sec * NSEC100_PER_SEC
+        + CEIL_DIV (abs_time->tv_nsec, 100);
+
+  if (abs_time_nsec100 < now.nsec100)
+    return 0;
+
+  return (DWORD) CEIL_DIV (abs_time_nsec100 - now.nsec100, NSEC100_PER_SEC);
+}
+
+/* Check the sizes of the local version of the Win32 types.  */
+
+#define CHECK_SIZE_OF(TYPE) \
+  typedef int assertion[sizeof(__gthr_win32_##TYPE) == sizeof(TYPE) ? 1 : -1];
+
+CHECK_SIZE_OF (CONDITION_VARIABLE)
Index: libgcc/config/i386/gthr-win32-thread.c
===================================================================
--- libgcc/config/i386/gthr-win32-thread.c	(nonexistent)
+++ libgcc/config/i386/gthr-win32-thread.c	(working copy)
@@ -0,0 +1,162 @@ 
+/* Implementation of threads compatibility routines for libgcc2.  */
+
+/* Copyright (C) 1999-2019 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC 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 General Public License
+for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/* This module is separate from the rest of the implementation because only
+   one copy of it ought to be linked.  */
+
+/* The implementation strategy for the c++0x thread support is as follows.
+
+   A GNU thread is represented by a Win32 HANDLE that is obtained when the
+   Win32 thread is created, except of course for the initial thread.  This
+   Win32 HANDLE is stored in a descriptor keyed from TLS memory for every
+   thread, so the self routine can return it instead of having to duplicate
+   the pseudo-handle returned by GetCurrentThread each time it is invoked.
+   For the initial thread, this Win32 HANDLE is created during the first
+   call to the self routine using the aforementioned technique.
+
+   Note that the equal routine compares the identifier of threads instead
+   of their Win32 HANDLE, which will give the correct positive answer even
+   in the case where distinct Win32 HANDLEs have been created for the same
+   thread by multiple instances of libgcc included in the link.  */
+
+#include "gthr-win32.h"
+
+/* The thread descriptor keyed from TLS memory.  */
+struct __gthr_win32_thr_desc
+{
+  void *(*func) (void*);
+  void *args;
+  HANDLE h;
+};
+
+/* The TLS key used by one instance of the library.  */
+static __gthread_key_t __gthr_win32_tls = TLS_OUT_OF_INDEXES;
+
+/* The initialization device for the TLS key.  */
+static __gthread_once_t __gthr_win32_tls_once = __GTHREAD_ONCE_INIT;
+
+/* Initialize the TLS key.  */
+
+static void
+__gthr_win32_tls_init (void)
+{
+  if (__gthread_key_create (&__gthr_win32_tls, free))
+    abort ();
+}
+
+/* Wrapper routine around thread functions.  */
+
+static DWORD
+__gthr_win32_thread_wrapper (void *args)
+{
+  struct __gthr_win32_thr_desc *td = (struct __gthr_win32_thr_desc *) args;
+
+  __gthread_setspecific (__gthr_win32_tls, td);
+
+  DWORD exit_code = (DWORD) (ULONG_PTR) (*td->func) (td->args);
+
+  ExitThread (exit_code);
+  return exit_code;
+}
+
+/* Implement the __gthread_create routine.  */
+
+int
+__gthr_win32_create (__gthread_t *thr, void *(*func) (void*), void *args)
+{
+  struct __gthr_win32_thr_desc *td;
+
+  __gthread_once (&__gthr_win32_tls_once, __gthr_win32_tls_init);
+
+  td = malloc (sizeof (struct __gthr_win32_thr_desc));
+  td->func = func;
+  td->args = args;
+  td->h = CreateThread (NULL, 0,
+			(LPTHREAD_START_ROUTINE) __gthr_win32_thread_wrapper,
+			(LPVOID) td, CREATE_SUSPENDED, NULL);
+  if (td->h)
+    {
+      ResumeThread (td->h);
+      *thr = (__gthread_t) td->h;
+      return 0;
+    }
+  else
+    {
+      free (td);
+      return (int) GetLastError ();
+    }
+}
+
+/* Implement the __gthread_join routine.  */
+
+int
+__gthr_win32_join (__gthread_t thr, void **value_ptr)
+{
+  int status = 0;
+
+  if (GetThreadId ((HANDLE) thr) == GetCurrentThreadId ())
+    return 1;
+
+  if (WaitForSingleObject ((HANDLE) thr, INFINITE) == WAIT_OBJECT_0)
+    {
+      if (value_ptr)
+	{
+	  DWORD exit_code;
+	  if (GetExitCodeThread ((HANDLE) thr, &exit_code))
+	    *value_ptr = (void *) (ULONG_PTR) exit_code;
+	  else
+	    status = (int) GetLastError ();
+	}
+    }
+  else
+    status = (int) GetLastError ();
+
+  CloseHandle ((HANDLE) thr);
+  return status;
+}
+
+/* Implement the __gthread_self routine.  */
+
+__gthread_t
+__gthr_win32_self (void)
+{
+  struct __gthr_win32_thr_desc *td;
+
+  __gthread_once (&__gthr_win32_tls_once, __gthr_win32_tls_init);
+
+  if (!(td = __gthread_getspecific (__gthr_win32_tls)))
+    {
+      HANDLE proc = GetCurrentProcess ();
+      td = malloc (sizeof (struct __gthr_win32_thr_desc));
+      td->func = NULL;
+      td->args = NULL;
+      if (!DuplicateHandle (proc, GetCurrentThread(), proc, &td->h, 0, FALSE,
+			    DUPLICATE_SAME_ACCESS))
+	abort ();
+      __gthread_setspecific (__gthr_win32_tls, td);
+    }
+
+  return td->h;
+}
Index: libgcc/config/i386/gthr-win32.c
===================================================================
--- libgcc/config/i386/gthr-win32.c	(revision 272633)
+++ libgcc/config/i386/gthr-win32.c	(working copy)
@@ -1,10 +1,6 @@ 
-/* Implementation of W32-specific threads compatibility routines for
-   libgcc2.  */
+/* Implementation of threads compatibility routines for libgcc2.  */
 
 /* Copyright (C) 1999-2019 Free Software Foundation, Inc.
-   Contributed by Mumit Khan <khan@xraylith.wisc.edu>.
-   Modified and moved to separate file by Danny Smith
-   <dannysmith@users.sourceforge.net>.
 
 This file is part of GCC.
 
@@ -27,241 +23,33 @@  a copy of the GCC Runtime Library Except
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#include <windows.h>
-#ifndef __GTHREAD_HIDE_WIN32API
-# define __GTHREAD_HIDE_WIN32API 1
-#endif
-#undef  __GTHREAD_I486_INLINE_LOCK_PRIMITIVES
-#define __GTHREAD_I486_INLINE_LOCK_PRIMITIVES
+/* Get the out-of-line version of the inline routines.  */
+
+#define __GTHREAD_WIN32_ACTIVE_P() 1
+#define __GTHREAD_WIN32_INLINE
+
+#define __gthread_detach __gthr_win32_detach
+#define __gthread_equal __gthr_win32_equal
+#define __gthread_yield __gthr_win32_yield
+#define __gthread_once __gthr_win32_once
+#define __gthread_key_create __gthr_win32_key_create
+#define __gthread_key_delete __gthr_win32_key_delete
+#define __gthread_getspecific __gthr_win32_getspecific
+#define __gthread_setspecific __gthr_win32_setspecific
+#define __gthread_mutex_init_function __gthr_win32_mutex_init_function
+#define __gthread_mutex_destroy __gthr_win32_mutex_destroy
+#define __gthread_mutex_lock __gthr_win32_mutex_lock
+#define __gthread_mutex_trylock __gthr_win32_mutex_trylock
+#define __gthread_mutex_unlock __gthr_win32_mutex_unlock
+#define __gthread_recursive_mutex_trylock __gthr_win32_recursive_mutex_trylock
+
 #include "gthr-win32.h"
 
-/* Windows32 threads specific definitions. The windows32 threading model
-   does not map well into pthread-inspired gcc's threading model, and so 
-   there are caveats one needs to be aware of.
-
-   1. The destructor supplied to __gthread_key_create is ignored for
-      generic x86-win32 ports. This will certainly cause memory leaks 
-      due to unreclaimed eh contexts (sizeof (eh_context) is at least 
-      24 bytes for x86 currently).
-
-      This memory leak may be significant for long-running applications
-      that make heavy use of C++ EH.
-
-      However, Mingw runtime (version 0.3 or newer) provides a mechanism
-      to emulate pthreads key dtors; the runtime provides a special DLL,
-      linked in if -mthreads option is specified, that runs the dtors in
-      the reverse order of registration when each thread exits. If
-      -mthreads option is not given, a stub is linked in instead of the
-      DLL, which results in memory leak. Other x86-win32 ports can use 
-      the same technique of course to avoid the leak.
-
-   2. The error codes returned are non-POSIX like, and cast into ints.
-      This may cause incorrect error return due to truncation values on 
-      hw where sizeof (DWORD) > sizeof (int).
-   
-   3. We are currently using a special mutex instead of the Critical
-      Sections, since Win9x does not support TryEnterCriticalSection
-      (while NT does).
-  
-   The basic framework should work well enough. In the long term, GCC
-   needs to use Structured Exception Handling on Windows32.  */
-
-int
-__gthr_win32_once (__gthread_once_t *once, void (*func) (void))
-{
-  if (once == NULL || func == NULL)
-    return EINVAL;
-
-  if (! once->done)
-    {
-      if (InterlockedIncrement (&(once->started)) == 0)
-        {
-	  (*func) ();
-	  once->done = TRUE;
-	}
-      else
-	{
-	  /* Another thread is currently executing the code, so wait for it 
-	     to finish; yield the CPU in the meantime.  If performance 
-	     does become an issue, the solution is to use an Event that 
-	     we wait on here (and set above), but that implies a place to 
-	     create the event before this routine is called.  */ 
-	  while (! once->done)
-	    Sleep (0);
-	}
-    }
-  return 0;
-}
-
-/* Windows32 thread local keys don't support destructors; this leads to
-   leaks, especially in threaded applications making extensive use of 
-   C++ EH. Mingw uses a thread-support DLL to work-around this problem.  */
-
-int
-__gthr_win32_key_create (__gthread_key_t *key,
-			 void (*dtor) (void *) __attribute__((unused)))
-{
-  int status = 0;
-  DWORD tls_index = TlsAlloc ();
-  if (tls_index != 0xFFFFFFFF)
-    {
-      *key = tls_index;
-#ifdef MINGW32_SUPPORTS_MT_EH
-      /* Mingw runtime will run the dtors in reverse order for each thread
-         when the thread exits.  */
-      status = __mingwthr_key_dtor (*key, dtor);
-#endif
-    }
-  else
-    status = (int) GetLastError ();
-  return status;
-}
-
-int
-__gthr_win32_key_delete (__gthread_key_t key)
-{
-  return (TlsFree (key) != 0) ? 0 : (int) GetLastError ();
-}
-
-void *
-__gthr_win32_getspecific (__gthread_key_t key)
-{
-  DWORD lasterror;
-  void *ptr;
-  lasterror = GetLastError();
-  ptr = TlsGetValue(key);
-  SetLastError( lasterror );
-  return ptr;
-}
-
-int
-__gthr_win32_setspecific (__gthread_key_t key, const void *ptr)
-{
-  if (TlsSetValue (key, CONST_CAST2(void *, const void *, ptr)) != 0)
-    return 0;
-  else
-    return GetLastError ();
-}
-
-void
-__gthr_win32_mutex_init_function (__gthread_mutex_t *mutex)
-{
-  mutex->counter = -1;
-  mutex->sema = CreateSemaphoreW (NULL, 0, 65535, NULL);
-}
-
-void
-__gthr_win32_mutex_destroy (__gthread_mutex_t *mutex)
-{
-  CloseHandle ((HANDLE) mutex->sema);
-}
-
-int
-__gthr_win32_mutex_lock (__gthread_mutex_t *mutex)
-{
-  if (InterlockedIncrement (&mutex->counter) == 0 ||
-      WaitForSingleObject (mutex->sema, INFINITE) == WAIT_OBJECT_0)
-    return 0;
-  else
-    {
-      /* WaitForSingleObject returns WAIT_FAILED, and we can only do
-         some best-effort cleanup here.  */
-      InterlockedDecrement (&mutex->counter);
-      return 1;
-    }
-}
-
-int
-__gthr_win32_mutex_trylock (__gthread_mutex_t *mutex)
-{
-  if (__GTHR_W32_InterlockedCompareExchange (&mutex->counter, 0, -1) < 0)
-    return 0;
-  else
-    return 1;
-}
-
-int
-__gthr_win32_mutex_unlock (__gthread_mutex_t *mutex)
-{
-  if (InterlockedDecrement (&mutex->counter) >= 0)
-    return ReleaseSemaphore (mutex->sema, 1, NULL) ? 0 : 1;
-  else
-    return 0;
-}
-
-void
-__gthr_win32_recursive_mutex_init_function (__gthread_recursive_mutex_t *mutex)
-{
-  mutex->counter = -1;
-  mutex->depth = 0;
-  mutex->owner = 0;
-  mutex->sema = CreateSemaphoreW (NULL, 0, 65535, NULL);
-}
-
-int
-__gthr_win32_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
-{
-  DWORD me = GetCurrentThreadId();
-  if (InterlockedIncrement (&mutex->counter) == 0)
-    {
-      mutex->depth = 1;
-      mutex->owner = me;
-    }
-  else if (mutex->owner == me)
-    {
-      InterlockedDecrement (&mutex->counter);
-      ++(mutex->depth);
-    }
-  else if (WaitForSingleObject (mutex->sema, INFINITE) == WAIT_OBJECT_0)
-    {
-      mutex->depth = 1;
-      mutex->owner = me;
-    }
-  else
-    {
-      /* WaitForSingleObject returns WAIT_FAILED, and we can only do
-         some best-effort cleanup here.  */
-      InterlockedDecrement (&mutex->counter);
-      return 1;
-    }
-  return 0;
-}
-
-int
-__gthr_win32_recursive_mutex_trylock (__gthread_recursive_mutex_t *mutex)
-{
-  DWORD me = GetCurrentThreadId();
-  if (__GTHR_W32_InterlockedCompareExchange (&mutex->counter, 0, -1) < 0)
-    {
-      mutex->depth = 1;
-      mutex->owner = me;
-    }
-  else if (mutex->owner == me)
-    ++(mutex->depth);
-  else
-    return 1;
-
-  return 0;
-}
-
-int
-__gthr_win32_recursive_mutex_unlock (__gthread_recursive_mutex_t *mutex)
-{
-  --(mutex->depth);
-  if (mutex->depth == 0)
-    {
-      mutex->owner = 0;
-
-      if (InterlockedDecrement (&mutex->counter) >= 0)
-	return ReleaseSemaphore (mutex->sema, 1, NULL) ? 0 : 1;
-    }
-
-  return 0;
-}
-
-int
-__gthr_win32_recursive_mutex_destroy (__gthread_recursive_mutex_t *mutex)
-{
-  CloseHandle ((HANDLE) mutex->sema);
-  return 0;
-}
+/* Check the sizes of the local version of the Win32 types.  */
+
+#define CHECK_SIZE_OF(TYPE) \
+  typedef int assertion[sizeof(__gthr_win32_##TYPE) == sizeof(TYPE) ? 1 : -1];
+
+CHECK_SIZE_OF (DWORD)
+CHECK_SIZE_OF (HANDLE)
+CHECK_SIZE_OF (CRITICAL_SECTION)
Index: libgcc/config/i386/gthr-win32.h
===================================================================
--- libgcc/config/i386/gthr-win32.h	(revision 272633)
+++ libgcc/config/i386/gthr-win32.h	(working copy)
@@ -28,18 +28,12 @@  see the files COPYING3 and COPYING.RUNTI
 #ifndef GCC_GTHR_WIN32_H
 #define GCC_GTHR_WIN32_H
 
-/* Make sure CONST_CAST2 (origin in system.h) is declared.  */
-#ifndef CONST_CAST2
-#define CONST_CAST2(TOTYPE,FROMTYPE,X) ((__extension__(union {FROMTYPE _q; TOTYPE _nq;})(X))._nq)
-#endif
-
-/* Windows32 threads specific definitions. The windows32 threading model
-   does not map well into pthread-inspired gcc's threading model, and so
-   there are caveats one needs to be aware of.
+/* The Windows threading model does not map well into the POSIX inspired
+   GCC threading model, so there are caveats one needs to be aware of.
 
    1. The destructor supplied to __gthread_key_create is ignored for
-      generic x86-win32 ports. This will certainly cause memory leaks
-      due to unreclaimed eh contexts (sizeof (eh_context) is at least
+      generic Windows ports.  This will certainly cause memory leaks
+      due to unreclaimed EH contexts (sizeof (eh_context) is at least
       24 bytes for x86 currently).
 
       This memory leak may be significant for long-running applications
@@ -50,29 +44,46 @@  see the files COPYING3 and COPYING.RUNTI
       linked in if -mthreads option is specified, that runs the dtors in
       the reverse order of registration when each thread exits. If
       -mthreads option is not given, a stub is linked in instead of the
-      DLL, which results in memory leak. Other x86-win32 ports can use
+      DLL, which results in memory leak.  Other Windows ports can use
       the same technique of course to avoid the leak.
 
    2. The error codes returned are non-POSIX like, and cast into ints.
       This may cause incorrect error return due to truncation values on
       hw where sizeof (DWORD) > sizeof (int).
 
-   3. We are currently using a special mutex instead of the Critical
-      Sections, since Win9x does not support TryEnterCriticalSection
-      (while NT does).
+   3. POSIX-like condition variables are supported, but only on Vista and
+      Server 2008 or later versions, so they must be explicitly enabled
+      by defining the preprocessor symbol _GTHREADS_USE_COND to 1.
 
-   The basic framework should work well enough. In the long term, GCC
-   needs to use Structured Exception Handling on Windows32.  */
+   4. Timed lock primitives are not supported.  */
 
 #define __GTHREADS 1
 
-#include <errno.h>
-#ifdef __MINGW32__
-#include <_mingw.h>
+#if _GTHREADS_USE_COND
+#define __GTHREAD_HAS_COND 1
+#define __GTHREADS_CXX0X 1
+/* Condition variables are supported on Vista and Server 2008 or later.  */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
+#if _GTHREAD_USE_MUTEX_TIMEDLOCK
+#error Timed lock primitives are not supported on Windows targets
 #endif
 
-#ifndef __UNUSED_PARAM
-#define __UNUSED_PARAM(x) x
+#include <stdlib.h>
+
+/* Make sure CONST_CAST2 (origin in system.h) is declared.  */
+#ifndef CONST_CAST2
+#ifdef __cplusplus
+#define CONST_CAST2(TOTYPE,FROMTYPE,X) (const_cast<TOTYPE> (X))
+#else
+#define CONST_CAST2(TOTYPE,FROMTYPE,X) ((__extension__(union {FROMTYPE _q; TOTYPE _nq;})(X))._nq)
+#endif
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
 #endif
 
 #ifdef _LIBOBJC
@@ -82,12 +93,13 @@  see the files COPYING3 and COPYING.RUNTI
 #ifndef __OBJC__
 #define __OBJC__
 #endif
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 /* Now undef the windows BOOL.  */
 #undef BOOL
 
 /* Key structure for maintaining thread specific storage */
-static DWORD	__gthread_objc_data_tls = (DWORD) -1;
+static DWORD __gthread_objc_data_tls = TLS_OUT_OF_INDEXES;
 
 /* Backend initialization functions */
 
@@ -96,7 +108,7 @@  int
 __gthread_objc_init_thread_system (void)
 {
   /* Initialize the thread storage key.  */
-  if ((__gthread_objc_data_tls = TlsAlloc ()) != (DWORD) -1)
+  if ((__gthread_objc_data_tls = TlsAlloc ()) != TLS_OUT_OF_INDEXES)
     return 0;
   else
     return -1;
@@ -106,7 +118,7 @@  __gthread_objc_init_thread_system (void)
 int
 __gthread_objc_close_thread_system (void)
 {
-  if (__gthread_objc_data_tls != (DWORD) -1)
+  if (__gthread_objc_data_tls != TLS_OUT_OF_INDEXES)
     TlsFree (__gthread_objc_data_tls);
   return 0;
 }
@@ -222,15 +234,9 @@  __gthread_objc_thread_set_data (void *va
 void *
 __gthread_objc_thread_get_data (void)
 {
-  DWORD lasterror;
-  void *ptr;
-
-  lasterror = GetLastError ();
-
-  ptr = TlsGetValue (__gthread_objc_data_tls);          /* Return thread data.  */
-
+  DWORD lasterror = GetLastError ();
+  void * ptr = TlsGetValue (__gthread_objc_data_tls);
   SetLastError (lasterror);
-
   return ptr;
 }
 
@@ -294,7 +300,7 @@  __gthread_objc_mutex_unlock (objc_mutex_
 
 /* Allocate a condition.  */
 int
-__gthread_objc_condition_allocate (objc_condition_t __UNUSED_PARAM(condition))
+__gthread_objc_condition_allocate (objc_condition_t condition ATTRIBUTE_UNUSED)
 {
   /* Unimplemented.  */
   return -1;
@@ -302,7 +308,7 @@  __gthread_objc_condition_allocate (objc_
 
 /* Deallocate a condition.  */
 int
-__gthread_objc_condition_deallocate (objc_condition_t __UNUSED_PARAM(condition))
+__gthread_objc_condition_deallocate (objc_condition_t condition ATTRIBUTE_UNUSED)
 {
   /* Unimplemented.  */
   return -1;
@@ -310,8 +316,8 @@  __gthread_objc_condition_deallocate (obj
 
 /* Wait on the condition */
 int
-__gthread_objc_condition_wait (objc_condition_t __UNUSED_PARAM(condition),
-			       objc_mutex_t __UNUSED_PARAM(mutex))
+__gthread_objc_condition_wait (objc_condition_t condition ATTRIBUTE_UNUSED,
+			       objc_mutex_t mutex ATTRIBUTE_UNUSED)
 {
   /* Unimplemented.  */
   return -1;
@@ -319,7 +325,7 @@  __gthread_objc_condition_wait (objc_cond
 
 /* Wake up all threads waiting on this condition.  */
 int
-__gthread_objc_condition_broadcast (objc_condition_t __UNUSED_PARAM(condition))
+__gthread_objc_condition_broadcast (objc_condition_t condition ATTRIBUTE_UNUSED)
 {
   /* Unimplemented.  */
   return -1;
@@ -327,7 +333,7 @@  __gthread_objc_condition_broadcast (objc
 
 /* Wake up one thread waiting on this condition.  */
 int
-__gthread_objc_condition_signal (objc_condition_t __UNUSED_PARAM(condition))
+__gthread_objc_condition_signal (objc_condition_t condition ATTRIBUTE_UNUSED)
 {
   /* Unimplemented.  */
   return -1;
@@ -335,35 +341,46 @@  __gthread_objc_condition_signal (objc_co
 
 #else /* _LIBOBJC */
 
+/* For struct timespec.  Do not include <sys/time.h> here since Gnulib provides
+   its own version which drags the Win32 API definitions.  */
+#include <sys/timeb.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-typedef unsigned long __gthread_key_t;
+typedef unsigned int __gthr_win32_DWORD;
+typedef void *__gthr_win32_HANDLE;
 
 typedef struct {
-  int done;
-  long started;
-} __gthread_once_t;
+  void *DebugInfo;
+  int LockCount;
+  int RecursionCount;
+  __gthr_win32_HANDLE OwningThread;
+  __gthr_win32_HANDLE LockSemaphore;
+  void *SpinCount;
+} __gthr_win32_CRITICAL_SECTION;
 
 typedef struct {
-  long counter;
-  void *sema;
-} __gthread_mutex_t;
+  void *Ptr;
+} __gthr_win32_CONDITION_VARIABLE;
 
-typedef struct {
-  long counter;
-  long depth;
-  unsigned long owner;
-  void *sema;
-} __gthread_recursive_mutex_t;
+typedef __gthr_win32_HANDLE __gthread_t;
+typedef __gthr_win32_DWORD __gthread_key_t;
+typedef struct { int done; long started; } __gthread_once_t;
+typedef __gthr_win32_CRITICAL_SECTION __gthread_mutex_t;
+typedef __gthr_win32_CRITICAL_SECTION __gthread_recursive_mutex_t;
+#if _GTHREADS_USE_COND
+typedef __gthr_win32_CONDITION_VARIABLE __gthread_cond_t;
+#endif
+typedef struct timespec __gthread_time_t;
 
 #define __GTHREAD_ONCE_INIT {0, -1}
 #define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init_function
-#define __GTHREAD_MUTEX_INIT_DEFAULT {-1, 0}
 #define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION \
   __gthread_recursive_mutex_init_function
-#define __GTHREAD_RECURSIVE_MUTEX_INIT_DEFAULT {-1, 0, 0, 0}
+#define __GTHREAD_COND_INIT_FUNCTION __gthread_cond_init_function
+#define __GTHREAD_TIME_INIT {0, 0}
 
 #if defined (_WIN32) && !defined(__CYGWIN__)
 #define MINGW32_SUPPORTS_MT_EH 1
@@ -374,29 +391,6 @@  extern int _CRT_MT;
 extern int __mingwthr_key_dtor (unsigned long, void (*) (void *));
 #endif /* _WIN32 && !__CYGWIN__ */
 
-/* The Windows95 kernel does not export InterlockedCompareExchange.
-   This provides a substitute.   When building apps that reference
-   gthread_mutex_try_lock, the  __GTHREAD_I486_INLINE_LOCK_PRIMITIVES
-   macro  must be defined if Windows95 is a target.  Currently
-   gthread_mutex_try_lock is not referenced by libgcc or libstdc++.  */
-#ifdef __GTHREAD_I486_INLINE_LOCK_PRIMITIVES
-static inline long
-__gthr_i486_lock_cmp_xchg(long *__dest, long __xchg, long __comperand)
-{
-  long result;
-  __asm__ __volatile__ ("\n\
-	lock\n\
-	cmpxchg{l} {%4, %1|%1, %4}\n"
-	: "=a" (result), "=m" (*__dest)
-	: "0" (__comperand), "m" (*__dest), "r" (__xchg)
-	: "cc");
-  return result;
-}
-#define __GTHR_W32_InterlockedCompareExchange __gthr_i486_lock_cmp_xchg
-#else  /* __GTHREAD_I486_INLINE_LOCK_PRIMITIVES */
-#define __GTHR_W32_InterlockedCompareExchange InterlockedCompareExchange
-#endif /* __GTHREAD_I486_INLINE_LOCK_PRIMITIVES */
-
 static inline int
 __gthread_active_p (void)
 {
@@ -407,30 +401,74 @@  __gthread_active_p (void)
 #endif
 }
 
-#if __GTHREAD_HIDE_WIN32API
-
-/* The implementations are in config/i386/gthr-win32.c in libgcc.a.
-   Only stubs are exposed to avoid polluting the C++ namespace with
-   windows api definitions.  */
-
+extern int __gthr_win32_create (__gthread_t *, void *(*) (void*), void *);
+extern int __gthr_win32_join (__gthread_t, void **);
+extern __gthread_t __gthr_win32_self (void);
 extern int __gthr_win32_once (__gthread_once_t *, void (*) (void));
+extern int __gthr_win32_detach (__gthread_t);
+extern int __gthr_win32_equal (__gthread_t, __gthread_t);
+extern int __gthr_win32_yield (void);
 extern int __gthr_win32_key_create (__gthread_key_t *, void (*) (void*));
 extern int __gthr_win32_key_delete (__gthread_key_t);
 extern void * __gthr_win32_getspecific (__gthread_key_t);
 extern int __gthr_win32_setspecific (__gthread_key_t, const void *);
 extern void __gthr_win32_mutex_init_function (__gthread_mutex_t *);
+extern void __gthr_win32_mutex_destroy (__gthread_mutex_t *);
 extern int __gthr_win32_mutex_lock (__gthread_mutex_t *);
 extern int __gthr_win32_mutex_trylock (__gthread_mutex_t *);
 extern int __gthr_win32_mutex_unlock (__gthread_mutex_t *);
-extern void
-  __gthr_win32_recursive_mutex_init_function (__gthread_recursive_mutex_t *);
-extern int __gthr_win32_recursive_mutex_lock (__gthread_recursive_mutex_t *);
-extern int
-  __gthr_win32_recursive_mutex_trylock (__gthread_recursive_mutex_t *);
-extern int __gthr_win32_recursive_mutex_unlock (__gthread_recursive_mutex_t *);
-extern void __gthr_win32_mutex_destroy (__gthread_mutex_t *);
-extern int
-  __gthr_win32_recursive_mutex_destroy (__gthread_recursive_mutex_t *);
+extern int __gthr_win32_recursive_mutex_trylock (__gthread_recursive_mutex_t *);
+#if _GTHREADS_USE_COND
+extern void __gthr_win32_cond_init_function (__gthread_cond_t *);
+extern int __gthr_win32_cond_broadcast (__gthread_cond_t *);
+extern int __gthr_win32_cond_signal (__gthread_cond_t *);
+extern int __gthr_win32_cond_wait (__gthread_cond_t *, __gthread_mutex_t *);
+extern int __gthr_win32_cond_timedwait (__gthread_cond_t *, __gthread_mutex_t *,
+					const __gthread_time_t *);
+#endif
+
+static inline int
+__gthread_create (__gthread_t *__thr, void *(*__func) (void*),
+		  void *__args)
+{
+  return __gthr_win32_create (__thr, __func, __args);
+}
+
+static inline int
+__gthread_join (__gthread_t __thr, void **__value_ptr)
+{
+  return __gthr_win32_join (__thr, __value_ptr);
+}
+
+static inline __gthread_t
+__gthread_self (void)
+{
+  return __gthr_win32_self ();
+}
+
+#if __GTHREAD_HIDE_WIN32API
+
+/* The implementations are in config/i386/gthr-win32.c in libgcc.a.
+   Only stubs are exposed to avoid polluting the C++ namespace with
+   Win32 API definitions.  */
+
+static inline int
+__gthread_detach (__gthread_t __thr)
+{
+  return __gthr_win32_detach (__thr);
+}
+
+static inline int
+__gthread_equal (__gthread_t __thr1, __gthread_t __thr2)
+{
+  return __gthr_win32_equal (__thr1, __thr2);
+}
+
+static inline int
+__gthread_yield (void)
+{
+  return __gthr_win32_yield ();
+}
 
 static inline int
 __gthread_once (__gthread_once_t *__once, void (*__func) (void))
@@ -504,279 +542,317 @@  __gthread_mutex_unlock (__gthread_mutex_
     return 0;
 }
 
+static inline int
+__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex)
+{
+  if (__gthread_active_p ())
+    return __gthr_win32_recursive_mutex_trylock (__mutex);
+  else
+    return 0;
+}
+
+#if _GTHREADS_USE_COND
+
 static inline void
-__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *__mutex)
+__gthread_cond_init_function (__gthread_cond_t *__cond)
 {
-   __gthr_win32_recursive_mutex_init_function (__mutex);
+  __gthr_win32_cond_init_function (__cond);
 }
 
 static inline int
-__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *__mutex)
+__gthread_cond_broadcast (__gthread_cond_t *__cond)
 {
-  if (__gthread_active_p ())
-    return __gthr_win32_recursive_mutex_lock (__mutex);
-  else
-    return 0;
+  return __gthr_win32_cond_broadcast (__cond);
 }
 
 static inline int
-__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex)
+__gthread_cond_signal (__gthread_cond_t *__cond)
 {
-  if (__gthread_active_p ())
-    return __gthr_win32_recursive_mutex_trylock (__mutex);
-  else
-    return 0;
+  return __gthr_win32_cond_signal (__cond);
 }
 
 static inline int
-__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *__mutex)
+__gthread_cond_wait (__gthread_cond_t *__cond, __gthread_mutex_t *__mutex)
 {
-  if (__gthread_active_p ())
-    return __gthr_win32_recursive_mutex_unlock (__mutex);
-  else
-    return 0;
+  return __gthr_win32_cond_wait (__cond, __mutex);
 }
 
 static inline int
-__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex)
+__gthread_cond_timedwait (__gthread_cond_t *__cond, __gthread_mutex_t *__mutex,
+			  const __gthread_time_t *__abs_time)
 {
-  return __gthr_win32_recursive_mutex_destroy (__mutex);
+  return __gthr_win32_cond_timedwait (__cond, __mutex, __abs_time);
 }
 
+#endif /* _GTHREADS_USE_COND */
+
 #else /* ! __GTHREAD_HIDE_WIN32API */
 
-#define NOGDI
+#ifndef __GTHREAD_WIN32_INLINE
+#define __GTHREAD_WIN32_INLINE static inline
+#endif
+
+#ifndef __GTHREAD_WIN32_COND_INLINE
+#define __GTHREAD_WIN32_COND_INLINE static inline
+#endif
+
+#ifndef __GTHREAD_WIN32_ACTIVE_P
+#define __GTHREAD_WIN32_ACTIVE_P __gthread_active_p
+#endif
+
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-#include <errno.h>
 
-static inline int
+__GTHREAD_WIN32_INLINE int
+__gthread_detach (__gthread_t __thr)
+{
+  CloseHandle ((HANDLE) __thr);
+  return 0;
+}
+
+__GTHREAD_WIN32_INLINE int
+__gthread_equal (__gthread_t __t1, __gthread_t __t2)
+{
+  return GetThreadId ((HANDLE) __t1) == GetThreadId ((HANDLE) __t2);
+}
+
+__GTHREAD_WIN32_INLINE int
+__gthread_yield (void)
+{
+  Sleep (0);
+  return 0;
+}
+
+__GTHREAD_WIN32_INLINE int
 __gthread_once (__gthread_once_t *__once, void (*__func) (void))
 {
-  if (! __gthread_active_p ())
+  if (!__GTHREAD_WIN32_ACTIVE_P ())
     return -1;
-  else if (__once == NULL || __func == NULL)
-    return EINVAL;
 
-  if (! __once->done)
+  if (__builtin_expect (!__once->done, 0))
     {
-      if (InterlockedIncrement (&(__once->started)) == 0)
+      /* We rely on the memory model of the x86 architecture where every load
+	 has acquire semantics and every store has release semantics.  */
+      if (__atomic_add_fetch (&__once->started, 1, __ATOMIC_ACQ_REL) == 0)
 	{
 	  (*__func) ();
-	  __once->done = TRUE;
+	  __once->done = 1;
 	}
       else
 	{
 	  /* Another thread is currently executing the code, so wait for it
-	     to finish; yield the CPU in the meantime.  If performance
+	     to finish and yield the CPU in the meantime.  If performance
 	     does become an issue, the solution is to use an Event that
 	     we wait on here (and set above), but that implies a place to
 	     create the event before this routine is called.  */
-	  while (! __once->done)
-	    Sleep (0);
+	  while (!__once->done)
+	    __gthread_yield ();
 	}
     }
 
   return 0;
 }
 
-/* Windows32 thread local keys don't support destructors; this leads to
+/* Windows thread local keys don't support destructors; this leads to
    leaks, especially in threaded applications making extensive use of
    C++ EH. Mingw uses a thread-support DLL to work-around this problem.  */
-static inline int
+__GTHREAD_WIN32_INLINE int
 __gthread_key_create (__gthread_key_t *__key,
-		      void (*__dtor) (void *) __attribute__((__unused__)))
+		      void (*__dtor) (void *) ATTRIBUTE_UNUSED)
 {
-  int __status = 0;
   DWORD __tls_index = TlsAlloc ();
-  if (__tls_index != 0xFFFFFFFF)
+  if (__tls_index != TLS_OUT_OF_INDEXES)
     {
       *__key = __tls_index;
 #ifdef MINGW32_SUPPORTS_MT_EH
       /* Mingw runtime will run the dtors in reverse order for each thread
          when the thread exits.  */
-      __status = __mingwthr_key_dtor (*__key, __dtor);
+      return __mingwthr_key_dtor (*__key, __dtor);
+#else
+      return 0;
 #endif
     }
   else
-    __status = (int) GetLastError ();
-  return __status;
+    return (int) GetLastError ();
 }
 
-static inline int
+__GTHREAD_WIN32_INLINE int
 __gthread_key_delete (__gthread_key_t __key)
 {
-  return (TlsFree (__key) != 0) ? 0 : (int) GetLastError ();
+  if (TlsFree (__key))
+    return 0;
+  else
+    return (int) GetLastError ();
 }
 
-static inline void *
+__GTHREAD_WIN32_INLINE void *
 __gthread_getspecific (__gthread_key_t __key)
 {
-  DWORD __lasterror;
-  void *__ptr;
-
-  __lasterror = GetLastError ();
-
-  __ptr = TlsGetValue (__key);
-
+  DWORD __lasterror = GetLastError ();
+  void *__ptr = TlsGetValue (__key);
   SetLastError (__lasterror);
-
   return __ptr;
 }
 
-static inline int
+__GTHREAD_WIN32_INLINE int
 __gthread_setspecific (__gthread_key_t __key, const void *__ptr)
 {
-  if (TlsSetValue (__key, CONST_CAST2(void *, const void *, __ptr)) != 0)
+  if (TlsSetValue (__key, CONST_CAST2(void *, const void *, __ptr)))
     return 0;
   else
-    return GetLastError ();
+    return (int) GetLastError ();
 }
 
-static inline void
+__GTHREAD_WIN32_INLINE void
 __gthread_mutex_init_function (__gthread_mutex_t *__mutex)
 {
-  __mutex->counter = -1;
-  __mutex->sema = CreateSemaphoreW (NULL, 0, 65535, NULL);
+  InitializeCriticalSection ((LPCRITICAL_SECTION) __mutex);
 }
 
-static inline void
+__GTHREAD_WIN32_INLINE void
 __gthread_mutex_destroy (__gthread_mutex_t *__mutex)
 {
-  CloseHandle ((HANDLE) __mutex->sema);
+  DeleteCriticalSection ((LPCRITICAL_SECTION) __mutex);
 }
 
-static inline int
+__GTHREAD_WIN32_INLINE int
 __gthread_mutex_lock (__gthread_mutex_t *__mutex)
 {
-  int __status = 0;
+  if (__GTHREAD_WIN32_ACTIVE_P ())
+    EnterCriticalSection ((LPCRITICAL_SECTION) __mutex);
+  return 0;
+}
 
-  if (__gthread_active_p ())
+__GTHREAD_WIN32_INLINE int
+__gthread_mutex_trylock (__gthread_mutex_t *__mutex)
+{
+  if (__GTHREAD_WIN32_ACTIVE_P ())
     {
-      if (InterlockedIncrement (&__mutex->counter) == 0 ||
-	  WaitForSingleObject (__mutex->sema, INFINITE) == WAIT_OBJECT_0)
-	__status = 0;
-      else
+      BOOL __ret = TryEnterCriticalSection ((LPCRITICAL_SECTION) __mutex);
+      if (__ret)
 	{
-	  /* WaitForSingleObject returns WAIT_FAILED, and we can only do
-	     some best-effort cleanup here.  */
-	  InterlockedDecrement (&__mutex->counter);
-	  __status = 1;
+	  if (__mutex->RecursionCount > 1)
+	    {
+	      LeaveCriticalSection ((LPCRITICAL_SECTION) __mutex);
+	      return 1;
+	    }
+	  else
+	    return 0;
 	}
+      else
+	return 1;
     }
-  return __status;
+  else
+    return 0;
 }
 
-static inline int
-__gthread_mutex_trylock (__gthread_mutex_t *__mutex)
+__GTHREAD_WIN32_INLINE int
+__gthread_mutex_unlock (__gthread_mutex_t *__mutex)
 {
-  int __status = 0;
+  if (__GTHREAD_WIN32_ACTIVE_P ())
+    LeaveCriticalSection ((LPCRITICAL_SECTION) __mutex);
+  return 0;
+}
 
-  if (__gthread_active_p ())
-    {
-      if (__GTHR_W32_InterlockedCompareExchange (&__mutex->counter, 0, -1) < 0)
-	__status = 0;
-      else
-	__status = 1;
-    }
-  return __status;
+__GTHREAD_WIN32_INLINE int
+__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex)
+{
+  if (__GTHREAD_WIN32_ACTIVE_P ())
+    return TryEnterCriticalSection ((LPCRITICAL_SECTION) __mutex) ? 0 : 1;
+  else
+    return 0;
 }
 
-static inline int
-__gthread_mutex_unlock (__gthread_mutex_t *__mutex)
+#if _GTHREADS_USE_COND
+
+__GTHREAD_WIN32_COND_INLINE void
+__gthread_cond_init_function (__gthread_cond_t *__cond)
 {
-  if (__gthread_active_p ())
-    {
-      if (InterlockedDecrement (&__mutex->counter) >= 0)
-	return ReleaseSemaphore (__mutex->sema, 1, NULL) ? 0 : 1;
-    }
+  InitializeConditionVariable ((PCONDITION_VARIABLE) __cond);
+}
+
+__GTHREAD_WIN32_COND_INLINE int
+__gthread_cond_broadcast (__gthread_cond_t *__cond)
+{
+  WakeAllConditionVariable ((PCONDITION_VARIABLE) __cond);
   return 0;
 }
 
+__GTHREAD_WIN32_COND_INLINE int
+__gthread_cond_signal (__gthread_cond_t *__cond)
+{
+  WakeConditionVariable ((PCONDITION_VARIABLE) __cond);
+  return 0;
+}
+
+__GTHREAD_WIN32_COND_INLINE int
+__gthread_cond_wait (__gthread_cond_t *__cond, __gthread_mutex_t *__mutex)
+{
+  if (SleepConditionVariableCS ((PCONDITION_VARIABLE) __cond,
+				(PCRITICAL_SECTION) __mutex,
+				INFINITE))
+    return 0;
+  else
+    return (int) GetLastError ();
+}
+
+extern DWORD __gthr_win32_abs_to_rel_time (const __gthread_time_t *);
+
+__GTHREAD_WIN32_COND_INLINE int
+__gthread_cond_timedwait (__gthread_cond_t *__cond,
+			  __gthread_mutex_t *__mutex,
+			  const __gthread_time_t *__abs_time)
+{
+  DWORD __rel_time = __gthr_win32_abs_to_rel_time (__abs_time);
+  if (SleepConditionVariableCS ((PCONDITION_VARIABLE) __cond,
+				(PCRITICAL_SECTION) __mutex,
+				__rel_time))
+    return 0;
+  else
+    return (int) GetLastError ();
+}
+
+#endif /* _GTHREADS_USE_COND */
+
+#endif /*  __GTHREAD_HIDE_WIN32API */
+
 static inline void
 __gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *__mutex)
 {
-  __mutex->counter = -1;
-  __mutex->depth = 0;
-  __mutex->owner = 0;
-  __mutex->sema = CreateSemaphoreW (NULL, 0, 65535, NULL);
+  __gthread_mutex_init_function (__mutex);
 }
 
-static inline int
-__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *__mutex)
+static inline void
+__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex)
 {
-  if (__gthread_active_p ())
-    {
-      DWORD __me = GetCurrentThreadId();
-      if (InterlockedIncrement (&__mutex->counter) == 0)
-	{
-	  __mutex->depth = 1;
-	  __mutex->owner = __me;
-	}
-      else if (__mutex->owner == __me)
-	{
-	  InterlockedDecrement (&__mutex->counter);
-	  ++(__mutex->depth);
-	}
-      else if (WaitForSingleObject (__mutex->sema, INFINITE) == WAIT_OBJECT_0)
-	{
-	  __mutex->depth = 1;
-	  __mutex->owner = __me;
-	}
-      else
-	{
-	  /* WaitForSingleObject returns WAIT_FAILED, and we can only do
-	     some best-effort cleanup here.  */
-	  InterlockedDecrement (&__mutex->counter);
-	  return 1;
-	}
-    }
-  return 0;
+  __gthread_mutex_destroy (__mutex);
 }
 
 static inline int
-__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex)
+__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *__mutex)
 {
-  if (__gthread_active_p ())
-    {
-      DWORD __me = GetCurrentThreadId();
-      if (__GTHR_W32_InterlockedCompareExchange (&__mutex->counter, 0, -1) < 0)
-	{
-	  __mutex->depth = 1;
-	  __mutex->owner = __me;
-	}
-      else if (__mutex->owner == __me)
-	++(__mutex->depth);
-      else
-	return 1;
-    }
-  return 0;
+  return __gthread_mutex_lock (__mutex);
 }
 
 static inline int
 __gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *__mutex)
 {
-  if (__gthread_active_p ())
-    {
-      --(__mutex->depth);
-      if (__mutex->depth == 0)
-	{
-	  __mutex->owner = 0;
-
-	  if (InterlockedDecrement (&__mutex->counter) >= 0)
-	    return ReleaseSemaphore (__mutex->sema, 1, NULL) ? 0 : 1;
-	}
-    }
-  return 0;
+  return __gthread_mutex_unlock (__mutex);
 }
 
+#if _GTHREADS_USE_COND
+
+static inline void
+__gthread_cond_destroy (__gthread_cond_t *__cond ATTRIBUTE_UNUSED) {}
+
 static inline int
-__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex)
+__gthread_cond_wait_recursive (__gthread_cond_t *__cond,
+			       __gthread_recursive_mutex_t *__mutex)
 {
-  CloseHandle ((HANDLE) __mutex->sema);
-  return 0;
+  return __gthread_cond_wait (__cond, __mutex);
 }
 
-#endif /*  __GTHREAD_HIDE_WIN32API */
+#endif
 
 #ifdef __cplusplus
 }
Index: libgcc/config/i386/libgcc-mingw.ver
===================================================================
--- libgcc/config/i386/libgcc-mingw.ver	(nonexistent)
+++ libgcc/config/i386/libgcc-mingw.ver	(working copy)
@@ -0,0 +1,23 @@ 
+# Copyright (C) 2019 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+GCC_10 {
+  __gthr_win32_create
+  __gthr_win32_join
+  __gthr_win32_self
+}
Index: libgcc/config/i386/t-gthr-win32
===================================================================
--- libgcc/config/i386/t-gthr-win32	(revision 272633)
+++ libgcc/config/i386/t-gthr-win32	(working copy)
@@ -1,2 +1,6 @@ 
-# We hide calls to w32api needed for w32 thread support here:
-LIB2ADD = $(srcdir)/config/i386/gthr-win32.c
+# We need a unique module interfacing with the Win32 API for thread support.
+LIB2ADDEH += $(srcdir)/config/i386/gthr-win32-thread.c
+# We hide calls to the Win32 API needed for condition variable support here.
+LIB2ADD_ST += $(srcdir)/config/i386/gthr-win32-cond.c
+# We hide calls to the Win32 API needed for the rest here.
+LIB2ADD_ST += $(srcdir)/config/i386/gthr-win32.c
Index: libgcc/config/i386/t-mingw-pthread
===================================================================
--- libgcc/config/i386/t-mingw-pthread	(revision 272633)
+++ libgcc/config/i386/t-mingw-pthread	(working copy)
@@ -1,2 +1,4 @@ 
+# For binary compatibility with t-gthr-win32
+LIB2ADDEH += $(srcdir)/config/i386/gthr-win32-thread.c
 SHLIB_PTHREAD_CFLAG = -pthread
 SHLIB_PTHREAD_LDFLAG = -Wl,-lpthread
Index: libgcc/config/i386/t-slibgcc-mingw
===================================================================
--- libgcc/config/i386/t-slibgcc-mingw	(nonexistent)
+++ libgcc/config/i386/t-slibgcc-mingw	(working copy)
@@ -0,0 +1 @@ 
+SHLIB_MAPFILES += $(srcdir)/config/i386/libgcc-mingw.ver
Index: libgcc/config.host
===================================================================
--- libgcc/config.host	(revision 272633)
+++ libgcc/config.host	(working copy)
@@ -778,10 +778,10 @@  i[34567]86-*-mingw*)
 	fi
 	case ${target_thread_file} in
 	  win32)
-	    tmake_file="$tmake_file i386/t-gthr-win32"
+	    tmake_thr_file="i386/t-gthr-win32"
 	    ;;
 	  posix)
-	    tmake_file="i386/t-mingw-pthread $tmake_file"
+	    tmake_thr_file="i386/t-mingw-pthread"
 	    ;;
 	esac
 	# This has to match the logic for DWARF2_UNWIND_INFO in gcc/config/i386/cygming.h
@@ -797,15 +797,15 @@  i[34567]86-*-mingw*)
 	else
 		tmake_dlldir_file="i386/t-dlldir-x"
 	fi
-	tmake_file="${tmake_file} ${tmake_eh_file} ${tmake_dlldir_file} i386/t-slibgcc-cygming i386/t-cygming i386/t-mingw32 t-crtfm i386/t-chkstk t-dfprules"
+	tmake_file="${tmake_file} ${tmake_eh_file} ${tmake_thr_file} ${tmake_dlldir_file} i386/t-slibgcc-cygming i386/t-slibgcc-mingw i386/t-cygming i386/t-mingw32 t-crtfm i386/t-chkstk t-dfprules"
 	;;
 x86_64-*-mingw*)
 	case ${target_thread_file} in
 	  win32)
-	    tmake_file="$tmake_file i386/t-gthr-win32"
+	    tmake_thr_file="i386/t-gthr-win32"
 	    ;;
 	  posix)
-	    tmake_file="i386/t-mingw-pthread $tmake_file"
+	    tmake_thr_file="i386/t-mingw-pthread"
 	    ;;
 	esac
 	# This has to match the logic for DWARF2_UNWIND_INFO in gcc/config/i386/cygming.h
@@ -824,7 +824,7 @@  x86_64-*-mingw*)
 	else
 		tmake_dlldir_file="i386/t-dlldir-x"
 	fi
-	tmake_file="${tmake_file} ${tmake_eh_file} ${tmake_dlldir_file} i386/t-slibgcc-cygming i386/t-cygming i386/t-mingw32 t-dfprules t-crtfm i386/t-chkstk"
+	tmake_file="${tmake_file} ${tmake_eh_file} ${tmake_thr_file} ${tmake_dlldir_file} i386/t-slibgcc-cygming i386/t-slibgcc-mingw i386/t-cygming i386/t-mingw32 t-dfprules t-crtfm i386/t-chkstk"
 	extra_parts="$extra_parts crtbegin.o crtend.o crtfastmath.o"
 	if test x$enable_vtable_verify = xyes; then
 		extra_parts="$extra_parts vtv_start.o vtv_end.o vtv_start_preinit.o vtv_end_preinit.o"
Index: libstdc++-v3/acinclude.m4
===================================================================
--- libstdc++-v3/acinclude.m4	(revision 272633)
+++ libstdc++-v3/acinclude.m4	(working copy)
@@ -1400,6 +1400,10 @@  AC_DEFUN([GLIBCXX_ENABLE_LIBSTDCXX_TIME]
       cygwin*)
         ac_has_nanosleep=yes
         ;;
+      mingw*)
+        ac_has_win32_sleep=yes
+        ac_has_sched_yield=yes
+        ;;
       darwin*)
         ac_has_nanosleep=yes
         ac_has_sched_yield=yes
@@ -1567,6 +1571,9 @@  AC_DEFUN([GLIBCXX_ENABLE_LIBSTDCXX_TIME]
   if test x"$ac_has_nanosleep" = x"yes"; then
     AC_DEFINE(_GLIBCXX_USE_NANOSLEEP, 1,
       [ Defined if nanosleep is available. ])
+  elif test x"$ac_has_win32_sleep" = x"yes"; then
+    AC_DEFINE(_GLIBCXX_USE_WIN32_SLEEP, 1,
+      [Defined if Sleep exists.])
   else
       AC_MSG_CHECKING([for sleep])
       AC_TRY_COMPILE([#include <unistd.h>],
@@ -1587,17 +1594,6 @@  AC_DEFUN([GLIBCXX_ENABLE_LIBSTDCXX_TIME]
       AC_MSG_RESULT($ac_has_usleep)
   fi
 
-  if test x"$ac_has_nanosleep$ac_has_sleep" = x"nono"; then
-      AC_MSG_CHECKING([for Sleep])
-      AC_TRY_COMPILE([#include <windows.h>],
-                     [Sleep(1)],
-                     [ac_has_win32_sleep=yes],[ac_has_win32_sleep=no])
-      if test x"$ac_has_win32_sleep" = x"yes"; then
-        AC_DEFINE(HAVE_WIN32_SLEEP,1, [Defined if Sleep exists.])
-      fi
-      AC_MSG_RESULT($ac_has_win32_sleep)
-  fi
-
   AC_SUBST(GLIBCXX_LIBS)
 
   CXXFLAGS="$ac_save_CXXFLAGS"
@@ -2330,15 +2326,16 @@  AC_DEFUN([GLIBCXX_CHECK_MATH11_PROTO], [
 
 dnl
 dnl Check whether macros, etc are present for <system_error>
+dnl Please keep in sync with:
+dnl   testsuite/19_diagnostics/headers/system_error/errc_std_c++0x.cc
 dnl
 AC_DEFUN([GLIBCXX_CHECK_SYSTEM_ERROR], [
 
 m4_pushdef([n_syserr], [1])dnl
-m4_foreach([syserr], [EOWNERDEAD, ENOTRECOVERABLE, ENOLINK, EPROTO, ENODATA,
-		      ENOSR, ENOSTR, ETIME, EBADMSG, ECANCELED,
-		      EOVERFLOW, ENOTSUP, EIDRM, ETXTBSY,
-		      ECHILD, ENOSPC, EPERM,
-		      ETIMEDOUT, EWOULDBLOCK],
+m4_foreach([syserr], [EBADMSG, EIDRM, ECHILD, ENOLINK, ENODATA, ENOMSG,
+		      ENOSPC, ENOSR, ENOSTR, ENOTSUP, ECANCELED, EPERM,
+		      EWOULDBLOCK, EOWNERDEAD, EPROTO, ENOTRECOVERABLE,
+		      ETIME, ETXTBSY, ETIMEDOUT, EOVERFLOW],
 [m4_pushdef([SYSERR], m4_toupper(syserr))dnl
 AC_MSG_CHECKING([for syserr])
 AC_CACHE_VAL([glibcxx_cv_system_error[]n_syserr], [
@@ -3975,8 +3972,18 @@  AC_DEFUN([GLIBCXX_CHECK_GTHREADS], [
   case $target_thread_file in
     posix)
       CXXFLAGS="$CXXFLAGS -DSUPPORTS_WEAK -DGTHREAD_USE_WEAK -D_PTHREADS"
+      ;;
+    win32)
+      CXXFLAGS="$CXXFLAGS -D_WIN32_THREADS"
+      ;;
   esac
 
+  # The support of condition variables may be disabled by default in the
+  # gthreads library, so enable it on explicit request.
+  if test x$enable_libstdcxx_threads = xyes; then
+    CXXFLAGS="$CXXFLAGS -D_GTHREADS_USE_COND"
+  fi
+
   AC_MSG_CHECKING([whether it can be safely assumed that mutex_timedlock is available])
 
   AC_TRY_COMPILE([#include <unistd.h>],
@@ -3985,6 +3992,9 @@  AC_DEFUN([GLIBCXX_CHECK_GTHREADS], [
       #if (defined(_PTHREADS) \
 	  && (!defined(_POSIX_TIMEOUTS) || _POSIX_TIMEOUTS <= 0))
       #error
+      // In case of Win32 threads there is no support.
+      #elif defined(_WIN32_THREADS)
+      #error
       #endif
     ], [ac_gthread_use_mutex_timedlock=1], [ac_gthread_use_mutex_timedlock=0])
 
@@ -4013,6 +4023,9 @@  AC_DEFUN([GLIBCXX_CHECK_GTHREADS], [
     AC_DEFINE(_GLIBCXX_HAS_GTHREADS, 1,
 	      [Define if gthreads library is available.])
 
+    AC_DEFINE(_GTHREADS_USE_COND, 1,
+	      [Define if gthreads library uses condition variables.])
+
     # Also check for pthread_rwlock_t for std::shared_timed_mutex in C++14
     AC_CHECK_TYPE([pthread_rwlock_t],
             [AC_DEFINE([_GLIBCXX_USE_PTHREAD_RWLOCK_T], 1,
Index: libstdc++-v3/config/os/mingw32/os_defines.h
===================================================================
--- libstdc++-v3/config/os/mingw32/os_defines.h	(revision 272633)
+++ libstdc++-v3/config/os/mingw32/os_defines.h	(working copy)
@@ -75,6 +75,14 @@ 
 #define _GLIBCXX_LLP64 1
 #endif
 
+// Enable use of GetModuleHandleEx (requires Windows XP/2003) in
+// __cxa_thread_atexit to prevent modules from being unloaded before
+// their dtors are called
+#define _GLIBCXX_THREAD_ATEXIT_WIN32 1
+
+// Enable use of GetSystemInfo to implement get_nprocs
+#define _GLIBCXX_USE_GET_NPROCS_WIN32 1
+
 // See libstdc++/59807
 #define _GTHREAD_USE_MUTEX_INIT_FUNC 1
 
Index: libstdc++-v3/config/os/mingw32-w64/os_defines.h
===================================================================
--- libstdc++-v3/config/os/mingw32-w64/os_defines.h	(revision 272633)
+++ libstdc++-v3/config/os/mingw32-w64/os_defines.h	(working copy)
@@ -85,6 +85,9 @@ 
 // their dtors are called
 #define _GLIBCXX_THREAD_ATEXIT_WIN32 1
 
+// Enable use of GetSystemInfo to implement get_nprocs
+#define _GLIBCXX_USE_GET_NPROCS_WIN32 1
+
 // See libstdc++/59807
 #define _GTHREAD_USE_MUTEX_INIT_FUNC 1
 
Index: libstdc++-v3/src/c++11/thread.cc
===================================================================
--- libstdc++-v3/src/c++11/thread.cc	(revision 272633)
+++ libstdc++-v3/src/c++11/thread.cc	(working copy)
@@ -49,6 +49,16 @@  static inline int get_nprocs()
  return 0;
 }
 # define _GLIBCXX_NPROCS get_nprocs()
+#elif defined(_GLIBCXX_USE_GET_NPROCS_WIN32)
+#define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+static inline int get_nprocs()
+{
+  SYSTEM_INFO sysinfo;
+  GetSystemInfo (&sysinfo);
+  return (int)sysinfo.dwNumberOfProcessors;
+}
+# define _GLIBCXX_NPROCS get_nprocs()
 #elif defined(_GLIBCXX_USE_SC_NPROCESSORS_ONLN)
 # include <unistd.h>
 # define _GLIBCXX_NPROCS sysconf(_SC_NPROCESSORS_ONLN)
@@ -62,7 +72,7 @@  static inline int get_nprocs()
 #ifndef _GLIBCXX_USE_NANOSLEEP
 # ifdef _GLIBCXX_HAVE_SLEEP
 #  include <unistd.h>
-# elif defined(_GLIBCXX_HAVE_WIN32_SLEEP)
+# elif defined(_GLIBCXX_USE_WIN32_SLEEP)
 #  include <windows.h>
 # else
 #  error "No sleep function known for this target"
@@ -223,7 +233,7 @@  namespace this_thread
 	__s = chrono::duration_cast<chrono::seconds>(target - now);
 	__ns = chrono::duration_cast<chrono::nanoseconds>(target - (now + __s));
     }
-#elif defined(_GLIBCXX_HAVE_WIN32_SLEEP)
+#elif defined(_GLIBCXX_USE_WIN32_SLEEP)
     unsigned long ms = __ns.count() / 1000000;
     if (__ns.count() > 0 && ms == 0)
       ms = 1;
Index: libstdc++-v3/testsuite/19_diagnostics/headers/system_error/errc_std_c++0x.cc
===================================================================
--- libstdc++-v3/testsuite/19_diagnostics/headers/system_error/errc_std_c++0x.cc	(revision 272633)
+++ libstdc++-v3/testsuite/19_diagnostics/headers/system_error/errc_std_c++0x.cc	(working copy)
@@ -70,7 +70,10 @@  void test01()
   TEST_ERRC(network_reset);
   TEST_ERRC(network_unreachable);
   TEST_ERRC(no_buffer_space);
+
+#ifdef _GLIBCXX_HAVE_ECHILD
   TEST_ERRC(no_child_process);
+#endif
 
 #ifdef _GLIBCXX_HAVE_ENOLINK
   TEST_ERRC(no_link);
@@ -86,7 +89,10 @@  void test01()
   TEST_ERRC(no_message);
 #endif
   TEST_ERRC(no_protocol_option);
+
+#ifdef _GLIBCXX_HAVE_ENOSPC
   TEST_ERRC(no_space_on_device);
+#endif
 
 #ifdef _GLIBCXX_HAVE_ENOSR
   TEST_ERRC(no_stream_resources);
@@ -105,16 +111,26 @@  void test01()
 
   TEST_ERRC(not_connected); 
   TEST_ERRC(not_enough_memory);
+
+#ifdef _GLIBCXX_HAVE_ENOTSUP
   TEST_ERRC(not_supported);
+#endif
 
 #ifdef _GLIBCXX_HAVE_ECANCELED
   TEST_ERRC(operation_canceled);
 #endif
 
   TEST_ERRC(operation_in_progress);
+
+#ifdef _GLIBCXX_HAVE_EPERM
   TEST_ERRC(operation_not_permitted);
+#endif
+
   TEST_ERRC(operation_not_supported);
+
+#ifdef _GLIBCXX_HAVE_EWOULDBLOCK
   TEST_ERRC(operation_would_block);
+#endif
 
 #ifdef _GLIBCXX_HAVE_EOWNERDEAD
   TEST_ERRC(owner_dead);
@@ -144,7 +160,10 @@  void test01()
   TEST_ERRC(text_file_busy);
 #endif
 
+#ifdef _GLIBCXX_HAVE_ETIMEDOUT
   TEST_ERRC(timed_out);
+#endif
+
   TEST_ERRC(too_many_files_open_in_system);
   TEST_ERRC(too_many_files_open);
   TEST_ERRC(too_many_links);
Index: libstdc++-v3/testsuite/lib/libstdc++.exp
===================================================================
--- libstdc++-v3/testsuite/lib/libstdc++.exp	(revision 272633)
+++ libstdc++-v3/testsuite/lib/libstdc++.exp	(working copy)
@@ -508,6 +508,15 @@  proc v3_target_compile { source dest typ
 	}
     }
 
+    # Small adjustment for MinGW hosts.
+    if { $dest == "/dev/null" && [ishost "*-*-mingw*"] } {
+	if { $type == "executable" } {
+	    set dest "x.exe"
+	} else {
+	    set dest "nul"
+	}
+    }
+
     lappend options "compiler=$cxx_final"
     lappend options "timeout=[timeout_value]"
 
@@ -1239,8 +1248,10 @@  proc check_v3_target_sleep { } {
 	set f [open $src "w"]
 	puts $f "#include <bits/c++config.h>"
 	puts $f "#ifndef _GLIBCXX_USE_NANOSLEEP"
-	puts $f "# ifndef _GLIBCXX_HAVE_SLEEP"
-	puts $f "#  error No nanosleep or sleep"
+	puts $f "# ifndef _GLIBCXX_USE_WIN32_SLEEP"
+	puts $f "#  ifndef _GLIBCXX_HAVE_SLEEP"
+	puts $f "#   error No nanosleep or Sleep or sleep"
+	puts $f "#  endif"
 	puts $f "# endif"
 	puts $f "#endif"
 	close $f
@@ -1402,6 +1413,7 @@  proc check_v3_target_nprocs { } {
 	set f [open $src "w"]
 	puts $f "#include <bits/c++config.h>"
 	puts $f "#if defined(_GLIBCXX_USE_GET_NPROCS)"
+	puts $f "#elif defined(_GLIBCXX_USE_GET_NPROCS_WIN32)"
 	puts $f "#elif defined(_GLIBCXX_USE_PTHREADS_NUM_PROCESSORS_NP)"
 	puts $f "#elif defined(_GLIBCXX_USE_SYSCTL_HW_NCPU)"
 	puts $f "#elif defined(_GLIBCXX_USE_SC_NPROCESSORS_ONLN)"
Index: gcc/testsuite/lib/target-supports.exp
===================================================================
--- gcc/testsuite/lib/target-supports.exp	(revision 272633)
+++ gcc/testsuite/lib/target-supports.exp	(working copy)
@@ -1040,11 +1040,10 @@  proc check_effective_target_swapcontext
     }]
 }
 
-# Return 1 if compilation with -pthread is error-free for trivial
-# code, 0 otherwise.
-
+# Return 1 if the target supports POSIX threads, 0 otherwise.
 proc check_effective_target_pthread {} {
     return [check_no_compiler_messages pthread object {
+	#include <pthread.h>
 	void foo (void) { }
     } "-pthread"]
 }