Reimplement GNU threads library on native Windows

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 was the default minimal setting of MinGW-W64
until end of 2020.  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 _WIN32_WINNT >= 0x0600.

This also changes libstdc++ to pass -D_WIN32_WINNT=0x0600 but only when the
switch --enable-libstdcxx-threads is passed, which means that C++11 threads
are still disabled by default *unless* MinGW-W64 itself is configured for
Windows Vista/Server 2008 or later by default (this has been the case in
the development version since end of 2020, for earlier versions you can
configure it --with-default-win32-winnt=0x0600 to get the same effect).

I only manually tested it on i686-w64-mingw32 and x86_64-w64-mingw32 but
AdaCore has used it in their C/C++/Ada compilers for 3 years now and the
30_threads chapter of the libstdc++ testsuite was clean at the time.

2022-10-31  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 _WIN32_WINNT is at least 0x0600, define
	both __GTHREAD_HAS_COND and __GTHREADS_CXX0X to 1.
	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_HAS_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_HAS_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-slibgcc-mingw: New file.
	* config/i386/libgcc-mingw.ver: Likewise.
libstdc++-v3/
	* acinclude.m4 (GLIBCXX_EXPORT_FLAGS): Substitute CPPFLAGS.
	(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 -D_WIN32_WINNT=0x0600 to compilation flags if yes was configured
	and add it to CPPFLAGS 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_USE_GET_NPROCS_WIN32): Ditto
	* 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.

Signed-off-by: Eric Botcazou <ebotcazou@adacore.com>
Signed-off-by: Jonathan Yong <10walls@gmail.com>
This commit is contained in:
Eric Botcazou 2022-12-23 23:45:15 +00:00 committed by Jonathan Yong
parent 6a95f0e0a0
commit 9149a5b7e0
16 changed files with 761 additions and 535 deletions

View File

@ -820,13 +820,13 @@ 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"
;;
mcf)
tmake_file="i386/t-mingw-mcfgthread $tmake_file"
tmake_thr_file="i386/t-mingw-mcfgthread"
;;
esac
# This has to match the logic for DWARF2_UNWIND_INFO in gcc/config/i386/cygming.h
@ -842,18 +842,18 @@ 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"
;;
mcf)
tmake_file="i386/t-mingw-mcfgthread $tmake_file"
tmake_thr_file="i386/t-mingw-mcfgthread"
;;
esac
# This has to match the logic for DWARF2_UNWIND_INFO in gcc/config/i386/cygming.h
@ -872,7 +872,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"

View File

@ -0,0 +1,89 @@
/* Implementation of threads compatibility routines for libgcc2. */
/* Copyright (C) 1999-2022 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. */
#if _WIN32_WINNT < 0x0600
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#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)

View File

