summaryrefslogtreecommitdiff
path: root/src/tet3/dtet2lib/alarm.c
blob: f9bb70859f621f1c2b144fac1520ec817a93cffb (plain)
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
/*
 *	SCCS: @(#)alarm.c	1.10 (98/08/28)
 *
 *	UniSoft Ltd., London, England
 *
 * (C) Copyright 1996 X/Open Company Limited
 *
 * All rights reserved.  No part of this source code may be reproduced,
 * stored in a retrieval system, or transmitted, in any form or by any
 * means, electronic, mechanical, photocopying, recording or otherwise,
 * except as stated in the end-user licence agreement, without the prior
 * permission of the copyright owners.
 * A copy of the end-user licence agreement is contained in the file
 * Licence which accompanies this distribution.
 * 
 * X/Open and the 'X' symbol are trademarks of X/Open Company Limited in
 * the UK and other countries.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/************************************************************************

SCCS:   	@(#)alarm.c	1.10 98/08/28 TETware release 3.3
NAME:		functions to provide timeouts (including thread-safe version)
PRODUCT:	TETware
AUTHOR:		Geoff Clare, UniSoft Ltd.
DATE CREATED:	July 1996
SYNOPSIS:

	#include <signal.h>
	#include "dtetlib.h"

	int	tet_set_alarm(struct alrmaction *new_aa,
			      struct alrmaction *old_aa);

	int	tet_clr_alarm(struct alrmaction *old_aa);

	int *	tet_thr_alrm_flag(void);

DESCRIPTION:

	Tet_set_alarm() and tet_clr_alarm() provide a timeout facility
	for use in code that is compiled both thread-safe and not.
	They return 0 on success, -1 (with errno set) on failure.

	The waittime field of new_aa specifies the timeout period.  The
	sa field will be used in a struct sigaction to set the signal
	disposition for SIGALRM.  (In the threads version this does not
	happen until the timeout period has expired.)  The previous
	disposition, which is placed in old_aa->sa in the non-threads
	version only, will have been restored by the time tet_clr_alarm()
	returns.

	The handler specified in new_aa->sa.sa_handler should just
	increment alrm_flag.  It must not longjmp.  For thread-safety,
	new_aa and old_aa must not point to global or static data.

	Tet_thr_alrm_flag() is used (in the threads version) in the
	#define of alrm_flag.

	note that none of these functions are implemented on WIN32

MODIFICATIONS:

	Geoff Clare, UniSoft Ltd., Sept 1996
	Moved from apilib to dtet2lib (so can be used in fifolib).
	Unblock SIGALRM in non-thread version.

	Andrew Dingwall, UniSoft Ltd., February 1998
	Use TETware-specific macros to access threads functions and
	data items.

	Andrew Dingwall, UniSoft Ltd., July 1998
	Added support for shared API libraries.
 
************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include "dtmac.h"
#include "dtthr.h"
#include "sigsafe.h"
#include "alarm.h"
#include "error.h"
#include "tet_api.h"

#ifndef NOTRACE
#include "ltoa.h"
#endif

#ifdef TET_THREADS

#define ALRMWAIT	5	/* seconds to wait for tet_clr_alarm() to
				   be called after SIGALRM sent */

extern tet_mutex_t tet_sigalrm_mtx;
extern tet_mutex_t tet_alarm_mtx;

struct alrmarg {
	unsigned int	waittime;
	struct sigaction *sap;
	tet_thread_t	kill_tid;
	tet_cond_t	*cvp;
	unsigned int	*condp;
};

static void *alrm_thr PROTOLIST((void *));

#endif /* TET_THREADS */

int
tet_set_alarm(new_aa, old_aa)
struct alrmaction *new_aa, *old_aa;
{
#ifndef TET_THREADS

	sigset_t alrmset;

	ASSERT(new_aa->waittime != 0);
	if (sigaction(SIGALRM, &new_aa->sa, &old_aa->sa) == -1)
		return -1;

	/* SIGALRM is blocked between tet_sigsafe_start/end calls,
	   so unblock it.  (This means the handler mustn't longjmp.) */
	(void) sigemptyset(&alrmset);
	(void) sigaddset(&alrmset, SIGALRM);
	(void) sigprocmask(SIG_UNBLOCK, &alrmset, &old_aa->mask);

