use POSIX feature macro to check for clock_gettime#348
use POSIX feature macro to check for clock_gettime#348
Conversation
note: while _POSIX_TIMERS is "directly" defined by including time.h on some systems, it may only be done if the internal macros are defined, which is commonly done by including unistd.h, so that is done before tested to work on Debian and MSYS2 (gcc + clang) fixes #345 (C part)
|
ping @Bill-Gray for review/merge |
|
I just tried building WinGUI and WinCon, cross-compiling on my Linux box. For both, I got : So... compiled without trouble, but couldn't link. Presumably, there's something a little more subtle about when the various timing functions are available to us... any ideas? I also built with Digital Mars and OpenWATCOM 1.9, each for DOS and WinCon, and it all worked and the resulting I also compiled everything with both of those compilers for OS/2. I mention this just for completeness, since I don't actually have an OS/2-capable machine on which to test the results. I got build errors with DMC, but I've had those before (don't know why); it's not related to the current issue. With OpenWATCOM, everything compiled without warnings or errors. So I think if we can figure out a fix for the above error message, we'd be good. |
|
For clock_gettime we'd nee to add -lrt (for glibc only necessary before 2.17, see https://www.man7.org/linux/man-pages/man3/clock_gettime.3.html - but likely no problem to do so in any case), no? Can you please test if adding it to the Makefile(s) works for your other build environments? |
|
Sorry, should have just given this a go right away... especially since the only change required on my machine was adding Unfortunately, it didn't work, because (with my MinGW version 9.3-win32 20200320) /* only newer MinGW environments have clock_gettime and
those have CLOCK_REALTIME as a macro */So the question becomes : how do we reliably determine that a given version of MinGW actually has It appears that there are portability problems with
This is a little puzzling to me, since it seems to say that the function is missing for MinGW, and it also doesn't work. Seems to me it should be one or the other. (Maybe they mean "missing on old MinGW and broken on newer MinGW"?) The implication is that we need to initialize The following patch, which simply says "don't use diff --git a/pdcurses/getch.c b/pdcurses/getch.c
index 78d0dbbb..a19ac8e5 100644
--- a/pdcurses/getch.c
+++ b/pdcurses/getch.c
@@ -419,9 +419,8 @@ use clock_gettime() or gettimeofday() when available. */
#endif
#if defined( _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 199309L) \
- && (!defined( __MINGW32__) || defined( CLOCK_REALTIME))
- /* only newer MinGW environments have clock_gettime and
- those have CLOCK_REALTIME as a macro */
+ && !defined( __MINGW32__)
+ /* only newer MinGW environments have clock_gettime */
#define HAVE_CLOCK_GETTIME
#elif defined( _DEFAULT_SOURCE) || defined( _BSD_SOURCE) \
|| defined( __FreeBSD__) || defined( __MINGW32__)However (as it says just below that patch) "POSIX.1-2008 marks So ideally, we'd find out with which version of MinGW the |
That's... strange. can you run what we effectively do with this PR please?
? Note: we still may have to add |
|
Figured it out... So I am reasonably confident that your fix will work (works here, anyway). I am just sufficiently worried by these problems that I'd rather hold off on having it in a release version, though. The other possibility (maybe safer) is to say that we only use I'm going to go ahead with the release "as is" (basically just change version constants and, in |
|
Sounds good to me. |
|
friendly ping @Bill-Gray "shortly after the release" seems to be ... not much later ;-) |
|
another ping @Bill-Gray :-) |
|
I returned to this tonight, and... I think we might want to try something completely different. I wrestled with ensuring that Specifically, that API provides a It does return the time in a manner that is a little odd for our purposes (described in comments in the patch). A little arithmetic trickery is required to get it to work in situations where we don't have 64-bit integers (for example, Digital Mars). You can click here for the modified diff --git a/pdcurses/getch.c b/pdcurses/getch.c
index fb3a306a..d20ca2a0 100644
--- a/pdcurses/getch.c
+++ b/pdcurses/getch.c
@@ -407,6 +407,67 @@ static int _mouse_key(void)
return key;
}
+#ifdef _WIN32
+#undef MOUSE_MOVED
+#include <windows.h>
+
+/* GetSystemTimeAsFileTime( ) returns the time in units of 0.1 microsecond.
+We want milliseconds, i.e., one ten-thousandth of that. Also, it returns
+the time as a 64-bit integer, spread across two unsigned 32-bit ints. So
+the time is given as
+
+decimicroseconds = A * 2^32 + B (A=high 32-bit word, B=low)
+
+ If 64-bit long integers are supported, we can just shift A, add B,
+cast to a long, and divide by 10000. On older systems/compilers,
+further logic is needed. We actually want
+
+milliseconds = (A * 2^32 + B) / 10000 (note : 2^32 = 4294967296)
+
+ With integer division (i.e., truncation),
+
+A = 10000 * (A / 10000) + (A % 10000)
+
+milliseconds =
+ 10000 * 2^32 * (A/10000) + ((A % 10000) * 2^32 + B) / 10000
+ = 2^32 * (A/10000) + ((A % 10000) * 2^32 + B) / 10000
+ < High word of result> <-- Low word of result --------->
+
+ Since we're only doing this for 32-bit systems, we ignore the high
+word. (This does mean our count overflows every 49.7 days. Care must
+be taken.)
+
+rval = ((A % 10000) * 4294967296 + B) / 10000
+ = ((A % 10000) * 4294960000 + (A % 10000) * 7296 + B) / 10000
+ = 429496 * (A % 10000) + ((A % 10000) * 7296 + B) / 10000
+
+ The addition within the last bit could overflow. Fortunately, 10000 and
+7296 are both multiples of 16; reducing the fraction gets us :
+
+ = 429496 * (A % 10000) + ((A % 10000) * 456 + B >> 4) / 625
+*/
+
+long PDC_millisecs( void)
+{
+ FILETIME ft;
+ long rval;
+#if LONG_MAX < 9223372036854775807L
+ DWORD tval;
+
+ GetSystemTimeAsFileTime( &ft);
+ tval = ft.dwHighDateTime % 10000u;
+ rval = 429496L * tval + (456L * tval + (long)(ft.dwLowDateTime >> 4)) / 625L;
+#else
+ uint64_t tval;
+
+ GetSystemTimeAsFileTime( &ft);
+ tval = ((uint64_t)ft.dwHighDateTime << 32) | (uint64_t)ft.dwLowDateTime;
+ rval = (long)( tval / 10000u);
+#endif
+ return( rval);
+}
+#else /* Non-Microsoft Windows cases */
+
/* ftime() is considered obsolete. But it's all we have for
millisecond precision on older compilers/systems. We'll
use clock_gettime() or gettimeofday() when available. */
@@ -460,6 +521,7 @@ long PDC_millisecs( void)
return( (long)t.time * 1000L + (long)t.millitm);
}
#endif
+#endif /* #ifndef _WIN32 */
/* On many systems, checking for a key hit is quite slow. If
PDC_check_key( ) returns FALSE, we can safely stop checking for |
|
Interesting. If we can get away with only a subset for 64 bit integers - let's add a check of INT_MAX or similar to limit the calculation in this case. Note: After some back and forth we added the following logic and compatibility notes to GnuCOBOL: #if defined (_MSC_VER)
/* Get function pointer for most precise time function
GetSystemTimePreciseAsFileTime is available since OS-version Windows 2000
GetSystemTimeAsFileTime is available since OS-version Windows 8 / Server 2012
*/
static VOID (WINAPI *time_as_filetime_func) (LPFILETIME) = NULL;
static void
get_function_ptr_for_precise_time (void)
{
HMODULE kernel32_handle;
kernel32_handle = GetModuleHandle (TEXT ("kernel32.dll"));
if (kernel32_handle != NULL) {
time_as_filetime_func = (VOID (WINAPI *) (LPFILETIME))
GetProcAddress (kernel32_handle, "GetSystemTimePreciseAsFileTime");
}
if (time_as_filetime_func == NULL) {
time_as_filetime_func = GetSystemTimeAsFileTime;
}
}
#endif
[...]
/* Get nanoseconds with highest precision possible */
#if defined (_MSC_VER)
if (!time_as_filetime_func) {
get_function_ptr_for_precise_time ();
}
#pragma warning(suppress: 6011) /* the function pointer is always set by get_function_ptr_for_precise_time */
(time_as_filetime_func) (&filetime);
/* fallback to GetLocalTime if one of the following does not work */
if (FileTimeToSystemTime (&filetime, &utc_time)
&& SystemTimeToTzSpecificLocalTime (NULL, &utc_time, &local_time)) {
set_cob_time_ns_from_filetime (filetime, &cb_time);
return cb_time;
}
#endif
GetLocalTime (&local_time);
cb_time.nanosecond = local_time.wMilliseconds * 1000000;
return cb_time;but from the code flow and your test result the compatibility comment seems wrong and should be the following instead - do you agree? |
|
@GitMensch - an interesting thought, and I do have some projects where nanosecond accuracy might be useful. For this, though, we're looking for time to milliseconds. We're already tossing out four digits; Yes, the "imprecise" function definitely goes back at least as far as WinME. I've sometimes found it a little difficult to learn exactly when a Windows API call was added; Microsoft's documentation doesn't go back before Win2000. (I can understand them wanting everyone to forget that WinME ever existed.) But I did find a site that says that However... there may be a still better way, involving use of the GetTickCount() function. That would cause the code to contract to the following almost trivial patch. It's not as arithmetically interesting, but it's a lot simpler. And it, too, works for Win95. diff --git a/pdcurses/getch.c b/pdcurses/getch.c
index 78d0dbbb..28ff1d37 100644
--- a/pdcurses/getch.c
+++ b/pdcurses/getch.c
@@ -407,6 +407,15 @@ static int _mouse_key(void)
return key;
}
+#ifdef _WIN32
+#include <windows.h>
+
+long PDC_millisecs( void)
+{
+ return( (long)GetTickCount( ));
+}
+#else
+
/* ftime() is considered obsolete. But it's all we have for
millisecond precision on older compilers/systems. We'll
use clock_gettime() or gettimeofday() when available. */
@@ -460,6 +469,7 @@ long PDC_millisecs( void)
return( (long)t.time * 1000L + (long)t.millitm);
}
#endif
+#endif /* #ifndef _WIN32 */
```patch |
|
The patch is too trivial as it needs to cater for overflow between calls. |
Well, we have to do that anyway on any system where long integers are four bytes. And I was going to tell you that the code already does that correctly; the trick is to treat However, it's a little more complicated than that, and there are oddities depending on whether long integers are 64 or 32 bits. Blinking of text and the cursor can be disabled at the overflow point, and (less likely) if you pressed the mouse just before the overflow time and released it afterward, the press and release might not be properly combined into a click on some platforms. Shouldn't be too difficult to fix, though. |
…n PDC_millisecs(). This addresses pull request #348 and issue #345. Standard timekeeping functions are poorly and unevenly supported on MS Windows; it seems simplest just to use the Windows API functions. The function in question returns the time in units of 0.1 microseconds, in a 64-bit integer that is broken into two 32-bit integers (DWORDs, in Microsoftese). On 64-bit systems, we shift the two halves into a single 64-bit integer and divide by 10000. If 64-bit long integers are not available, some 32-bit math must be done to simulate the division and compute the lower 32 bits of the result. If A is the high 32 bits and B the low 32 bits, then decimicrosecs = A * 2^32 + B millisecs = (A * 2^32 + B) / 10000 Since integer division truncates, A = 10000 * (A/10000) + (A%10000), ms = (10000*(A/10000) * 2^32 + (A%10000) + 2^32 + B) / 10000 = 2^32 * (A/10000) + ((A % 10000) * 2^32 + B) / 10000 < High word result> <Low word of result... part we want> Tossing that high word of the result, our return value is rval = ((A % 10000) * 2^32 + B) / 10000 except that would overflow. Using 2^32 = 4294967296, rval = 429496 * (A % 10000) + ((A % 10000) * 7296 + B) / 10000 The addition within the last part could still overflow. However, 7296 and 10000 have a common factor of 16; division gets us rval = 429496 * (A % 10000) + ((A % 10000) * 456 + B >> 4) / 625
|
Okay, I think we've got this one fixed. Three platforms using I considered modified use of long PDC_millisecs( void)
{
static long rval = 0;
static DWORD prev_t = 0;
const DWORD t = GetTickCount( );
if( rval || prev_t)
rval += (long)( t - prev_t);
prev_t = t;
return( rval);
}However... we've got the Once the AppVeyor builds complete successfully, I'll close this and issue #345. |
note: while _POSIX_TIMERS may be "indirectly" defined by including time.h on some systems, they come from unistd.h.
tested to work on Debian and MSYS2 (gcc + clang)
fixes #345 (C part)
to be checked:
Is there an environment where this does not work (it seems that on non-released mingwrt we do have the feature with clock_gettime since Dec 2017 but no posix-feature-macros at all; not sure if this is something to care for)?
Should we apply something similar to other places? See https://linux.die.net/man/7/posixoptions.