@ -0,0 +1,162 @@
/* Implementation of threads compatibility routines for libgcc2. */
/* Copyright (C) 1999-2022 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;
}

View File

@ -1,10 +1,6 @@
/* Implementation of W32-specific threads compatibility routines for
libgcc2. */
/* Implementation of threads compatibility routines for libgcc2. */
/* Copyright (C) 1999-2022 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,239 +23,33 @@ 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/>. */
#include <windows.h>
#ifndef __GTHREAD_HIDE_WIN32API
# define __GTHREAD_HIDE_WIN32API 1
#endif
/* 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.
/* Check the sizes of the local version of the Win32 types. */
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).
#define CHECK_SIZE_OF(TYPE) \
typedef int assertion[sizeof(__gthr_win32_##TYPE) == sizeof(TYPE) ? 1 : -1];
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_SIZE_OF (DWORD)
CHECK_SIZE_OF (HANDLE)
CHECK_SIZE_OF (CRITICAL_SECTION)

View File

@ -28,18 +28,15 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#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
/* So we can test Windows version numbers. */
#include <stdlib.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.
/* 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 +47,41 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
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.
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>
/* Condition variables are supported on Vista and Server 2008 or later. */
#if _WIN32_WINNT >= 0x0600
#define __GTHREAD_HAS_COND 1
#define __GTHREADS_CXX0X 1
#endif
#ifndef __UNUSED_PARAM
#define __UNUSED_PARAM(x) x
#if _GTHREAD_USE_MUTEX_TIMEDLOCK
#error Timed lock primitives are not supported on Windows targets
#endif
/* 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 +91,13 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#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 +106,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 +116,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 +232,9 @@ __gthread_objc_thread_set_data (void *value)
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 +298,7 @@ __gthread_objc_mutex_unlock (objc_mutex_t 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 +306,7 @@ __gthread_objc_condition_allocate (objc_condition_t __UNUSED_PARAM(condition))
/* 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 +314,8 @@ __gthread_objc_condition_deallocate (objc_condition_t __UNUSED_PARAM(condition))
/* 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 +323,7 @@ __gthread_objc_condition_wait (objc_condition_t __UNUSED_PARAM(condition),
/* 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 +331,7 @@ __gthread_objc_condition_broadcast (objc_condition_t __UNUSED_PARAM(condition))
/* 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 +339,46 @@ __gthread_objc_condition_signal (objc_condition_t __UNUSED_PARAM(condition))
#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 __GTHREAD_HAS_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
@ -388,30 +403,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 __GTHREAD_HAS_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))
@ -485,21 +544,6 @@ __gthread_mutex_unlock (__gthread_mutex_t *__mutex)
return 0;
}
static inline void
__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *__mutex)
{
__gthr_win32_recursive_mutex_init_function (__mutex);
}
static inline int
__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *__mutex)
{
if (__gthread_active_p ())
return __gthr_win32_recursive_mutex_lock (__mutex);
else
return 0;
}
static inline int
__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex)
{
@ -509,255 +553,311 @@ __gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex)
return 0;
}
static inline int
__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *__mutex)
#if __GTHREAD_HAS_COND
static inline void
__gthread_cond_init_function (__gthread_cond_t *__cond)
{
if (__gthread_active_p ())
return __gthr_win32_recursive_mutex_unlock (__mutex);
else
return 0;
__gthr_win32_cond_init_function (__cond);
}
static inline int
__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex)
__gthread_cond_broadcast (__gthread_cond_t *__cond)
{
return __gthr_win32_recursive_mutex_destroy (__mutex);
return __gthr_win32_cond_broadcast (__cond);
}
static inline int
__gthread_cond_signal (__gthread_cond_t *__cond)
{
return __gthr_win32_cond_signal (__cond);
}
static inline int
__gthread_cond_wait (__gthread_cond_t *__cond, __gthread_mutex_t *__mutex)
{
return __gthr_win32_cond_wait (__cond, __mutex);
}
static inline int
__gthread_cond_timedwait (__gthread_cond_t *__cond, __gthread_mutex_t *__mutex,
const __gthread_time_t *__abs_time)
{
return __gthr_win32_cond_timedwait (__cond, __mutex, __abs_time);
}
#endif /* __GTHREAD_HAS_COND */
#else /* ! __GTHREAD_HIDE_WIN32API */
#define NOGDI
#include <windows.h>
#include <errno.h>
#ifndef __GTHREAD_WIN32_INLINE
#define __GTHREAD_WIN32_INLINE static inline
#endif
static inline int
#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>
__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_active_p ())
{
if (InterlockedIncrement (&__mutex->counter) == 0 ||
WaitForSingleObject (__mutex->sema, INFINITE) == WAIT_OBJECT_0)
__status = 0;
else
{
/* WaitForSingleObject returns WAIT_FAILED, and we can only do
some best-effort cleanup here. */
InterlockedDecrement (&__mutex->counter);
__status = 1;
}
}
return __status;
}
static inline int
__gthread_mutex_trylock (__gthread_mutex_t *__mutex)
{
int __status = 0;
if (__gthread_active_p ())
{
if (__GTHR_W32_InterlockedCompareExchange (&__mutex->counter, 0, -1) < 0)
__status = 0;
else
__status = 1;
}
return __status;
}
static inline int
__gthread_mutex_unlock (__gthread_mutex_t *__mutex)
{
if (__gthread_active_p ())
{
if (InterlockedDecrement (&__mutex->counter) >= 0)
return ReleaseSemaphore (__mutex->sema, 1, NULL) ? 0 : 1;
}
if (__GTHREAD_WIN32_ACTIVE_P ())
EnterCriticalSection ((LPCRITICAL_SECTION) __mutex);
return 0;
}
__GTHREAD_WIN32_INLINE int
__gthread_mutex_trylock (__gthread_mutex_t *__mutex)
{
if (__GTHREAD_WIN32_ACTIVE_P ())
{
BOOL __ret = TryEnterCriticalSection ((LPCRITICAL_SECTION) __mutex);
if (__ret)
{
if (__mutex->RecursionCount > 1)
{
LeaveCriticalSection ((LPCRITICAL_SECTION) __mutex);
return 1;
}
else
return 0;
}
else
return 1;
}
else
return 0;
}
__GTHREAD_WIN32_INLINE int
__gthread_mutex_unlock (__gthread_mutex_t *__mutex)
{
if (__GTHREAD_WIN32_ACTIVE_P ())
LeaveCriticalSection ((LPCRITICAL_SECTION) __mutex);
return 0;
}
__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;
}
#if __GTHREAD_HAS_COND
__GTHREAD_WIN32_COND_INLINE void
__gthread_cond_init_function (__gthread_cond_t *__cond)
{
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 /* __GTHREAD_HAS_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 void
__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex)
{
__gthread_mutex_destroy (__mutex);
}
static inline int
__gthread_recursive_mutex_lock (__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;
}
static inline int
__gthread_recursive_mutex_trylock (__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;
return __gthread_mutex_unlock (__mutex);
}
if (InterlockedDecrement (&__mutex->counter) >= 0)
return ReleaseSemaphore (__mutex->sema, 1, NULL) ? 0 : 1;
}
}
#if __GTHREAD_HAS_COND
static inline int
__gthread_cond_destroy (__gthread_cond_t *__cond ATTRIBUTE_UNUSED)
{
return 0;
}
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
}

View File

@ -0,0 +1,23 @@
# Copyright (C) 2022 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_13 {
__gthr_win32_create
__gthr_win32_join
__gthr_win32_self
}

View File

@ -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

View File

@ -0,0 +1 @@
SHLIB_MAPFILES += $(srcdir)/config/i386/libgcc-mingw.ver

View File

@ -678,10 +678,13 @@ dnl Set up *_FLAGS and *FLAGS variables for all sundry Makefile.am's.
dnl (SECTION_FLAGS is done under CHECK_COMPILER_FEATURES.)
dnl
dnl Substs:
dnl CPPFLAGS
dnl OPTIMIZE_CXXFLAGS
dnl WARN_FLAGS
dnl
AC_DEFUN([GLIBCXX_EXPORT_FLAGS], [
AC_SUBST(CPPFLAGS)
# Optimization flags that are probably a good idea for thrill-seekers. Just
# uncomment the lines below and make, everything else is ready to go...
# Alternatively OPTIMIZE_CXXFLAGS can be set in configure.host.
@ -1352,6 +1355,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
@ -1537,6 +1544,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>],
@ -1557,20 +1567,7 @@ 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_no_sleep=yes
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.])
ac_no_sleep=no
fi
AC_MSG_RESULT($ac_has_win32_sleep)
fi
if test x"$ac_no_sleep" = x"yes"; then
if test x"$ac_has_nanosleep$ac_has_win32_sleep$ac_has_sleep" = x"nonono"; then
AC_DEFINE(_GLIBCXX_NO_SLEEP,1, [Defined if no way to sleep is available.])
fi
@ -3987,6 +3984,15 @@ 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"
# The support of condition variables is disabled by default in
# the Win32 gthreads library, so enable it on explicit request.
if test x$enable_libstdcxx_threads = xyes; then
CXXFLAGS="$CXXFLAGS -D_WIN32_WINNT=0x0600"
fi
;;
esac
AC_MSG_CHECKING([whether it can be safely assumed that mutex_timedlock is available])
@ -3997,6 +4003,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])
@ -4043,6 +4052,11 @@ AC_DEFUN([GLIBCXX_CHECK_GTHREADS], [
[],
[#include "gthr.h"])
fi
# See above for the rationale.
if test $target_thread_file = win32; then
CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600"
fi
fi
AC_CHECK_HEADER(semaphore.h, [

View File

@ -525,9 +525,6 @@
/* Define to 1 if you have the <wctype.h> header file. */
#undef HAVE_WCTYPE_H
/* Defined if Sleep exists. */
#undef HAVE_WIN32_SLEEP
/* Define if writev is available in <sys/uio.h>. */
#undef HAVE_WRITEV
@ -1028,6 +1025,9 @@
/* Define if code specialized for wchar_t should be used. */
#undef _GLIBCXX_USE_WCHAR_T
/* Defined if Sleep exists. */
#undef _GLIBCXX_USE_WIN32_SLEEP
/* Define to 1 if a verbose library is built, or 0 otherwise. */
#undef _GLIBCXX_VERBOSE

View File

@ -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

View File

@ -75,6 +75,9 @@
#define _GLIBCXX_LLP64 1
#endif
// 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

View File

@ -20523,6 +20523,10 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
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
@ -21056,6 +21060,10 @@ $as_echo "#define _GLIBCXX_USE_SCHED_YIELD 1" >>confdefs.h
$as_echo "#define _GLIBCXX_USE_NANOSLEEP 1" >>confdefs.h
elif test x"$ac_has_win32_sleep" = x"yes"; then
$as_echo "#define _GLIBCXX_USE_WIN32_SLEEP 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sleep" >&5
$as_echo_n "checking for sleep... " >&6; }
@ -21112,38 +21120,7 @@ $as_echo "#define HAVE_USLEEP 1" >>confdefs.h
$as_echo "$ac_has_usleep" >&6; }
fi
if test x"$ac_has_nanosleep$ac_has_sleep" = x"nono"; then
ac_no_sleep=yes
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Sleep" >&5
$as_echo_n "checking for Sleep... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <windows.h>
int
main ()
{
Sleep(1)
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
ac_has_win32_sleep=yes
else
ac_has_win32_sleep=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
if test x"$ac_has_win32_sleep" = x"yes"; then
$as_echo "#define HAVE_WIN32_SLEEP 1" >>confdefs.h
ac_no_sleep=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_has_win32_sleep" >&5
$as_echo "$ac_has_win32_sleep" >&6; }
fi
if test x"$ac_no_sleep" = x"yes"; then
if test x"$ac_has_nanosleep$ac_has_win32_sleep$ac_has_sleep" = x"nonono"; then
$as_echo "#define _GLIBCXX_NO_SLEEP 1" >>confdefs.h
@ -69715,6 +69692,15 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
case $target_thread_file in
posix)
CXXFLAGS="$CXXFLAGS -DSUPPORTS_WEAK -DGTHREAD_USE_WEAK -D_PTHREADS"
;;
win32)
CXXFLAGS="$CXXFLAGS -D_WIN32_THREADS"
# The support of condition variables is disabled by default in
# the Win32 gthreads library, so enable it on explicit request.
if test x$enable_libstdcxx_threads = xyes; then
CXXFLAGS="$CXXFLAGS -D_WIN32_WINNT=0x0600"
fi
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it can be safely assumed that mutex_timedlock is available" >&5
@ -69731,6 +69717,9 @@ main ()
#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
;
@ -69827,6 +69816,11 @@ $as_echo "#define _GLIBCXX_USE_PTHREAD_RWLOCK_T 1" >>confdefs.h
fi
fi
# See above for the rationale.
if test $target_thread_file = win32; then
CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600"
fi
fi
ac_fn_cxx_check_header_mongrel "$LINENO" "semaphore.h" "ac_cv_header_semaphore_h" "$ac_includes_default"
@ -72603,6 +72597,8 @@ $as_echo "$gxx_include_dir" >&6; }
# Optimization flags that are probably a good idea for thrill-seekers. Just
# uncomment the lines below and make, everything else is ready to go...
# Alternatively OPTIMIZE_CXXFLAGS can be set in configure.host.