	(void) alarm(new_aa->waittime);

#else /* TET_THREADS */

	int err;
	struct alrmarg *alrmarg;
	sigset_t alrmset;

	ASSERT(new_aa->waittime != 0);

	old_aa->cvp = NULL; /* indicates not yet valid to tet_clr_alarm() */
	old_aa->waittime = new_aa->waittime; /* use as condition flag */

	alrmarg = (struct alrmarg *) malloc(sizeof(*alrmarg));
	if (alrmarg == NULL)
		return -1;
	TRACE2(tet_Tbuf, 6, "allocate alrmarg = %s", tet_i2x(alrmarg));

	alrmarg->cvp = (tet_cond_t *) malloc(sizeof(tet_cond_t));
	if (alrmarg->cvp == NULL)
	{
		TRACE2(tet_Tbuf, 6, "free alrmarg = %s", tet_i2x(alrmarg));
		free((void *)alrmarg);
		return -1;
	}
	TRACE2(tet_Tbuf, 6, "allocate condition variable = %s",
		tet_i2x(alrmarg->cvp));

	(void) TET_COND_INIT(alrmarg->cvp);

	/* call alrm_thr() in a new thread */
	alrmarg->waittime = new_aa->waittime;
	alrmarg->sap = &new_aa->sa;
	alrmarg->condp = &old_aa->waittime;
	alrmarg->kill_tid = TET_THR_SELF();
	err = TET_THR_CREATE(alrm_thr, (void *) alrmarg, &old_aa->join_tid);
	if (err != 0)
	{
		(void) TET_COND_DESTROY(alrmarg->cvp);
		TRACE2(tet_Tbuf, 6, "free condition variable = %s",
			tet_i2x(alrmarg->cvp));
		free((void *)alrmarg->cvp);
		TRACE2(tet_Tbuf, 6, "free alrmarg = %s", tet_i2x(alrmarg));
		free((void *)alrmarg);
		errno = err;
		return -1;
	}

	/*
	 * If tet_clr_alarm() is called within waittime, alrm_thr
	 * will see this via TET_COND_TIMEDWAIT() and will return.
	 * If not, it will install the specified new signal
	 * action and send a SIGALRM to this thread.
	 */

	(void) sigemptyset(&alrmset);
	(void) sigaddset(&alrmset, SIGALRM);
	(void) TET_THR_SIGSETMASK(SIG_UNBLOCK, &alrmset, &old_aa->mask);
	old_aa->cvp = alrmarg->cvp;

	/* note alrmarg is freed in alrm_thr() */

#endif /* TET_THREADS */

	return 0;
}

#ifdef TET_THREADS

