+// sleep based timing loop.
+//
+// Calling sleep, even usleep() on linux is less accurate than
+// we like, but it does free up the cpu for other tasks during
+// the sleep so it is desirable. Because of the way sleep()
+// is implemented in consumer operating systems like windows
+// and linux, you almost always sleep a little longer than the
+// requested amount.
+//
+// To combat the problem of sleeping too long, we calculate the
+// desired wait time and shorten it by 2000us (2ms) to avoid
+// [hopefully] over-sleep'ing. The 2ms value was arrived at
+// via experimentation. We follow this up at the end with a
+// simple busy-wait loop to get the final pause timing exactly
+// right.
+//
+// Assuming we don't oversleep by more than 2000us, this
+// should be a reasonable compromise between sleep based
+// waiting, and busy waiting.
+//
+// Usually posix timer resolutions are low enough that we
+// could just leave this to the operating system today.
+// The day where the busy loop was introduced in flightgear,
+// the usual kernels still had just about 10ms (=HZ for
+// the timer tick) accuracy which is too bad to catch 60Hz...
+bool SGTimeStamp::sleepUntil(const SGTimeStamp& abstime)
+{
+#if defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS)
+ SGTimeStamp abstimeForSleep = abstime;
+
+ // Always undersleep by resolution of the clock
+ struct timespec ts;
+ if (-1 != clock_getres(getClockId(), &ts)) {
+ abstimeForSleep -= SGTimeStamp::fromSecNSec(ts.tv_sec, ts.tv_nsec);
+ } else {
+ abstimeForSleep -= SGTimeStamp::fromSecMSec(0, 2);
+ }
+
+ ts.tv_sec = abstimeForSleep._sec;
+ ts.tv_nsec = abstimeForSleep._nsec;
+ for (;;) {
+ int ret = clock_nanosleep(getClockId(), TIMER_ABSTIME, &ts, NULL);
+ if (-1 == ret && errno != EINTR)
+ return false;
+ if (ret == 0)
+ break;
+ }
+
+ // The busy loop for the rest
+ SGTimeStamp currentTime;
+ do {
+ currentTime.stamp();
+ } while (currentTime < abstime);
+
+ return true;
+
+#elif defined _WIN32
+
+ SGTimeStamp currentTime;
+ currentTime.stamp();
+ if (abstime <= currentTime)
+ return true;
+
+ SGTimeStamp abstimeForSleep = abstime - SGTimeStamp::fromSecMSec(0, 2);
+ for (;abstimeForSleep < currentTime;) {
+ SGTimeStamp timeDiff = abstimeForSleep - currentTime;
+ if (timeDiff < SGTimeStamp::fromSecMSec(0, 1))
+ break;
+ // Don't know, but may be win32 has something better today??
+ Sleep(static_cast<DWORD>(timeDiff.toMSecs()));
+
+ currentTime.stamp();
+ }
+
+ // Follow by a busy loop
+ while (currentTime < abstime) {
+ currentTime.stamp();
+ }
+
+ return true;
+
+#else
+
+ SGTimeStamp currentTime;
+ currentTime.stamp();
+ if (abstime <= currentTime)
+ return true;
+
+ SGTimeStamp abstimeForSleep = abstime - SGTimeStamp::fromSecMSec(0, 2);
+ for (;abstimeForSleep < currentTime;) {
+ SGTimeStamp timeDiff = abstimeForSleep - currentTime;
+ if (timeDiff < SGTimeStamp::fromSecUSec(0, 1))
+ break;
+ // Its documented that some systems bail out on usleep for longer than 1s
+ // since we recheck the current time anyway just wait for
+ // less than a second multiple times
+ bool truncated = false;
+ if (SGTimeStamp::fromSec(1) < timeDiff) {
+ timeDiff = SGTimeStamp::fromSec(1);
+ truncated = true;
+ }
+ int ret = usleep(static_cast<int>(timeDiff.toUSecs()));
+ if (-1 == ret && errno != EINTR)
+ return false;
+ if (ret == 0 && !truncated)
+ break;
+
+ currentTime.stamp();
+ }
+
+ // Follow by a busy loop
+ while (currentTime < abstime) {
+ currentTime.stamp();
+ }
+
+ return true;
+
+#endif
+}
+
+bool SGTimeStamp::sleepFor(const SGTimeStamp& reltime)
+{
+#if defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS)
+ struct timespec ts;
+ ts.tv_sec = reltime._sec;
+ ts.tv_nsec = reltime._nsec;
+ for (;;) {
+ struct timespec rem;
+ int ret = clock_nanosleep(getClockId(), 0, &ts, &rem);
+ if (-1 == ret && errno != EINTR)
+ return false;
+ if (ret == 0)
+ break;
+ // Use the remainder for the next cycle.
+ ts = rem;
+ }
+ return true;
+#elif defined _WIN32
+ if (reltime < SGTimeStamp::fromSecMSec(0, 1))
+ return true;
+ // Don't know, but may be win32 has something better today??
+ Sleep(static_cast<DWORD>(reltime.toMSecs()));
+ return true;
+#else
+ SGTimeStamp abstime;
+ abstime.stamp();
+ abstime += reltime;
+
+ SGTimeStamp currentTime;
+ currentTime.stamp();
+ for (;abstime < currentTime;) {
+ SGTimeStamp timeDiff = abstime - currentTime;
+ if (timeDiff < SGTimeStamp::fromSecUSec(0, 1))
+ break;
+ // Its documented that some systems bail out on usleep for longer than 1s
+ // since we recheck the current time anyway just wait for
+ // less than a second multiple times
+ bool truncated = false;
+ if (SGTimeStamp::fromSec(1) < timeDiff) {
+ timeDiff = SGTimeStamp::fromSec(1);
+ truncated = true;
+ }
+ int ret = usleep(static_cast<int>(timeDiff.toUSecs()));
+ if (-1 == ret && errno != EINTR)
+ return false;
+ if (ret == 0 && !truncated)
+ break;
+
+ currentTime.stamp();
+ }
+
+ return true;
+#endif
+}
+