AJA NTV2 SDK  17.5.0.1242
NTV2 SDK 17.5.0.1242
systemtime.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
11 #include "ajabase/common/types.h"
12 #if defined(AJA_COLLECT_SLEEP_STATS)
13  #include "ajabase/common/timer.h"
14  #include "ajabase/system/atomic.h"
15  #include "ajabase/system/thread.h"
16  #include <sstream>
17 #endif // defined(AJA_COLLECT_SLEEP_STATS)
18 
19 #if defined(AJA_MAC)
20  #include <mach/mach_time.h>
21  #include <CoreServices/CoreServices.h>
22  static int64_t s_PerformanceFrequency;
23  static bool s_bPerformanceInit = false;
24 #endif
25 #if defined(AJA_SLEEP_USE_STL) || defined(AJA_SYSCLK_USE_STL)
26  #include <chrono>
27  #include <thread>
28 #endif // AJA_SLEEP_USE_STL || AJA_SYSCLK_USE_STL
29 
30 #if defined(AJA_WINDOWS)
31  #include "timeapi.h"
32  static LARGE_INTEGER s_PerformanceFrequency;
33  static bool s_bPerformanceInit = false;
34 
35  // From OBS utils
36  #if defined(_MSC_VER) && defined(_M_X64)
37  #include <intrin.h>
38  #endif
39  static inline uint64_t util_mul_div64(uint64_t num, uint64_t mul, uint64_t div)
40  {
41  #if defined(_MSC_VER) && defined(_M_X64) && (_MSC_VER >= 1920)
42  unsigned __int64 high;
43  const unsigned __int64 low = _umul128(num, mul, &high);
44  unsigned __int64 rem;
45  return _udiv128(high, low, div, &rem);
46  #else
47  const uint64_t rem = num % div;
48  return (num / div) * mul + (rem * mul) / div;
49  #endif
50  }
51 
52 #elif defined(AJA_LINUX)
53  #include <unistd.h>
54  #if (_POSIX_TIMERS > 0)
55  #ifdef _POSIX_MONOTONIC_CLOCK
56  #define AJA_USE_CLOCK_GETTIME
57  #else
58  #undef AJA_USE_CLOCK_GETTIME
59  #endif
60  #endif
61 
62  #ifdef AJA_USE_CLOCK_GETTIME
63  #include <time.h>
64  #else
65  // Use gettimeofday - this is not really desirable
66  #include <sys/time.h>
67  #endif
68 
69 #elif defined(AJA_BAREMETAL)
70  #include <unistd.h>
71 #endif
72 
73 
74 int64_t AJATime::GetSystemTime (void)
75 {
76  // system dependent time function
77 #if defined(AJA_SYSCLK_USE_STL)
78  ** NEED STL IMPL **
79 #elif defined(AJA_WINDOWS)
80  return (int64_t)::timeGetTime();
81 #elif defined(AJA_MAC)
82  static mach_timebase_info_data_t sTimebaseInfo;
83  uint64_t ticks = ::mach_absolute_time();
84 
85  if ( sTimebaseInfo.denom == 0 )
86  {
87  (void) mach_timebase_info(&sTimebaseInfo);
88  }
89 
90  // Do the maths. We hope that the multiplication doesn't
91  // overflow; the price you pay for working in fixed point.
92  int64_t nanoSeconds = ticks * sTimebaseInfo.numer / sTimebaseInfo.denom;
93  return nanoSeconds;
94 #elif defined(AJA_LINUX)
95  #ifdef AJA_USE_CLOCK_GETTIME
96  struct timespec ts;
98  return (ts.tv_sec * ((int64_t)1000)) + (ts.tv_nsec / (int64_t)1000000);
99  #else
100  struct timeval tv;
101  struct timezone tz;
102 
103  gettimeofday( &tv, &tz );
104  return (int64_t)((tv.tv_sec * ((int64_t)1000)) + (int64_t)(tv.tv_usec / 1000));
105  #endif
106 #else
107  return 0;
108 #endif
109 }
110 
111 
113 {
114 #if defined(AJA_SYSCLK_USE_STL)
115  ** NEED STL IMPL **
116 #elif defined(AJA_WINDOWS)
117  LARGE_INTEGER performanceCounter;
118 
119  performanceCounter.QuadPart = 0;
120  if (!QueryPerformanceCounter(&performanceCounter))
121  {
122  return 0;
123  }
124 
125  return (int64_t)performanceCounter.QuadPart;
126 #elif defined(AJA_MAC)
127  // Issue 846: mach_absolute_time doesn't have nanosec resolution on ARM hardware.
128  // Need to use clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW), but need to
129  // also change GetSystemFrequency.
130  return int64_t(mach_absolute_time());
131 #elif defined(AJA_LINUX)
132  #ifdef AJA_USE_CLOCK_GETTIME
133  struct timespec ts;
135  return (ts.tv_sec * ((int64_t)1000000000)) + (ts.tv_nsec);
136  #else
137  struct timeval tv;
138  struct timezone tz;
139 
140  gettimeofday( &tv, &tz );
141  return (int64_t)((int64_t)tv.tv_sec * (int64_t)1000000 + tv.tv_usec);
142  #endif
143 #else
144  return 0;
145 #endif
146 }
147 
148 
150 {
151 #if defined(AJA_SYSCLK_USE_STL)
152  ** NEED STL IMPL **
153 #elif defined(AJA_WINDOWS)
154  if (!s_bPerformanceInit)
155  {
156  QueryPerformanceFrequency(&s_PerformanceFrequency);
157  s_bPerformanceInit = true;
158  }
159  return (int64_t)s_PerformanceFrequency.QuadPart;
160 #elif defined(AJA_MAC)
161  if (!s_bPerformanceInit)
162  {
163  // 1 billion ticks approximately equals 1 sec on a Mac
164  static mach_timebase_info_data_t sTimebaseInfo;
165  uint64_t ticks = 1000000000;
166 
167  if ( sTimebaseInfo.denom == 0 )
168  {
169  (void) mach_timebase_info(&sTimebaseInfo);
170  }
171 
172  // Do the maths. We hope that the multiplication doesn't
173  // overflow; the price you pay for working in fixed point.
174  int64_t nanoSeconds = ticks * sTimebaseInfo.numer / sTimebaseInfo.denom;
175 
176  // system frequency - ticks per second units
177  s_PerformanceFrequency = ticks * 1000000000 / nanoSeconds;
178  s_bPerformanceInit = true;
179  }
180  return s_PerformanceFrequency;
181 #elif defined(AJA_LINUX)
182  #ifdef AJA_USE_CLOCK_GETTIME
183  return 1000000000;
184  #else
185  return 1000000;
186  #endif
187 #else
188  return 0;
189 #endif
190 }
191 
192 
194 {
195  double ticks = double(GetSystemCounter());
196  double ticksPerSecond = double(GetSystemFrequency());
197  double sec = 0.0;
198  if (ticksPerSecond)
199  {
200  sec = ticks / ticksPerSecond;
201  }
202  return sec;
203 }
204 
205 
207 {
208  uint64_t ticks = GetSystemCounter();
209  uint64_t ticksPerSecond = GetSystemFrequency();
210  uint64_t ms = 0;
211  if (ticksPerSecond)
212  {
213  // floats are being used here to avoid the issue of overflow
214  // or inaccuracy when chosing where to apply the '1000' correction
215  ms = uint64_t((double(ticks) / double(ticksPerSecond)) * 1000.);
216  }
217  return ms;
218 }
219 
220 
222 {
223  uint64_t ticks = GetSystemCounter();
224  uint64_t ticksPerSecond = GetSystemFrequency();
225  uint64_t us = 0;
226  if (ticksPerSecond)
227  {
228  // floats are being used here to avoid the issue of overflow
229  // or inaccuracy when chosing where to apply the '1000000' correction
230  us = uint64_t((double(ticks) / double(ticksPerSecond)) * 1000000.);
231  }
232  return us;
233 }
234 
235 
237 {
238  uint64_t ticks = GetSystemCounter();
239  uint64_t ticksPerSecond = GetSystemFrequency();
240  uint64_t us = 0;
241  if (ticksPerSecond)
242  {
243  // floats are being used here to avoid the issue of overflow
244  // or inaccuracy when chosing where to apply the '1000000000' correction
245  us = uint64_t((double(ticks) / double(ticksPerSecond)) * 1000000000.);
246  }
247  return us;
248 }
249 
250 
251 #if defined(AJA_COLLECT_SLEEP_STATS)
252  static uint64_t sMonThreadID = 0;// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
253  static const double sPercentiles[] = { 1.0, 1.1, 1.2, 1.5, 2.0, 3.0, 6.0, 11.0, 21.0, 51.0, 101.0, 201.0, 501.0, 1001.0, 2001.0, 5001.0, 10001.0};
254  static uint32_t sCounts[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
255  static int sNumPercentiles = sizeof(sPercentiles) / sizeof(double); // 17
256  static int sNumCounts = sizeof(sCounts) / sizeof(uint32_t); // 18
257  static AJATimer sTimer (AJATimerPrecisionMicroseconds);
258 
259  #define PRE_STATS const bool doStats(sMonThreadID == AJAThread::GetThreadId()); \
260  if (doStats) \
261  { sTimer.Reset(); \
262  sTimer.Start(); \
263  }
264 
265  #define POST_STATS(_req_) if (doStats) \
266  { sTimer.Stop(); \
267  const double act(sTimer.ETSecs()); \
268  const double req(_req_); \
269  for (int n(0); n < sNumPercentiles; n++) \
270  if (act < (sPercentiles[n] * req)) \
271  { \
272  AJAAtomic::Increment(&sCounts[n]); \
273  return; \
274  } \
275  AJAAtomic::Increment(&sCounts[sNumPercentiles]); \
276  }
277 #else // AJA_COLLECT_SLEEP_STATS
278  #define PRE_STATS
279  #define POST_STATS(__x__)
280 #endif // !defined(AJA_COLLECT_SLEEP_STATS)
281 
282 
283 // sleep time in milliseconds
284 void AJATime::Sleep (const int32_t inTime)
285 {
286  if (inTime <= 0)
287  return; // Don't sleep at all
288 
289  PRE_STATS
290  #if defined(AJA_SLEEP_USE_STL)
291  std::this_thread::sleep_for(std::chrono::milliseconds(inTime));
292  #elif defined(AJA_WINDOWS)
293  ::Sleep(DWORD(inTime));
294  #elif defined(AJA_BAREMETAL)
295  usleep(inTime * 1000);
296  #else // POSIX
297  usleep(inTime * 1000); // NOTE: usleep is deprecated in POSIX
298  #endif
299  POST_STATS(double(inTime) / 1000.0);
300 }
301 
302 
303 // sleep time in microseconds
304 void AJATime::SleepInMicroseconds (const int32_t inTime)
305 {
306  if (inTime <= 0)
307  return; // Don't sleep at all
308 
309  PRE_STATS
310  #if defined(AJA_SLEEP_USE_STL)
311  std::this_thread::sleep_for(std::chrono::microseconds(inTime));
312  #elif defined(AJA_WINDOWS)
313  ::Sleep(DWORD(inTime) / 1000); // Windows Sleep expects millisecs
314  #elif defined(AJA_BAREMETAL)
315  // TODO
316  #else // POSIX
317  usleep(inTime); // NOTE: usleep is deprecated in POSIX
318  #endif
319  POST_STATS(double(inTime) / 1000000.0);
320 }
321 
322 // sleep time in nanoseconds
323 void AJATime::SleepInNanoseconds (const uint64_t inTime)
324 {
325  if (inTime == 0)
326  return; // Don't sleep at all
327 
328  #if defined(AJA_SLEEP_USE_STL)
329  std::this_thread::sleep_for(std::chrono::nanoseconds(inTime));
330  #elif defined(AJA_WINDOWS)
331  // Adapted from OBS Windows platform code
332  int64_t freq = GetSystemFrequency();
333  int64_t timeTarget = (int64_t)GetSystemNanoseconds() + (int64_t)inTime;
334  const LONGLONG countTarget = (LONGLONG)util_mul_div64(timeTarget, freq, 1000000000);
335  int64_t count = GetSystemCounter();
336  const bool stall = count < countTarget;
337  if (stall) {
338  DWORD milliseconds = (DWORD)(((countTarget - count) * 1000.0) / freq);
339  if (milliseconds > 1) {
340  Sleep(milliseconds - 1);
341  }
342  for (;;) {
343  count = GetSystemCounter();
344  if (count >= countTarget)
345  break;
346  YieldProcessor();
347  }
348  }
349  #elif defined(AJA_BAREMETAL)
350  // TODO
351  #else
352  timespec req, rm;
353  req.tv_sec = inTime / 1000000000;
354  req.tv_nsec = long(inTime) % 1000000000L;
355  rm.tv_sec = 0;
356  rm.tv_nsec = 0;
357  nanosleep(&req, &rm);
358  #endif
359 
360  PRE_STATS
361  POST_STATS(double(inTime) / 1000000000.0)
362 }
363 
364 #if defined(AJA_COLLECT_SLEEP_STATS)
365  bool AJATime::CollectSleepStats (const bool inEnable)
366  {
367  if (!inEnable)
368  {sMonThreadID = 0; return true;}
369 
370  if (sMonThreadID != AJAThread::GetThreadId())
371  for (int n(0); n < sNumPercentiles; n++)
372  sCounts[n] = 0;
373  sMonThreadID = AJAThread::GetThreadId();
374  return true;
375  }
376 
377  std::string AJATime::GetSleepStats (void)
378  {
379  std::ostringstream oss;
380  for (int n(0); n < sNumCounts; n++)
381  if (sCounts[n])
382  {
383  oss << sCounts[n] << "\t< " << sPercentiles[n] << std::endl;
384  }
385  return oss.str();
386  }
387 #endif // defined(AJA_COLLECT_SLEEP_STATS)
AJATime::SleepInMicroseconds
static void SleepInMicroseconds(const int32_t inMicroseconds)
Suspends execution of the current thread for a given number of microseconds.
Definition: systemtime.cpp:304
POST_STATS
#define POST_STATS(__x__)
Definition: systemtime.cpp:279
types.h
Declares common types used in the ajabase library.
systemtime.h
Declares the AJATime class.
AJAThread::GetThreadId
static uint64_t GetThreadId()
Definition: thread.cpp:180
AJATime::GetSystemSeconds
static double GetSystemSeconds(void)
Returns the current value of the host's high-resolution clock, in seconds.
Definition: systemtime.cpp:193
nlohmann::json_abiNLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON_v3_11_NLOHMANN_JSON_VERSION_PATCH::detail::void
j template void())
Definition: json.hpp:4893
AJATime::GetSystemMicroseconds
static uint64_t GetSystemMicroseconds(void)
Returns the current value of the host's high-resolution clock, in microseconds.
Definition: systemtime.cpp:221
AJATime::Sleep
static void Sleep(const int32_t inMilliseconds)
Suspends execution of the current thread for a given number of milliseconds.
Definition: systemtime.cpp:284
timer.h
Declares the AJATimer class.
CLOCK_MONOTONIC
#define CLOCK_MONOTONIC
Definition: pthreadsextra.h:23
AJATimer
Definition: timer.h:25
AJATime::GetSystemNanoseconds
static uint64_t GetSystemNanoseconds(void)
Returns the current value of the host's high-resolution clock, in nanoseconds.
Definition: systemtime.cpp:236
system.h
System specific functions.
common.h
Private include file for all ajabase sources.
AJATime::GetSystemTime
static int64_t GetSystemTime(void)
Returns the current value of the host system's low-resolution clock, in milliseconds.
Definition: systemtime.cpp:74
PRE_STATS
#define PRE_STATS
Definition: systemtime.cpp:278
AJATimerPrecisionMicroseconds
@ AJATimerPrecisionMicroseconds
Definition: timer.h:17
atomic.h
Declares the AJAAtomic class.
AJATime::SleepInNanoseconds
static void SleepInNanoseconds(const uint64_t inNanoseconds)
Suspends execution of the current thread for a given number of nanoseconds.
Definition: systemtime.cpp:323
clock_gettime
int clock_gettime(clockid_t clk_id, struct timespec *tp)
Definition: pthreadsextra.cpp:12
thread.h
Declares the AJAThread class.
AJATime::GetSystemCounter
static int64_t GetSystemCounter(void)
Returns the current value of the host system's high-resolution time counter.
Definition: systemtime.cpp:112
AJATime::GetSystemFrequency
static int64_t GetSystemFrequency(void)
Returns the frequency of the host system's high-resolution time counter.
Definition: systemtime.cpp:149
AJATime::GetSystemMilliseconds
static uint64_t GetSystemMilliseconds(void)
Returns the current value of the host's high-resolution clock, in milliseconds.
Definition: systemtime.cpp:206