Some WinAPI functions were introduced in recent versions of
Windows. We could detect whether to use the newer functions based on
the _WIN32_WINNT define. However, the issue is that _WIN32_WINNT
defaults to Win10 these days, so a default build of a toolchain won't
work for older targets, which is why it was agreed to do the change to
try to load this function at runtime.

https://sourceforge.net/p/mingw-w64/mailman/message/47371359/,
https://sourceforge.net/p/mingw-w64/mailman/message/49958237/ and
https://sourceforge.net/p/mingw-w64/mailman/message/48131379/.

Multiple workarounds are needed for MSVC to make this detection
portable.

1. Missing constructor attribute in MSVC

> The `constructor` attribute causes the function to be called before
> entering `main()`.

https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-constructor-function-attribute
https://clang.llvm.org/docs/AttributeReference.html#constructor

This feature is not supported by the MSVC compiler, and can be worked
around by putting a function pointer to the constructor in the
.CRT$XC? section of the executable.

https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170#linker-features-for-initialization

We chose the .CRT$XCT section, however the documentation warns:

> The names .CRT$XCT and .CRT$XCV aren't used by either the compiler
> or the CRT library right now, but there's no guarantee that they'll
> remain unused in the future. And, your variables could still be
> optimized away by the compiler. Consider the potential engineering,
> maintenance, and portability issues before adopting this technique.

2. Missing typeof extension in MSVC

As MSVC doesn't support the GCC __typeof__ extension, we explicit the
type of the function pointer.

Casting the result of GetProcAddress (a function pointer) to
a (void *) (a data pointer) is allowed as an extension, mimics the
POSIX dlsym function, and prevents compiler warnings.

3. Missing __atomic functions in MSVC

MSVC doesn't support GCC __atomic functions. Finding whether
GetSystemTimePreciseAsFileTime is present on the system can be done
using a function tagged with the constructor attribute, avoiding the
need of atomics to prevent races.

4. Gather the initialization bits into a single function

Considering the amount of boilerplate code needed to instruct the
linker on MSVC, gather the discovery of the WinAPI functions in a
single winpthreads_init function. The WinAPI functions can be accessed
through internal global function pointers.

Signed-off-by: Antonin Décimo <anto...@tarides.com>
---
 mingw-w64-libraries/winpthreads/src/clock.c | 31 ++------------
 mingw-w64-libraries/winpthreads/src/misc.c  | 45 +++++++++++++++++----
 mingw-w64-libraries/winpthreads/src/misc.h  |  2 +
 3 files changed, 43 insertions(+), 35 deletions(-)

diff --git a/mingw-w64-libraries/winpthreads/src/clock.c 
b/mingw-w64-libraries/winpthreads/src/clock.c
index db8b34ea5..954d845b1 100644
--- a/mingw-w64-libraries/winpthreads/src/clock.c
+++ b/mingw-w64-libraries/winpthreads/src/clock.c
@@ -13,6 +13,7 @@
 #endif
 #include "pthread.h"
 #include "pthread_time.h"
+#include "misc.h"
 
 #define POW10_7                 10000000
 #define POW10_9                 1000000000
@@ -31,32 +32,6 @@ static WINPTHREADS_INLINE int lc_set_errno(int result)
     return 0;
 }
 
