--- /dev/null
+//
+// Monotonic clock API for CUPS.
+//
+// Copyright © 2024 by OpenPrinting.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#include "cups-private.h"
+
+
+//
+// Local globals...
+//
+
+static bool cups_clock_init = false;// Clock initialized?
+static cups_mutex_t cups_clock_mutex = CUPS_MUTEX_INITIALIZER;
+ // Mutex to control access
+#ifdef _WIN32
+static ULONGLONG cups_first_tick; // First tick count
+#else
+# ifdef CLOCK_MONOTONIC
+static struct timespec cups_first_clock;// First clock value
+# endif // CLOCK_MONOTONIC
+static struct timeval cups_first_time; // First time value
+#endif // _WIN32
+
+
+//
+// 'cupsGetClock()' - Get a monotonic clock value in seconds.
+//
+// This function returns a monotonically increasing clock value in seconds. The
+// first call will always return 0.0. Subsequent calls will return the number
+// of seconds that have elapsed since the first call, regardless of system time
+// changes, sleep, etc. The sub-second accuracy varies based on the operating
+// system and hardware but is typically 10ms or better.
+//
+// @since CUPS 2.5@
+//
+
+double // O - Elapsed seconds
+cupsGetClock(void)
+{
+ double secs; // Elapsed seconds
+#ifdef _WIN32
+ ULONGLONG curtick; // Current tick count
+#else
+# ifdef CLOCK_MONOTONIC
+ struct timespec curclock; // Current clock value
+# endif // CLOCK_MONOTONIC
+ struct timeval curtime; // Current time value
+#endif // _WIN32
+
+
+ cupsMutexLock(&cups_clock_mutex);
+
+#ifdef _WIN32
+ // Get the current tick count in milliseconds...
+ curtick = GetTickCount64();
+
+ if (!cups_clock_init)
+ {
+ // First time through initialize the initial tick count...
+ cups_clock_init = true;
+ cups_first_tick = curtick;
+ }
+
+ // Convert ticks to seconds...
+ if (curtick < cups_first_tick)
+ secs = 0.0;
+ else
+ secs = 0.001 * (curtick - cups_first_tick);
+
+#else
+# ifdef CLOCK_MONOTONIC
+ // Get the current tick count in milliseconds...
+ if (!clock_gettime(CLOCK_MONOTONIC, &curclock))
+ {
+ if (!cups_clock_init)
+ {
+ // First time through initialize the initial clock value...
+ cups_clock_init = true;
+ cups_first_clock = curclock;
+ }
+
+ // Convert clock value to seconds...
+ if ((secs = curclock.tv_sec - cups_first_clock.tv_sec + 0.000000001 * (curclock.tv_nsec - cups_first_clock.tv_nsec)) < 0.0)
+ secs = 0.0;
+ }
+ else
+# endif // CLOCK_MONOTONIC
+ {
+ gettimeofday(&curtime, /*tzp*/NULL);
+
+ if (!cups_clock_init)
+ {
+ // First time through initialize the initial clock value...
+ cups_clock_init = true;
+ cups_first_time = curtime;
+ }
+
+ // Convert time value to seconds...
+ if ((secs = curtime.tv_sec - cups_first_time.tv_sec + 0.000001 * (curtime.tv_usec - cups_first_time.tv_usec)) < 0.0)
+ secs = 0.0;
+ }
+#endif // _WIN32
+
+ cupsMutexUnlock(&cups_clock_mutex);
+
+ return (secs);
+}
--- /dev/null
+//
+// Monotonic clock test program for CUPS.
+//
+// Copyright © 2024 by OpenPrinting.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#include "cups.h"
+#include "test-internal.h"
+#include <math.h>
+
+
+//
+// 'main()' - Main entry for clock tests.
+//
+
+int // O - Exit status
+main(void)
+{
+ double current; // Current time
+
+
+ // Test clock values at 0, 1, 5, 10, and 30 seconds
+ testBegin("cupsGetClock(initial)");
+ current = cupsGetClock();
+ if (current == 0.0)
+ testEndMessage(true, "%g", current);
+ else
+ testEndMessage(false, "got %g, expected 0.0", current);
+
+ sleep(1);
+
+ testBegin("cupsGetClock(1 second)");
+ current = cupsGetClock();
+ if (fabs(current - 1.0) < 0.1)
+ testEndMessage(true, "%g", current);
+ else
+ testEndMessage(false, "got %g, expected 1.0 +/- 0.1", current);
+
+ sleep(4);
+
+ testBegin("cupsGetClock(5 seconds)");
+ current = cupsGetClock();
+ if (fabs(current - 5.0) < 0.1)
+ testEndMessage(true, "%g", current);
+ else
+ testEndMessage(false, "got %g, expected 5.0 +/- 0.1", current);
+
+ sleep(5);
+
+ testBegin("cupsGetClock(10 seconds)");
+ current = cupsGetClock();
+ if (fabs(current - 10.0) < 0.1)
+ testEndMessage(true, "%g", current);
+ else
+ testEndMessage(false, "got %g, expected 10.0 +/- 0.1", current);
+
+ sleep(20);
+
+ testBegin("cupsGetClock(30 seconds)");
+ current = cupsGetClock();
+ if (fabs(current - 30.0) < 0.1)
+ testEndMessage(true, "%g", current);
+ else
+ testEndMessage(false, "got %g, expected 30.0 +/- 0.1", current);
+
+ sleep(30);
+
+ testBegin("cupsGetClock(60 seconds)");
+ current = cupsGetClock();
+ if (fabs(current - 60.0) < 0.1)
+ testEndMessage(true, "%g", current);
+ else
+ testEndMessage(false, "got %g, expected 60.0 +/- 0.1", current);
+
+ sleep(60);
+
+ testBegin("cupsGetClock(120 seconds)");
+ current = cupsGetClock();
+ if (fabs(current - 120.0) < 0.1)
+ testEndMessage(true, "%g", current);
+ else
+ testEndMessage(false, "got %g, expected 120.0 +/- 0.1", current);
+
+ return (testsPassed ? 0 : 1);
+}