View File

@ -34,7 +34,7 @@
#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>
# elif defined _GLIBCXX_NO_SLEEP && defined _GLIBCXX_HAS_GTHREADS
// We expect to be able to sleep for targets that support multiple threads:
@ -62,6 +62,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)
@ -254,7 +264,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;

View File

@ -70,7 +70,10 @@ void test01()
TEST_ERRC(network_reset);
TEST_ERRC(network_unreachable);
TEST_ERRC(no_buffer_space);
#ifdef ECHILD
TEST_ERRC(no_child_process);
#endif
#ifdef ENOLINK
TEST_ERRC(no_link);
@ -86,7 +89,10 @@ void test01()
TEST_ERRC(no_message);
#endif
TEST_ERRC(no_protocol_option);
#ifdef ENOSPC
TEST_ERRC(no_space_on_device);
#endif
#ifdef ENOSR
TEST_ERRC(no_stream_resources);
@ -105,16 +111,26 @@ void test01()
TEST_ERRC(not_connected);
TEST_ERRC(not_enough_memory);
#ifdef ENOTSUP
TEST_ERRC(not_supported);
#endif
#ifdef ECANCELED
TEST_ERRC(operation_canceled);
#endif
TEST_ERRC(operation_in_progress);
#ifdef EPERM
TEST_ERRC(operation_not_permitted);
#endif
TEST_ERRC(operation_not_supported);
#ifdef EWOULDBLOCK
TEST_ERRC(operation_would_block);
#endif
#ifdef EOWNERDEAD
TEST_ERRC(owner_dead);
@ -144,7 +160,10 @@ void test01()
TEST_ERRC(text_file_busy);
#endif
#ifdef 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);

