Skip to content

Commit

Permalink
Reimplement GNU threads library on native Windows
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric Botcazou authored and jon-y committed Dec 23, 2022
1 parent 6a95f0e commit 9149a5b
Show file tree
Hide file tree
Showing 16 changed files with 743 additions and 517 deletions.
16 changes: 8 additions & 8 deletions libgcc/config.host
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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"
Expand Down
89 changes: 89 additions & 0 deletions libgcc/config/i386/gthr-win32-cond.c
Original file line number Diff line number Diff line change
@@ -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)
162 changes: 162 additions & 0 deletions libgcc/config/i386/gthr-win32-thread.c
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 9149a5b

Please sign in to comment.