static void *
alrm_thr(varg)
void *varg;
{
	/*
	 * Cause the target thread to execute the specified SIGALRM
	 * handler, after timeout of waittime seconds.
	 * (This function is executed in a new thread.)
	 */

	struct alrmarg *argp = (struct alrmarg *)varg;
	unsigned int	waittime;
	struct sigaction *new_sap;
	tet_thread_t	kill_tid;
	tet_cond_t	*cvp;
	unsigned int	*condp;
	struct sigaction oldsigact;
	tet_timestruc_t abstime;
	int err;

	/* copy passed arguments to local storage */

	waittime = argp->waittime;
	new_sap = argp->sap;
	kill_tid = argp->kill_tid;
	cvp = argp->cvp;
	condp = argp->condp;
	TRACE2(tet_Tbuf, 6, "free alrmarg = %s", tet_i2x(argp));
	free((void *)argp);

	/* wait for tet_clr_alarm() to clear condition flag */

	MTX_LOCK(&tet_alarm_mtx);
	abstime.tv_sec = time((time_t *)0) + waittime;
	abstime.tv_nsec = 0;
	while (*condp != 0) /* this points at old_aa->waittime */
	{
		err = TET_COND_TIMEDWAIT(cvp, &tet_alarm_mtx, &abstime);
		if (err != EINTR)
			break;
	}
	if (*condp == 0)
		err = 0;
	MTX_UNLOCK(&tet_alarm_mtx);
	if (err == 0)
	{
		(void) TET_COND_DESTROY(cvp);
		TRACE2(tet_Tbuf, 6, "free condition variable = %s",
			tet_i2x(cvp));
		free((void *)cvp);
		return (void *)0;
	}
	else if (err != ETIME)
		fatal(err, "first TET_COND_TIMEDWAIT() failed in alrm_thr()",
			(char *)0);

	MTX_LOCK(&tet_sigalrm_mtx);

	if (sigaction(SIGALRM, new_sap, &oldsigact) == -1)
		fatal(errno, "sigaction() failed in alrm_thr()", (char *)0);
	err = TET_THR_KILL(kill_tid, SIGALRM);
	if (err != 0)
		fatal(err, "TET_THR_KILL() failed in alrm_thr()", (char *)0);

	/* must wait until target thread calls tet_clr_alarm() before
	   it is safe to restore old SIGALRM handler */
	TET_MUTEX_LOCK(&tet_alarm_mtx);	/* don't nest MTX_LOCK */
	abstime.tv_sec = time((time_t *)0) + ALRMWAIT;
	abstime.tv_nsec = 0;
	while (*condp != 0)
	{
		err = TET_COND_TIMEDWAIT(cvp, &tet_alarm_mtx, &abstime);
		if (err != EINTR)
			break;
	}
	if (*condp == 0)
		err = 0;
	TET_MUTEX_UNLOCK(&tet_alarm_mtx);
	if (err != 0 && err != ETIME)
		fatal(err, "second TET_COND_TIMEDWAIT() failed in alrm_thr()",
			(char *)0);
	else if (err == ETIME)
		fatal(err,
			"second TET_COND_TIMEDWAIT() timed out in alrm_thr()",
			(char *)0);

	(void) sigaction(SIGALRM, &oldsigact, (struct sigaction *)0);

	MTX_UNLOCK(&tet_sigalrm_mtx);

	(void) TET_COND_DESTROY(cvp);
	TRACE2(tet_Tbuf, 6, "free condition variable = %s", tet_i2x(cvp));
	free((void *)cvp);

	return (void *)0;
}

#endif /* TET_THREADS */

int
tet_clr_alarm(old_aa)
struct alrmaction *old_aa;
{
#ifndef TET_THREADS

	(void) alarm(0);
	(void) sigprocmask(SIG_SETMASK, &old_aa->mask, (sigset_t *)0);
	if (sigaction(SIGALRM, &old_aa->sa, (struct sigaction *)0) == -1)
		return -1;

#else
	int err;

	if (old_aa->cvp == NULL)
	{
		errno = EINVAL;
		return -1;
	}

	(void) TET_THR_SIGSETMASK(SIG_SETMASK, &old_aa->mask, (sigset_t *) 0);
	MTX_LOCK(&tet_alarm_mtx);
	old_aa->waittime = 0;	/* used as condition var */
	(void) TET_COND_SIGNAL(old_aa->cvp);
	MTX_UNLOCK(&tet_alarm_mtx);
	old_aa->cvp = NULL;	/* so a second call will give EINVAL */

	err = TET_THR_JOIN(old_aa->join_tid, (void **) NULL);
	if (err != 0)
	{
		errno = err;
		return -1;
	}

#endif
	return 0;
}

#ifdef TET_THREADS

TET_IMPORT tet_thread_key_t tet_alrm_flag_key;

int *
tet_thr_alrm_flag()
{
	/* find tet_alrm_flag address for this thread */

	void *rtval;

	rtval = 0;
	TET_THR_GETSPECIFIC(tet_alrm_flag_key, &rtval);
	if (rtval == 0)
	{
		/* No tet_alrm_flag has been set up for this thread - probably
		   because it was not created with tet_thr_create().
		   Try and allocate a new tet_alrm_flag. */

		rtval = malloc(sizeof(int));
		TET_THR_SETSPECIFIC(tet_alrm_flag_key, rtval);
		rtval = 0;
		TET_THR_GETSPECIFIC(tet_alrm_flag_key, &rtval);
		if (rtval == 0)
			fatal(0, "could not set up tet_alrm_flag for new thread in tet_thr_alrm_flag", (char *)0);
		*((int *)rtval) = 0;
	}

	return (int *)rtval;
}
#endif /* TET_THREADS */