Hi Bruno,

On 5/24/24 4:44 AM, Bruno Haible wrote:
> The longer the API is available, the better. Gnulib supports
> Windows XP as the minimum. For modules used by Emacs ('boot-time' in
> particular) it should run even on Windows 2000.
> 
> Which means that the code needs to fetch a pointer to the particular
> Windows API function at runtime, through GetProcAddress. See lib/isatty.c
> as an example.
> 
> Would you like to give it a try?

I wrote this patch just now. Any thoughts?

I've only tested it with a mingw compiler and wine so far.

I wasn't sure the proper way to get the current time. I didn't want to
depend on timespec_get since that would bring in more dependencies for
non-Windows systems (like linking with -lrt). The resolution isn't
needed either since GetTickCount64 has a resolution of 10-16 ms [1].

I suppose a Native Windows function would do fine, but I am not
familiar with the API. I presume that is the case for most others too.
Therefore I think gettimeofday makes sense since most systems should
just have it.

[1] 
https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount64#remarks

Collin
From acd118c0057f206f212cb45000aa852cfb083af6 Mon Sep 17 00:00:00 2001
From: Collin Funk <collin.fu...@gmail.com>
Date: Fri, 24 May 2024 14:07:26 -0700
Subject: [PATCH] boot-time: Add a fallback function for Windows.

* lib/boot-time-aux.h (initialize, get_windows_boot_time_fallback): New
functions.
* lib/boot-time.c [_WIN32]: Include Windows headers and sys/time.h.
(get_boot_time_uncached): Use the new Windows fallback.
* modules/boot-time (Depends-on): Add gettimeofday.
---
 ChangeLog           |  9 ++++++
 lib/boot-time-aux.h | 72 +++++++++++++++++++++++++++++++++++++++++++++
 lib/boot-time.c     |  9 ++++++
 modules/boot-time   |  1 +
 4 files changed, 91 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index d9112a810a..b26fe146f4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2024-05-24  Collin Funk  <collin.fu...@gmail.com>
+
+	boot-time: Add a fallback function for Windows.
+	* lib/boot-time-aux.h (initialize, get_windows_boot_time_fallback): New
+	functions.
+	* lib/boot-time.c [_WIN32]: Include Windows headers and sys/time.h.
+	(get_boot_time_uncached): Use the new Windows fallback.
+	* modules/boot-time (Depends-on): Add gettimeofday.
+
 2024-05-24  Bruno Haible  <br...@clisp.org>
 
 	putenv tests: Put the putenv() argument strings into writable memory.
diff --git a/lib/boot-time-aux.h b/lib/boot-time-aux.h
index b1add30239..e51c58ff6f 100644
--- a/lib/boot-time-aux.h
+++ b/lib/boot-time-aux.h
@@ -345,4 +345,76 @@ get_windows_boot_time (struct timespec *p_boot_time)
   return -1;
 }
 
+# if !(_WIN32_WINNT >= _WIN32_WINNT_VISTA)
+
+/* Don't assume that UNICODE is not defined.  */
+#  undef LoadLibrary
+#  define LoadLibrary LoadLibraryA
+
+/* Avoid warnings from gcc -Wcast-function-type.  */
+#  define GetProcAddress \
+    (void *) GetProcAddress
+
+/* GetTickCount64 is only available on Windows Vista and later.  */
+typedef ULONGLONG (WINAPI * GetTickCount64FuncType) (void);
+
+static GetTickCount64FuncType GetTickCount64Func = NULL;
+static BOOL initialized = FALSE;
+
+static void
+initialize (void)
+{
+  HMODULE kernel32 = LoadLibrary ("kernel32.dll");
+  if (kernel32 != NULL)
+    {
+      GetTickCount64Func =
+        (GetTickCount64FuncType) GetProcAddress (kernel32, "GetTickCount64");
+    }
+  initialized = TRUE;
+}
+
+# else
+
+#  define GetTickCount64Func GetTickCount64
+
+# endif
+
+/* Fallback for Windows in the form:
+     boot time = current time - uptime
+   This uses the GetTickCount64 function which is only available on Windows
+   Vista and later. See:
+   <https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount64>.  */
+static int
+get_windows_boot_time_fallback (struct timespec *p_boot_time)
+{
+# if !(_WIN32_WINNT >= _WIN32_WINNT_VISTA)
+  if (! initialized)
+    initialize ();
+#endif
+  if (GetTickCount64Func != NULL)
+    {
+      ULONGLONG uptime_ms = GetTickCount64Func ();
+      struct timespec uptime;
+      struct timespec result;
+      struct timeval tv;
+      if (gettimeofday (&tv, NULL) >= 0)
+        {
+          uptime.tv_sec = uptime_ms / 1000;
+          uptime.tv_nsec = (uptime_ms % 1000) * 1000000;
+          result.tv_sec = tv.tv_sec;
+          result.tv_nsec = tv.tv_usec * 1000;
+          if (result.tv_nsec < uptime.tv_nsec)
+            {
+              result.tv_nsec += 1000000000;
+              result.tv_sec -= 1;
+            }
+          result.tv_sec -= uptime.tv_sec;
+          result.tv_nsec -= uptime.tv_nsec;
+          *p_boot_time = result;
+          return 0;
+        }
+    }
+  return -1;
+}
+
 #endif
diff --git a/lib/boot-time.c b/lib/boot-time.c
index c1171e8024..c0a8ea6903 100644
--- a/lib/boot-time.c
+++ b/lib/boot-time.c
@@ -43,6 +43,13 @@
 # include <OS.h>
 #endif
 
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# include <sysinfoapi.h>
+# include <sys/time.h>
+#endif
+
 #include "idx.h"
 #include "readutmp.h"
 #include "stat-time.h"
@@ -247,6 +254,8 @@ get_boot_time_uncached (struct timespec *p_boot_time)
     {
       /* Workaround for Windows:  */
       get_windows_boot_time (&found_boot_time);
+      if (found_boot_time.tv_sec == 0)
+        get_windows_boot_time_fallback (&found_boot_time);
     }
 # endif
 
diff --git a/modules/boot-time b/modules/boot-time
index 2d89969d5c..07e5e43bc6 100644
--- a/modules/boot-time
+++ b/modules/boot-time
@@ -12,6 +12,7 @@ Depends-on:
 extensions
 idx
 stat-time
+gettimeofday
 stdbool
 time-h
 unlocked-io-internal
-- 
2.45.1

Reply via email to