This is Unofficial EPICS BASE Doxygen Site
osdTime.cpp
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2002 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * EPICS BASE is distributed subject to a Software License Agreement found
7 * in file LICENSE that is included with this distribution.
8 \*************************************************************************/
9 
10 //
11 // Author: Jeff Hill
12 //
13 
14 //
15 // ANSI C
16 //
17 #include <cmath>
18 #include <ctime>
19 #include <climits>
20 #include <cstdio>
21 
22 //
23 // WIN32
24 //
25 #define VC_EXTRALEAN
26 #define STRICT
27 #include <windows.h>
28 #include <process.h>
29 
30 //
31 // EPICS
32 //
33 #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE
34 #include "epicsTime.h"
35 #include "generalTimeSup.h"
36 #include "epicsTimer.h"
37 #include "errlog.h"
38 #include "epicsAssert.h"
39 #include "epicsThread.h"
40 
41 #if defined ( DEBUG )
42 # define debugPrintf(argsInParen) ::printf argsInParen
43 #else
44 # define debugPrintf(argsInParen)
45 #endif
46 
47 extern "C" void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName );
48 
49 extern "C"
50 int osdTimeGetCurrent ( epicsTimeStamp *pDest );
51 
52 // for mingw
53 #if !defined ( MAXLONGLONG )
54 # define MAXLONGLONG 0x7fffffffffffffffLL
55 #endif
56 #if !defined ( MINLONGLONG )
57 # define MINLONGLONG ~0x7fffffffffffffffLL
58 #endif
59 #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
60 # define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000
61 #endif
62 
63 static const LONGLONG epicsEpochInFileTime = 0x01b41e2a18d64000LL;
64 
65 static unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn );
66 
67 class currentTime {
68 public:
69  currentTime ();
70  ~currentTime ();
71  void getCurrentTime ( epicsTimeStamp & dest );
72  void startPLL ();
73 private:
74  CRITICAL_SECTION mutex;
75  LONGLONG lastPerfCounter;
76  LONGLONG perfCounterFreq;
77  LONGLONG epicsTimeLast; // nano-sec since the EPICS epoch
78  LONGLONG perfCounterFreqPLL;
79  LONGLONG lastPerfCounterPLL;
80  LONGLONG lastFileTimePLL;
81  HANDLE threadHandle;
82  unsigned threadId;
83  bool perfCtrPresent;
84  bool threadShutdownCmd;
85  bool threadHasExited;
86  void updatePLL ();
87  static const int pllDelay; /* integer seconds */
88  // cant be static because of diff btw __stdcall and __cdecl
89  friend unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn );
90 };
91 
92 const int currentTime :: pllDelay = 5;
93 
94 static currentTime * pCurrentTime = 0;
95 static const LONGLONG FILE_TIME_TICKS_PER_SEC = 10000000;
96 static const LONGLONG EPICS_TIME_TICKS_PER_SEC = 1000000000;
97 static const LONGLONG ET_TICKS_PER_FT_TICK =
98  EPICS_TIME_TICKS_PER_SEC / FILE_TIME_TICKS_PER_SEC;
99 
100 //
101 // Start and register time provider
102 //
103 static int timeRegister(void)
104 {
105  pCurrentTime = new currentTime ();
106 
107  generalTimeCurrentTpRegister("PerfCounter", 150, osdTimeGetCurrent);
108 
109  pCurrentTime->startPLL ();
110 
112  return 1;
113 }
114 static int done = timeRegister();
115 
116 //
117 // osdTimeGetCurrent ()
118 //
120 {
121  assert ( pCurrentTime );
122 
123  pCurrentTime->getCurrentTime ( *pDest );
124  return epicsTimeOK;
125 }
126 
127 // synthesize a reentrant gmtime on WIN32
128 int epicsStdCall epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM )
129 {
130  struct tm * pRet = gmtime ( pAnsiTime );
131  if ( pRet ) {
132  *pTM = *pRet;
133  return epicsTimeOK;
134  }
135  else {
136  return errno;
137  }
138 }
139 
140 // synthesize a reentrant localtime on WIN32
141 int epicsStdCall epicsTime_localtime (
142  const time_t * pAnsiTime, struct tm * pTM )
143 {
144  struct tm * pRet = localtime ( pAnsiTime );
145  if ( pRet ) {
146  *pTM = *pRet;
147  return epicsTimeOK;
148  }
149  else {
150  return errno;
151  }
152 }
153 
155  lastPerfCounter ( 0 ),
156  perfCounterFreq ( 0 ),
157  epicsTimeLast ( 0 ),
158  perfCounterFreqPLL ( 0 ),
159  lastPerfCounterPLL ( 0 ),
160  lastFileTimePLL ( 0 ),
161  threadHandle ( 0 ),
162  threadId ( 0 ),
163  perfCtrPresent ( false ),
164  threadShutdownCmd ( false ),
165  threadHasExited ( false )
166 {
167  InitializeCriticalSection ( & this->mutex );
168 
169  // avoid interruptions by briefly becoming a time critical thread
170  int originalPriority = GetThreadPriority ( GetCurrentThread () );
171  SetThreadPriority ( GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL );
172 
173  FILETIME ft;
174  GetSystemTimeAsFileTime ( & ft );
175  LARGE_INTEGER tmp;
176  QueryPerformanceCounter ( & tmp );
177  this->lastPerfCounter = tmp.QuadPart;
178  // if no high resolution counters then default to low res file time
179  if ( QueryPerformanceFrequency ( & tmp ) ) {
180  this->perfCounterFreq = tmp.QuadPart;
181  this->perfCtrPresent = true;
182  }
183  SetThreadPriority ( GetCurrentThread (), originalPriority );
184 
185  LARGE_INTEGER liFileTime;
186  liFileTime.LowPart = ft.dwLowDateTime;
187  liFileTime.HighPart = ft.dwHighDateTime;
188 
189  if ( liFileTime.QuadPart >= epicsEpochInFileTime ) {
190  // the windows file time has a maximum resolution of 100 nS
191  // and a nominal resolution of 10 mS - 16 mS
192  this->epicsTimeLast =
193  ( liFileTime.QuadPart - epicsEpochInFileTime ) *
194  ET_TICKS_PER_FT_TICK;
195  }
196  else {
197  errlogPrintf (
198  "win32 osdTime.cpp init detected questionable "
199  "system date prior to EPICS epoch, epics time will not advance\n" );
200  this->epicsTimeLast = 0;
201  }
202 
203  this->perfCounterFreqPLL = this->perfCounterFreq;
204  this->lastPerfCounterPLL = this->lastPerfCounter;
205  this->lastFileTimePLL = liFileTime.QuadPart;
206 }
207 
209 {
210  // create frequency estimation thread when needed
211  if ( this->perfCtrPresent && ! this->threadHandle ) {
212  this->threadHandle = (HANDLE)
213  _beginthreadex ( 0, 4096, _pllThreadEntry, this,
214  CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION,
215  & this->threadId );
216  assert ( this->threadHandle );
217  BOOL bstat = SetThreadPriority (
218  this->threadHandle, THREAD_PRIORITY_HIGHEST );
219  assert ( bstat );
220  DWORD wstat = ResumeThread ( this->threadHandle );
221  assert ( wstat != 0xFFFFFFFF );
222  }
223 }
224 
226 {
227  EnterCriticalSection ( & this->mutex );
228  this->threadShutdownCmd = true;
229  while ( ! this->threadHasExited ) {
230  LeaveCriticalSection ( & this->mutex );
231  Sleep ( 250 /* mS */ );
232  EnterCriticalSection ( & this->mutex );
233  }
234  LeaveCriticalSection ( & this->mutex );
235  DeleteCriticalSection ( & this->mutex );
236 }
237 
239 {
240  if ( this->perfCtrPresent ) {
241  EnterCriticalSection ( & this->mutex );
242 
243  LARGE_INTEGER curPerfCounter;
244  QueryPerformanceCounter ( & curPerfCounter );
245 
246  LONGLONG offset;
247  if ( curPerfCounter.QuadPart >= this->lastPerfCounter ) {
248  offset = curPerfCounter.QuadPart - this->lastPerfCounter;
249  }
250  else {
251  //
252  // must have been a timer roll-over event
253  //
254  // It takes 9.223372036855e+18/perf_freq sec to roll over this
255  // counter. This is currently about 245118 years using the perf
256  // counter freq value on my system (1193182). Nevertheless, I
257  // have code for this situation because the performance
258  // counter resolution will more than likely improve over time.
259  //
260  offset = ( MAXLONGLONG - this->lastPerfCounter )
261  + ( curPerfCounter.QuadPart - MINLONGLONG ) + 1;
262  }
263  if ( offset < MAXLONGLONG / EPICS_TIME_TICKS_PER_SEC ) {
264  offset *= EPICS_TIME_TICKS_PER_SEC;
265  offset /= this->perfCounterFreq;
266  }
267  else {
268  double fpOffset = static_cast < double > ( offset );
269  fpOffset *= EPICS_TIME_TICKS_PER_SEC;
270  fpOffset /= static_cast < double > ( this->perfCounterFreq );
271  offset = static_cast < LONGLONG > ( fpOffset );
272  }
273  LONGLONG epicsTimeCurrent = this->epicsTimeLast + offset;
274  if ( this->epicsTimeLast > epicsTimeCurrent ) {
275  double diff = static_cast < double >
276  ( this->epicsTimeLast - epicsTimeCurrent ) / EPICS_TIME_TICKS_PER_SEC;
277  errlogPrintf (
278  "currentTime::getCurrentTime(): %f sec "
279  "time discontinuity detected\n",
280  diff );
281  }
282  this->epicsTimeLast = epicsTimeCurrent;
283  this->lastPerfCounter = curPerfCounter.QuadPart;
284 
285  LeaveCriticalSection ( & this->mutex );
286 
287  dest.secPastEpoch = static_cast < epicsUInt32 >
288  ( epicsTimeCurrent / EPICS_TIME_TICKS_PER_SEC );
289  dest.nsec = static_cast < epicsUInt32 >
290  ( epicsTimeCurrent % EPICS_TIME_TICKS_PER_SEC );
291  }
292  else {
293  // if high resolution performance counters are not supported then
294  // fall back to low res file time
295  FILETIME ft;
296  GetSystemTimeAsFileTime ( & ft );
297  dest = epicsTime ( ft );
298  }
299 }
300 
301 //
302 // Maintain corrected version of the performance counter's frequency using
303 // a phase locked loop. This approach is similar to NTP's.
304 //
305 void currentTime :: updatePLL ()
306 {
307  EnterCriticalSection ( & this->mutex );
308 
309  // avoid interruptions by briefly becoming a time critical thread
310  LARGE_INTEGER curFileTime;
311  LARGE_INTEGER curPerfCounter;
312  {
313  int originalPriority = GetThreadPriority ( GetCurrentThread () );
314  SetThreadPriority ( GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL );
315  FILETIME ft;
316  GetSystemTimeAsFileTime ( & ft );
317  QueryPerformanceCounter ( & curPerfCounter );
318  SetThreadPriority ( GetCurrentThread (), originalPriority );
319 
320  curFileTime.LowPart = ft.dwLowDateTime;
321  curFileTime.HighPart = ft.dwHighDateTime;
322  }
323 
324  LONGLONG perfCounterDiff;
325  if ( curPerfCounter.QuadPart >= this->lastPerfCounterPLL ) {
326  perfCounterDiff = curPerfCounter.QuadPart - this->lastPerfCounterPLL;
327  }
328  else {
329  //
330  // must have been a timer roll-over event
331  //
332  // It takes 9.223372036855e+18/perf_freq sec to roll over this
333  // counter. This is currently about 245118 years using the perf
334  // counter freq value on my system (1193182). Nevertheless, I
335  // have code for this situation because the performance
336  // counter resolution will more than likely improve over time.
337  //
338  perfCounterDiff = ( MAXLONGLONG - this->lastPerfCounterPLL )
339  + ( curPerfCounter.QuadPart - MINLONGLONG ) + 1;
340  }
341  this->lastPerfCounterPLL = curPerfCounter.QuadPart;
342 
343  LONGLONG fileTimeDiff = curFileTime.QuadPart - this->lastFileTimePLL;
344  this->lastFileTimePLL = curFileTime.QuadPart;
345 
346  // discard glitches
347  if ( fileTimeDiff <= 0 ) {
348  LeaveCriticalSection( & this->mutex );
349  debugPrintf ( ( "currentTime: file time difference in PLL was less than zero\n" ) );
350  return;
351  }
352 
353  LONGLONG freq = ( FILE_TIME_TICKS_PER_SEC * perfCounterDiff ) / fileTimeDiff;
354  LONGLONG delta = freq - this->perfCounterFreqPLL;
355 
356  // discard glitches
357  LONGLONG bound = this->perfCounterFreqPLL >> 10;
358  if ( delta < -bound || delta > bound ) {
359  LeaveCriticalSection( & this->mutex );
360  debugPrintf ( ( "freq est out of bounds l=%d e=%d h=%d\n",
361  static_cast < int > ( -bound ),
362  static_cast < int > ( delta ),
363  static_cast < int > ( bound ) ) );
364  return;
365  }
366 
367  // update feedback loop estimating the performance counter's frequency
368  LONGLONG feedback = delta >> 8;
369  this->perfCounterFreqPLL += feedback;
370 
371  LONGLONG perfCounterDiffSinceLastFetch;
372  if ( curPerfCounter.QuadPart >= this->lastPerfCounter ) {
373  perfCounterDiffSinceLastFetch =
374  curPerfCounter.QuadPart - this->lastPerfCounter;
375  }
376  else {
377  //
378  // must have been a timer roll-over event
379  //
380  // It takes 9.223372036855e+18/perf_freq sec to roll over this
381  // counter. This is currently about 245118 years using the perf
382  // counter freq value on my system (1193182). Nevertheless, I
383  // have code for this situation because the performance
384  // counter resolution will more than likely improve over time.
385  //
386  perfCounterDiffSinceLastFetch =
387  ( MAXLONGLONG - this->lastPerfCounter )
388  + ( curPerfCounter.QuadPart - MINLONGLONG ) + 1;
389  }
390 
391  // discard performance counter delay measurement glitches
392  {
393  const LONGLONG expectedDly = this->perfCounterFreq * pllDelay;
394  const LONGLONG bnd = expectedDly / 4;
395  if ( perfCounterDiffSinceLastFetch <= 0 ||
396  perfCounterDiffSinceLastFetch >= expectedDly + bnd ) {
397  LeaveCriticalSection( & this->mutex );
398  debugPrintf ( ( "perf ctr measured delay out of bounds m=%d max=%d\n",
399  static_cast < int > ( perfCounterDiffSinceLastFetch ),
400  static_cast < int > ( expectedDly + bnd ) ) );
401  return;
402  }
403  }
404 
405  // Update the current estimated time.
406  this->epicsTimeLast +=
407  ( perfCounterDiffSinceLastFetch * EPICS_TIME_TICKS_PER_SEC )
408  / this->perfCounterFreq;
409  this->lastPerfCounter = curPerfCounter.QuadPart;
410 
411  LONGLONG epicsTimeFromCurrentFileTime;
412 
413  {
414  static bool firstMessageWasSent = false;
415  if ( curFileTime.QuadPart >= epicsEpochInFileTime ) {
416  epicsTimeFromCurrentFileTime =
417  ( curFileTime.QuadPart - epicsEpochInFileTime ) *
418  ET_TICKS_PER_FT_TICK;
419  firstMessageWasSent = false;
420  }
421  else {
422  /*
423  * if the system time jumps to before the EPICS epoch
424  * then latch to the EPICS epoch printing only one
425  * warning message the first time that the issue is
426  * detected
427  */
428  if ( ! firstMessageWasSent ) {
429  errlogPrintf (
430  "win32 osdTime.cpp time PLL update detected questionable "
431  "system date prior to EPICS epoch, epics time will not advance\n" );
432  firstMessageWasSent = true;
433  }
434  epicsTimeFromCurrentFileTime = 0;
435  }
436  }
437 
438  delta = epicsTimeFromCurrentFileTime - this->epicsTimeLast;
439  if ( delta > EPICS_TIME_TICKS_PER_SEC || delta < -EPICS_TIME_TICKS_PER_SEC ) {
440  // When there is an abrupt shift in the current computed time vs
441  // the time derived from the current file time then someone has
442  // probably adjusted the real time clock and the best reaction
443  // is to just assume the new time base
444  this->epicsTimeLast = epicsTimeFromCurrentFileTime;
445  this->perfCounterFreq = this->perfCounterFreqPLL;
446  debugPrintf ( ( "currentTime: did someone adjust the date?\n" ) );
447  }
448  else {
449  // update the effective performance counter frequency that will bring
450  // our calculated time base in syncy with file time one second from now.
451  this->perfCounterFreq =
452  ( EPICS_TIME_TICKS_PER_SEC * this->perfCounterFreqPLL )
453  / ( delta + EPICS_TIME_TICKS_PER_SEC );
454 
455  // limit effective performance counter frequency rate of change
456  LONGLONG lowLimit = this->perfCounterFreqPLL - bound;
457  if ( this->perfCounterFreq < lowLimit ) {
458  debugPrintf ( ( "currentTime: out of bounds low freq excursion %d\n",
459  static_cast <int> ( lowLimit - this->perfCounterFreq ) ) );
460  this->perfCounterFreq = lowLimit;
461  }
462  else {
463  LONGLONG highLimit = this->perfCounterFreqPLL + bound;
464  if ( this->perfCounterFreq > highLimit ) {
465  debugPrintf ( ( "currentTime: out of bounds high freq excursion %d\n",
466  static_cast <int> ( this->perfCounterFreq - highLimit ) ) );
467  this->perfCounterFreq = highLimit;
468  }
469  }
470 
471 # if defined ( DEBUG )
472  LARGE_INTEGER sysFreq;
473  QueryPerformanceFrequency ( & sysFreq );
474  double freqDiff = static_cast <int>
475  ( this->perfCounterFreq - sysFreq.QuadPart );
476  freqDiff /= sysFreq.QuadPart;
477  freqDiff *= 100.0;
478  double freqEstDiff = static_cast <int>
479  ( this->perfCounterFreqPLL - sysFreq.QuadPart );
480  freqEstDiff /= sysFreq.QuadPart;
481  freqEstDiff *= 100.0;
482  debugPrintf ( ( "currentTime: freq delta %f %% freq est "
483  "delta %f %% time delta %f sec\n",
484  freqDiff,
485  freqEstDiff,
486  static_cast < double > ( delta ) /
487  EPICS_TIME_TICKS_PER_SEC ) );
488 # endif
489  }
490 
491  LeaveCriticalSection ( & this->mutex );
492 }
493 
494 static unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn )
495 {
496  currentTime * pCT =
497  reinterpret_cast < currentTime * > ( pCurrentTimeIn );
498  setThreadName ( pCT->threadId, "EPICS Time PLL" );
499  while ( ! pCT->threadShutdownCmd ) {
500  Sleep ( currentTime :: pllDelay * 1000 /* mS */ );
501  pCT->updatePLL ();
502  }
503  EnterCriticalSection ( & pCT->mutex );
504  pCT->threadHasExited = true;
505  LeaveCriticalSection ( & pCT->mutex );
506  return 1;
507 }
508 
509 epicsTime::operator FILETIME () const
510 {
511  LARGE_INTEGER ftTicks;
512  ftTicks.QuadPart = ( this->secPastEpoch * FILE_TIME_TICKS_PER_SEC ) +
513  ( this->nSec / ET_TICKS_PER_FT_TICK );
514  ftTicks.QuadPart += epicsEpochInFileTime;
515  FILETIME ts;
516  ts.dwLowDateTime = ftTicks.LowPart;
517  ts.dwHighDateTime = ftTicks.HighPart;
518  return ts;
519 }
520 
521 epicsTime::epicsTime ( const FILETIME & ts )
522 {
523  LARGE_INTEGER lift;
524  lift.LowPart = ts.dwLowDateTime;
525  lift.HighPart = ts.dwHighDateTime;
526  if ( lift.QuadPart > epicsEpochInFileTime ) {
527  LONGLONG fileTimeTicksSinceEpochEPICS =
528  lift.QuadPart - epicsEpochInFileTime;
529  this->secPastEpoch = static_cast < epicsUInt32 >
530  ( fileTimeTicksSinceEpochEPICS / FILE_TIME_TICKS_PER_SEC );
531  this->nSec = static_cast < epicsUInt32 >
532  ( ( fileTimeTicksSinceEpochEPICS % FILE_TIME_TICKS_PER_SEC ) *
533  ET_TICKS_PER_FT_TICK );
534  }
535  else {
536  this->secPastEpoch = 0;
537  this->nSec = 0;
538  }
539 }
540 
541 epicsTime & epicsTime::operator = ( const FILETIME & rhs )
542 {
543  *this = epicsTime ( rhs );
544  return *this;
545 }
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
An EPICS-specific replacement for ANSI C&#39;s assert.
#define generalTimeCurrentTpRegister
void getCurrentTime(epicsTimeStamp &dest)
Definition: osdTime.cpp:238
#define debugPrintf(argsInParen)
Definition: osdTime.cpp:44
void osdMonotonicInit(void)
Definition: osdMonotonic.c:19
#define epicsTimeOK
Success.
Definition: epicsTime.h:339
unsigned int epicsUInt32
Definition: epicsTypes.h:43
#define STACK_SIZE_PARAM_IS_A_RESERVATION
Definition: osdTime.cpp:60
epicsUInt32 secPastEpoch
seconds since 0000 Jan 1, 1990
Definition: epicsTime.h:34
void startPLL()
Definition: osdTime.cpp:208
friend unsigned __stdcall _pllThreadEntry(void *pCurrentTimeIn)
Definition: osdTime.cpp:494
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
EPICS time stamp, for use from C code.
Definition: epicsTime.h:33
int osdTimeGetCurrent(epicsTimeStamp *pDest)
Definition: osdTime.cpp:28
EPICS time-stamps (epicsTimeStamp), epicsTime C++ class and C functions for handling wall-clock times...
C++ and C descriptions for a thread.
int epicsTime_gmtime(const time_t *pAnsiTime, struct tm *pTM)
Break down a time_t into a struct tm in the UTC timezone.
Definition: osdTime.cpp:55
int epicsTime_localtime(const time_t *clock, struct tm *result)
Break down a time_t into a struct tm in the local timezone.
Definition: osdTime.cpp:61
#define MAXLONGLONG
Definition: osdTime.cpp:54
#define false
Definition: flexdef.h:85
#define MINLONGLONG
Definition: osdTime.cpp:57
epicsUInt32 nsec
nanoseconds within second
Definition: epicsTime.h:35
void setThreadName(DWORD dwThreadID, LPCSTR szThreadName)