File: GetSecs.c

package info (click to toggle)
psychtoolbox-3 3.0.19.14.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 86,796 kB
  • sloc: ansic: 176,245; cpp: 20,103; objc: 5,393; sh: 2,753; python: 1,397; php: 384; makefile: 193; java: 113
file content (199 lines) | stat: -rw-r--r-- 9,617 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*
 *    PsychSourceGL/Source/Common/GetSecs/GetSecs.c
 *
 *    PROJECTS:
 *
 *      GetSecs only
 *
 *    AUTHORS:
 *
 *      Allen.Ingling@nyu.edu           awi
 *      mario.kleiner.de@gmail.com      mk
 *
 *    PLATFORMS:
 *
 *      All.
 *
 *    HISTORY:
 *
 *      1/20/02         awi     Derived the GetSecs project from Screen .
 *      4/6/05          awi     Updated header comments.
 *
 *   DESCRIPTION:
 *
 *      Return the time in seconds with high precision. On MS-Windows, it allows to
 *      trigger some debug operations and returns some diagnostic values that allow
 *      to diagnose timer problems. Not so on Linux or OS/X: They don't have such problems afaik ;-)
 */

#include "GetSecs.h"

#define MAX_SYNOPSIS_STRINGS 500
static const char *synopsisSYNOPSIS[MAX_SYNOPSIS_STRINGS];

const char** InitializeSynopsis(void)
{
    int i = 0;
    const char **synopsis = synopsisSYNOPSIS;
    synopsis[i++] = "[GetSecsTime, WallTime, syncErrorSecs, MonotonicTime] = GetSecs('AllClocks' [, maxError=0.000020]);";
    synopsis[i++] = NULL;

    return(synopsisSYNOPSIS);
}

#if PSYCH_SYSTEM != PSYCH_WINDOWS
#include <sys/time.h>
#endif

PsychError GETSECSGetSecs(void)
{
    double returnValue;
    double referenceValue, realValue;
    int healthy, opmode;

    // Check to see if the user supplied superfluous arguments
    PsychErrorExit(PsychCapNumOutputArgs(5));
    PsychErrorExit(PsychCapNumInputArgs(1));

    PsychGetAdjustedPrecisionTimerSeconds(&returnValue);
    PsychCopyOutDoubleArg(1, FALSE, returnValue);

    // Special code for diagnosing problems with TimeGlue on systems that
    // are broken by design(TM) aka MS-Windows:
    #if PSYCH_SYSTEM == PSYCH_WINDOWS
        if (PsychCopyInIntegerArg(1, FALSE, &opmode) && (opmode!=0)) {
            referenceValue = PsychGetTimeGetTimeValueAtLastTimeQuery(&realValue);
            healthy = (int) PsychGetTimeBaseHealthiness();
            PsychCopyOutDoubleArg(2, FALSE, referenceValue);
            PsychCopyOutDoubleArg(3, FALSE, realValue);
            PsychCopyOutDoubleArg(4, FALSE, (double) healthy);

            // A positive opmode value allows to change the thread affinity mask of the PTB Thread.
            // The old affinity mask is returned in that case. This to check for TSC sync across cpu cores:
            if (opmode > 0)
                PsychCopyOutDoubleArg(5, FALSE, (double) SetThreadAffinityMask(GetCurrentThread(), (DWORD) opmode));

            // An opmode setting of smaller than -1 will try to cancel our timeBeginPeriod(1) requests, as
            // automatically done by the PsychTimeGlue: We try to reset the low-res timer to something
            // like its normal 10 or 15 msecs duty cycle. Could help to spot timers that are actually
            // broken, but do work with the new PTB due to the increased IRQ load and therefore the
            // reduced power management. Post-hoc test if timing was reliable in earlier PTB releases.
            if (opmode < -1)
                timeEndPeriod(1);
        }
    #else
        // For MacOS/X and Linux, we return gettimeofday() as reference value and
        // some default "no error" healthy flag. Please note that gettimeofday() is just a
        // drift/NTP corrected version of the system timebase mach_absolute_time(), ie the
        // same value as returned as primary time, just with some offset. Therefore its not
        // possible to check for timebase malfunction, only for clock drift against some
        // external timebase, e.g., UTC as determined by NTP protocol.
        if (PsychCopyInIntegerArg(1, FALSE, &opmode) && (opmode!=0)) {
            struct timeval tv;
            gettimeofday(&tv, NULL);
            referenceValue = ((double) tv.tv_sec) + (((double) tv.tv_usec) / 1000000.0);
            healthy = 0;
            realValue = returnValue;
            PsychCopyOutDoubleArg(2, FALSE, referenceValue);
            PsychCopyOutDoubleArg(3, FALSE, realValue);
            PsychCopyOutDoubleArg(4, FALSE, (double) healthy);

            // Copy out a fake thread affinity mask of the PTB Thread if requested:
            if (opmode > 0)
                PsychCopyOutDoubleArg(5, FALSE, 0);
        }
    #endif

    return(PsychError_none);
}