View File

@ -510,6 +510,15 @@ proc v3_target_compile { source dest type options } {
}
}
# 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]"
@ -1147,7 +1156,9 @@ proc check_effective_target_gthreads_timed { } {
# Return 1 if either nanosleep or sleep is available, 0 otherwise.
proc check_v3_target_sleep { } {
return [check_v3_target_prop_cached et_sleep {
set cond "defined _GLIBCXX_USE_NANOSLEEP || defined _GLIBCXX_HAVE_SLEEP"
set cond "defined _GLIBCXX_USE_NANOSLEEP"
set cond "$cond || defined _GLIBCXX_USE_WIN32_SLEEP"
set cond "$cond || defined _GLIBCXX_HAVE_SLEEP"
return [v3_check_preprocessor_condition sleep $cond]
}]
}
@ -1191,6 +1202,7 @@ proc check_v3_target_binary_io { } {
proc check_v3_target_nprocs { } {
return [check_v3_target_prop_cached et_nprocs {
set cond "defined _GLIBCXX_USE_GET_NPROCS"
set cond "$cond || defined _GLIBCXX_USE_GET_NPROCS_WIN32"
set cond "$cond || defined _GLIBCXX_USE_PTHREADS_NUM_PROCESSORS_NP"
set cond "$cond || defined _GLIBCXX_USE_SYSCTL_HW_NCPU"
set cond "$cond || defined _GLIBCXX_USE_SC_NPROCESSORS_ONLN"