File: sleep.h

package info (click to toggle)
fis-gtm 7.1-006-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 32,908 kB
  • sloc: ansic: 344,906; asm: 5,184; csh: 4,859; sh: 2,000; awk: 294; makefile: 73; sed: 13
file content (183 lines) | stat: -rwxr-xr-x 8,546 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
/****************************************************************
 *								*
 * 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 */