PsychError GETSECSAllClocks(void)
{
    static char useString[] = "[GetSecsTime, WallTime, syncErrorSecs, MonotonicTime] = GetSecs('AllClocks' [, maxError=0.000020]);";
    //                          1            2         3              4                                       1
    static char synopsisString[] =
    "Return current time in seconds according to all supported clocks.\n\n"
    "'GetSecsTime' is the usual GetSecs() clock, as returned by GetSecs(), in the timebase "
    "used by all other Psychtoolbox functions, e.g., Screen('Flip') or PsychPortAudio timestamps.\n\n"
    "'WallTime' is real-world system time in an operating system specific timebase. This clock is "
    "expected to be subject to time adjustments by the system administrator or by automated mechanisms "
    "like NTP time adjustments. Useful for synchronizing clocks across multiple machines on a local "
    "network, as it is possible for this clock to get automatically corrected for drift.\n"
    "On Linux and OSX, the timebase is gettimeofday(), seconds since 1. January 1970 00:00:00 UTC, "
    "with about 1 microsecond precision.\n"
    "On MS-Windows the timebase is also UTC time, with about 1 millisecond granularity, measuring "
    "elapsed seconds since 1. January 1601 00:00:00 UTC via the GetSystemTimeAsFileTime() function.\n\n"
    "'syncErrorSecs' How tightly together did the returned clock times get queried? A measure of "
    "confidence as to how much all returned times actually denote the same point in physical time.\n\n"
    "'MonotonicTime' is system monotonic timebase, not subject to administrator or NTP time adjustments, "
    "with a zero point at operating system boot time. Identical to 'GetSecsTime' on Windows and macOS, "
    "identical to Posix clock CLOCK_MONOTONIC on GNU/Linux.\n\n"
    "The input argument 'maxError' allows to set an allowable upper bound to 'syncErrorSecs'. The "
    "default value is 20 microseconds. The function will try up to 10 times to get a result no worse "
    "than 'maxError', and output a warning if it doesn't manage, e.g., due to some severely "
    "overloaded or deficient system.";
    static char seeAlsoString[] = "";

    static psych_bool firstTimeWarning = TRUE;
    int maxRetries = 10;
    double getSecsClock, getSecsClock2;
    double maxError = 20 * 1e-6;                // Default to 20 usecs max clock mapping error.
    double wallClock, tMonotonic;

    // All sub functions should have these two lines:
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if (PsychIsGiveHelp()) { PsychGiveHelp(); return(PsychError_none); };

    // Check to see if the user supplied superfluous arguments
    PsychErrorExit(PsychCapNumOutputArgs(4));
    PsychErrorExit(PsychCapNumInputArgs(1));

    PsychCopyInDoubleArg(1, FALSE, &maxError);
    if (maxError < 0.000001)
        PsychErrorExitMsg(PsychError_user, "Invalid 'maxError' argument supplied. Lower than minimum allowed value of 1 microsecond.\n");

    // Repeat clock queries up to maxRetries times if they can't be completed within
    // maxError seconds and thereby "clock sync" between returned values is not good
    // enough:
    do {
        PsychGetAdjustedPrecisionTimerSeconds(&getSecsClock);
        wallClock = PsychGetWallClockSeconds();
        #if PSYCH_SYSTEM == PSYCH_LINUX
            // Monotonic time has its own query in Linux for CLOCK_MONOTONIC:
            tMonotonic = PsychOSGetLinuxMonotonicTime();
        #else
            // Monotonic time is == standard GetSecs time on Windows and macOS:
            tMonotonic = getSecsClock;
        #endif
        PsychGetAdjustedPrecisionTimerSeconds(&getSecsClock2);
    } while ((maxRetries-- > 0) && (getSecsClock2 - getSecsClock > maxError));

    // Warn on excessive error, at least once:
    if (firstTimeWarning && (getSecsClock2 - getSecsClock > 2 * maxError)) {
        firstTimeWarning = FALSE;
        printf("PTB-WARNING: GetSecs('AllClocks') sync margin %f secs > 2 times maxError %f even after multiple retries! System timing problems?1?\n",
               getSecsClock2 - getSecsClock, 2 * maxError);
        printf("PTB-WARNING: This one-time warning will not repeat, even on successive failure to reach good precision. Check your system.\n");
    }

    // Return arg 1 is as usual GetSecs time:
    PsychCopyOutDoubleArg(1, FALSE, getSecsClock);

    // Return arg 2 is Wall clock, system specific but usually related to real-world
    // time, e.g., UTC, NTP time, or Unix time, something that translates in normal
    // clocks like humans use it. On Unix (Linux and OSX) this is gettimeofday() Unix
    // time, system time, measured in seconds since 1. January 1970. On Windows this is
    // UTC time, measured in seconds since whatever. This clock is subject to changes
    // of system time, manually by the system administrator, or automatically by NTP
    // and similar clock sync services.
    PsychCopyOutDoubleArg(2, FALSE, wallClock);

    // Return arg3 = Confidence interval for sync between clock queries:
    PsychCopyOutDoubleArg(3, FALSE, getSecsClock2 - getSecsClock);

    // Return arg 4 is monotonic clock, system specific, with a zero point usually
    // at OS boot, not subject to time adjustments by administrator or NTP:
    PsychCopyOutDoubleArg(4, FALSE, tMonotonic);

    return(PsychError_none);
}