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
|
/****************************************************************
* *
* Copyright (c) 2001-2021 Fidelity National Information *
* Services, Inc. and/or its subsidiaries. All rights reserved. *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#ifndef SLEEP_H
#define SLEEP_H
/* Note: GT.M code *MUST NOT* use the sleep function because it causes problems with GT.M's timers on some platforms. Specifically,
* the sleep function results in SIGARLM handler being silently deleted on Solaris systems (through Solaris 9 at least). This leads
* to lost timer pops and has the potential for system hangs. The proper long sleep mechanism is hiber_start which can be accessed
* through the LONG_SLEEP macro defined in mdef.h.
*
* On Linux boxes be sure to define USER_HZ macro (in gt_timers.c) appropriately to mitigate the timer clustering imposed by
* the OS. Historically, the USER_HZ value has defaulted to 100 (same as HZ), thus resulting in at most 10ms accuracy when
* delivering timed events.
*/
/* SLEEP_USEC wrapper, see sleep.c for more information */
void m_usleep(int useconds);
#if !defined(_AIX) && !defined(__linux__) && !defined(__MVS__) && !defined(__CYGWIN__)
# error "Unsure of support for sleep functions on this platform"
#endif
/* Where possible we use clock_nanosleep and clock_gettime, which, while currently no faster than gettimeofday(), do eventually
* promise sub-millisecond accuracy.
*/
#define CLOCK_NANOSLEEP(CLOCKID, SECONDS, NANOSECONDS, RESTART) \
MBSTART { \
int STATUS; \
struct timespec REQTIM; \
\
assert(0 <= (SECONDS)); \
assert((0 <= (NANOSECONDS)) && (E_9 > (NANOSECONDS))); \
clock_gettime(CLOCKID, &REQTIM); \
REQTIM.tv_sec += (long)(SECONDS); \
REQTIM.tv_nsec += (long)(NANOSECONDS); \
if (NANOSECS_IN_SEC <= REQTIM.tv_nsec) \
{ \
REQTIM.tv_sec++; \
REQTIM.tv_nsec -= NANOSECS_IN_SEC; \
} \
do \
{ \
STATUS = clock_nanosleep(CLOCKID, TIMER_ABSTIME, &REQTIM, NULL); \
if (!RESTART || (0 == STATUS)) \
break; \
assert(EINTR == STATUS); \
} while (TRUE); \
} MBEND
/* For most UNIX platforms a combination of nanosleep() and gettimeofday() proved to be the most supported, accurate, and
* operationally sound approach. Alternatives for implementing high-resolution sleeps include clock_nanosleep() and nsleep()
*/
#define SET_EXPIR_TIME(NOW_TIMEVAL, EXPIR_TIMEVAL, SECS, USECS) \
MBSTART { \
gettimeofday(&(NOW_TIMEVAL), NULL); \
if (E_6 <= ((NOW_TIMEVAL).tv_usec + USECS)) \
{ \
(EXPIR_TIMEVAL).tv_sec = (NOW_TIMEVAL).tv_sec + (SECS) + 1; \
(EXPIR_TIMEVAL).tv_usec = (NOW_TIMEVAL).tv_usec + (USECS) - E_6; \
} else \
{ \
(EXPIR_TIMEVAL).tv_sec = (NOW_TIMEVAL).tv_sec + (SECS); \
(EXPIR_TIMEVAL).tv_usec = (NOW_TIMEVAL).tv_usec + (USECS); \
} \
} MBEND
/* This macro *does not* have surrounding braces and *will break* out of the block it is in on non-positive remaining time. */
#define UPDATE_REM_TIME_OR_BREAK(NOW_TIMEVAL, EXPIR_TIMEVAL, SECS, USECS) \
gettimeofday(&(NOW_TIMEVAL), NULL); \
if (((NOW_TIMEVAL).tv_sec > (EXPIR_TIMEVAL).tv_sec) \
|| (((NOW_TIMEVAL).tv_sec == (EXPIR_TIMEVAL).tv_sec) \
&& ((NOW_TIMEVAL).tv_usec >= (EXPIR_TIMEVAL).tv_usec))) \
break; \
if ((EXPIR_TIMEVAL).tv_usec < (NOW_TIMEVAL).tv_usec) \
{ \
SECS = (time_t)((EXPIR_TIMEVAL).tv_sec - (NOW_TIMEVAL).tv_sec - 1); \
USECS = (int)(E_6 + (EXPIR_TIMEVAL).tv_usec - (NOW_TIMEVAL).tv_usec); \
} else \
{ \
SECS = (time_t)((EXPIR_TIMEVAL).tv_sec - (NOW_TIMEVAL).tv_sec); \
USECS = (int)((EXPIR_TIMEVAL).tv_usec - (NOW_TIMEVAL).tv_usec); \
} \
/* Because, as of this writing, in AIX the clock_* routines are so erratic with short times we use the functions mentioned above
* for most things but give the following macro a separate name so AIX can use it in op_hang.c to ensure that a 1 second sleep
* always puts the process in a different second as measured by $HOROLOG and the like.
*/
#define MICROSECOND_SLEEP_with_NANOSECOND_SLEEP(USECONDS, RESTART) \
MBSTART { \
int status, usecs, save_errno; \
struct timespec req; \
struct timeval now, expir; \
\
assert(0 < (USECONDS)); \
req.tv_sec = (time_t)((USECONDS) / E_6); \
req.tv_nsec = (long)((usecs = (USECONDS) % E_6) * 1000); /* Assignment! */ \
assert(E_9 > req.tv_nsec); \
/* A little wasteful for the non-restart case */ \
SET_EXPIR_TIME(now, expir, req.tv_sec, usecs); \
do \
{ /* This macro will break the loop when it is time. */ \
status = nanosleep(&req, NULL); \
if (!(RESTART) || (0 == status)) \
break; \
UPDATE_REM_TIME_OR_BREAK(now, expir, req.tv_sec, usecs); \
req.tv_nsec = (long)(usecs * 1000); \
assert(EINTR == (save_errno = errno)); /* inline assignment */ \
} while (TRUE); \
} MBEND
/* On z/OS neither clock_nanosleep nor nanosleep is available, so use a combination of sleep, usleep, and gettimeofday instead.
* Since we do not have a z/OS box presently, this implementation has not been tested, and so it likely needs some casts at the very
* least. Another note is that sleep is unsafe to mix with timers on other platforms, but on z/OS the documentation does not mention
* any fallouts, so this should be verified. If it turns out that sleep is unsafe, we might have to use pthread_cond_timewait or
* call usleep (which, given that we have used it on z/OS before, should be safe) in a loop.
* Due to the above stated limitations the minimum sleep on z/OS is 1 Usec
* cywin is a mystery so assume the worst */
#define MICROSECOND_SLEEP_with_SLEEP_and_USLEEP(USECONDS, RESTART) \
MBSTART { \
int secs, interrupted; \
useconds_t usecs; \
struct timeval now, expir; \
\
assert(0 < (USECONDS)); \
secs = (USECONDS) / E_6; \
usecs = (USECONDS) % E_6; \
SET_EXPIR_TIME(now, expir, secs, usecs); \
do \
{ \
/* Sleep for seconds first */ \
interrupted = sleep(secs); /* BYPASSOK */ \
if (interrupted && !(RESTART)) \
break; \
/* This macro will break the loop when it is time. */ \
UPDATE_REM_TIME_OR_BREAK(now, expir, secs, usecs); \
/* Recalculate time and sleep for remaining microseconds */ \
interrupted = usleep(usecs); /* BYPASSOK */ \
if (interrupted && !(RESTART)) \
break; \
/* This macro will break the loop when it is time. */ \
UPDATE_REM_TIME_OR_BREAK(now, expir, secs, usecs); \
} while ((0 < secs) || (0 < usecs)); \
} MBEND
#if defined(__MVS__) || defined(__CYGWIN__)
/* z/OS and Cygwin use the lowest quality sleep options. Sub-millisecond nanosecond sleeps round up to 1 millisecond */
# define SLEEP_USEC(USECONDS, RESTART) MICROSECOND_SLEEP_with_SLEEP_and_USLEEP(USECONDS, RESTART)
# define NANOSLEEP(NANOSECONDS, RESTART) SLEEP_USEC((1000 > (NANOSECONDS)) ? 1 : ((NANOSECONDS) / 1000), RESTART);
#elif defined(_AIX)
/* Because of unreliability, AIX uses plain old nanosleep(). Sub-millisecond nanosecond sleeps round up to 1 millisecond */
# define SLEEP_USEC(USECONDS, RESTART) MICROSECOND_SLEEP_with_NANOSECOND_SLEEP(USECONDS, RESTART)
# define NANOSLEEP(NANOSECONDS, RESTART) SLEEP_USEC((1000 > (NANOSECONDS)) ? 1 : ((NANOSECONDS) / 1000), RESTART);
#else
/* All other platforms use high performance sleeps with CLOCK_NANOSLEEP */
# define SLEEP_USEC(USECONDS, RESTART) \
MBSTART { \
time_t seconds = (USECONDS) / E_6; \
time_t nanoseconds = (USECONDS % E_6) * NANOSECS_IN_USEC; \
\
CLOCK_NANOSLEEP(CLOCK_MONOTONIC, seconds, nanoseconds, RESTART); \
} MBEND
# define NANOSLEEP(NANOSECONDS, RESTART) \
MBSTART { \
time_t seconds = (NANOSECONDS) / E_9; \
time_t nanoseconds = (NANOSECONDS % E_9); \
\
/* Really shouldn't be using this macro for sleep > 1 second */ \
assert(E_9 >= (NANOSECONDS)); \
CLOCK_NANOSLEEP(CLOCK_MONOTONIC, seconds, nanoseconds, RESTART); \
} MBEND
#endif
#endif /* SLEEP_H */
|