-typedef void (WINAPI * GetSystemTimeAsFileTime_t)(LPFILETIME);
-static GetSystemTimeAsFileTime_t GetSystemTimeAsFileTime_p /* = 0 */;
-
-static GetSystemTimeAsFileTime_t try_load_GetSystemPreciseTimeAsFileTime(void)
-{
-    /* Use GetSystemTimePreciseAsFileTime() if available (Windows 8 or later) 
*/
-    HMODULE mod = GetModuleHandle("kernel32.dll");
-    GetSystemTimeAsFileTime_t get_time = NULL;
-    if (mod)
-        get_time = (GetSystemTimeAsFileTime_t)(intptr_t)GetProcAddress(mod,
-            "GetSystemTimePreciseAsFileTime"); /* <1us precision on Windows 10 
*/
-    if (get_time == NULL)
-        get_time = GetSystemTimeAsFileTime; /* >15ms precision on Windows 10 */
-    __atomic_store_n(&GetSystemTimeAsFileTime_p, get_time, __ATOMIC_RELAXED);
-    return get_time;
-}
-
-static WINPTHREADS_INLINE GetSystemTimeAsFileTime_t 
load_GetSystemTimeBestAsFileTime(void)
-{
-    GetSystemTimeAsFileTime_t get_time =
-        __atomic_load_n(&GetSystemTimeAsFileTime_p, __ATOMIC_RELAXED);
-    if (get_time == NULL)
-        get_time = try_load_GetSystemPreciseTimeAsFileTime();
-    return get_time;
-}
-
 /**
  * Get the resolution of the specified clock clock_id and
  * stores it in the struct timespec pointed to by res.
@@ -80,7 +55,7 @@ int clock_getres(clockid_t clock_id, struct timespec *res)
 {
     clockid_t id = clock_id;
 
-    if (id == CLOCK_REALTIME && load_GetSystemTimeBestAsFileTime() == 
GetSystemTimeAsFileTime)
+    if (id == CLOCK_REALTIME && _pthread_get_system_time_best_as_file_time == 
GetSystemTimeAsFileTime)
         id = CLOCK_REALTIME_COARSE; /* GetSystemTimePreciseAsFileTime() not 
available */
 
     switch(id) {
@@ -150,7 +125,7 @@ int clock_gettime(clockid_t clock_id, struct timespec *tp)
     switch(clock_id) {
     case CLOCK_REALTIME:
         {
-            load_GetSystemTimeBestAsFileTime()(&ct.ft);
+            _pthread_get_system_time_best_as_file_time(&ct.ft);
             t = ct.u64 - DELTA_EPOCH_IN_100NS;
             tp->tv_sec = t / POW10_7;
             tp->tv_nsec = ((int) (t % POW10_7)) * 100;
diff --git a/mingw-w64-libraries/winpthreads/src/misc.c 
b/mingw-w64-libraries/winpthreads/src/misc.c
index 83caf262f..62acb9f50 100644
--- a/mingw-w64-libraries/winpthreads/src/misc.c
+++ b/mingw-w64-libraries/winpthreads/src/misc.c
@@ -24,15 +24,46 @@
 #include "pthread.h"
 #include "misc.h"
 
-static ULONGLONG (*GetTickCount64FuncPtr) (VOID);
+void (WINAPI *_pthread_get_system_time_best_as_file_time) (LPFILETIME) = NULL;
+static ULONGLONG (WINAPI *_pthread_get_tick_count_64) (VOID);
 
-static void __attribute__((constructor)) ctor (void)
+#if defined(__GNUC__) || defined(__clang__)
+__attribute__((constructor))
+#endif
+static void winpthreads_init(void)
 {
-  HMODULE mod = GetModuleHandle("kernel32.dll");
-  if (mod)
-    GetTickCount64FuncPtr = (__typeof__(GetTickCount64FuncPtr)) 
GetProcAddress(mod, "GetTickCount64");
+    HMODULE mod = GetModuleHandle("kernel32.dll");
+    if (!mod) return;
+
+    _pthread_get_tick_count_64 =
+        (ULONGLONG (WINAPI *)(VOID))(void*) GetProcAddress(mod, 
"GetTickCount64");
+
+    /* <1us precision on Windows 10 */
+    _pthread_get_system_time_best_as_file_time =
+        (void (WINAPI *)(LPFILETIME))(void*) GetProcAddress(mod, 
"GetSystemTimePreciseAsFileTime");
+
+    if (!_pthread_get_system_time_best_as_file_time)
+        /* >15ms precision on Windows 10 */
+        _pthread_get_system_time_best_as_file_time = GetSystemTimeAsFileTime;
 }
 
+#if defined(_MSC_VER) && !defined(__clang__)
+/* Force a reference to __xc_t to prevent whole program optimization
+ * from discarding the variable. */
+
+/* On x86, symbols are prefixed with an underscore. */
+# if defined(_M_IX86)
+#   pragma comment(linker, "/include:___xc_t")
+# else
+#   pragma comment(linker, "/include:__xc_t")
+# endif
+
+#pragma section(".CRT$XCT", long, read)
+__declspec(allocate(".CRT$XCT"))
+extern const _PVFV __xc_t;
+const _PVFV __xc_t = winpthreads_init;
+#endif
+
 unsigned long long _pthread_time_in_ms(void)
 {
     FILETIME ft;
@@ -64,8 +95,8 @@ unsigned long long _pthread_rel_time_in_ms(const struct 
timespec *ts)
 static unsigned long long
 _pthread_get_tick_count (long long *frequency)
 {
-  if (GetTickCount64FuncPtr != NULL)
-    return GetTickCount64FuncPtr ();
+  if (_pthread_get_tick_count_64 != NULL)
+    return _pthread_get_tick_count_64 ();
 
   LARGE_INTEGER freq, timestamp;
 
diff --git a/mingw-w64-libraries/winpthreads/src/misc.h 
b/mingw-w64-libraries/winpthreads/src/misc.h
index eb0f7f2b6..edefb0d54 100644
--- a/mingw-w64-libraries/winpthreads/src/misc.h
+++ b/mingw-w64-libraries/winpthreads/src/misc.h
@@ -107,6 +107,8 @@ unsigned long long _pthread_rel_time_in_ms(const struct 
timespec *ts);
 unsigned long _pthread_wait_for_single_object (void *handle, unsigned long 
timeout);
 unsigned long _pthread_wait_for_multiple_objects (unsigned long count, void 
**handles, unsigned int all, unsigned long timeout);
 
+extern void (WINAPI *_pthread_get_system_time_best_as_file_time) (LPFILETIME);
+
 #if defined(__GNUC__) || defined(__clang__)
 #define likely(cond) __builtin_expect((cond) != 0, 1)
 #define unlikely(cond) __builtin_expect((cond) != 0, 0)
-- 
2.43.0



_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to