1/*
2 * tclUnixNotify.c --
3 *
4 *	This file contains Unix-specific procedures for the notifier,
5 *	which is the lowest-level part of the Tcl event loop.  This file
6 *	works together with ../generic/tclNotify.c.
7 *
8 * Copyright (c) 1995 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 */
13
14static char sccsid[] = "@(#) tclUnixNotify.c 1.27 96/01/19 10:30:23";
15
16#include "tclInt.h"
17#include "tclPort.h"
18#include <signal.h>
19
20/*
21 * The information below is used to provide read, write, and
22 * exception masks to select during calls to Tcl_DoOneEvent.
23 */
24
25static fd_mask checkMasks[3*MASK_SIZE];
26				/* This array is used to build up the masks
27				 * to be used in the next call to select.
28				 * Bits are set in response to calls to
29				 * Tcl_WatchFile. */
30static fd_mask readyMasks[3*MASK_SIZE];
31				/* This array reflects the readable/writable
32				 * conditions that were found to exist by the
33				 * last call to select. */
34static int numFdBits;		/* Number of valid bits in checkMasks
35				 * (one more than highest fd for which
36				 * Tcl_WatchFile has been called). */
37
38/*
39 *----------------------------------------------------------------------
40 *
41 * Tcl_WatchFile --
42 *
43 *	Arrange for Tcl_DoOneEvent to include this file in the masks
44 *	for the next call to select.  This procedure is invoked by
45 *	event sources, which are in turn invoked by Tcl_DoOneEvent
46 *	before it invokes select.
47 *
48 * Results:
49 *	None.
50 *
51 * Side effects:
52 *
53 *	The notifier will generate a file event when the I/O channel
54 *	given by fd next becomes ready in the way indicated by mask.
55 *	If fd is already registered then the old mask will be replaced
56 *	with the new one.  Once the event is sent, the notifier will
57 *	not send any more events about the fd until the next call to
58 *	Tcl_NotifyFile.
59 *
60 *----------------------------------------------------------------------
61 */
62
63void
64Tcl_WatchFile(file, mask)
65    Tcl_File file;	/* Generic file handle for a stream. */
66    int mask;			/* OR'ed combination of TCL_READABLE,
67				 * TCL_WRITABLE, and TCL_EXCEPTION:
68				 * indicates conditions to wait for
69				 * in select. */
70{
71    int fd, type, index;
72    fd_mask bit;
73
74    fd = (int) Tcl_GetFileInfo(file, &type);
75
76    if (type != TCL_UNIX_FD) {
77	panic("Tcl_WatchFile: unexpected file type");
78    }
79
80    if (fd >= FD_SETSIZE) {
81	panic("Tcl_WatchFile can't handle file id %d", fd);
82    }
83
84    index = fd/(NBBY*sizeof(fd_mask));
85    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
86    if (mask & TCL_READABLE) {
87	checkMasks[index] |= bit;
88    }
89    if (mask & TCL_WRITABLE) {
90	(checkMasks+MASK_SIZE)[index] |= bit;
91    }
92    if (mask & TCL_EXCEPTION) {
93	(checkMasks+2*(MASK_SIZE))[index] |= bit;
94    }
95    if (numFdBits <= fd) {
96	numFdBits = fd+1;
97    }
98}
99
100/*
101 *----------------------------------------------------------------------
102 *
103 * Tcl_FileReady --
104 *
105 *	Indicates what conditions (readable, writable, etc.) were
106 *	present on a file the last time the notifier invoked select.
107 *	This procedure is typically invoked by event sources to see
108 *	if they should queue events.
109 *
110 * Results:
111 *	The return value is 0 if none of the conditions specified by mask
112 *	was true for fd the last time the system checked.  If any of the
113 *	conditions were true, then the return value is a mask of those
114 *	that were true.
115 *
116 * Side effects:
117 *	None.
118 *
119 *----------------------------------------------------------------------
120 */
121
122int
123Tcl_FileReady(file, mask)
124    Tcl_File file;	/* Generic file handle for a stream. */
125    int mask;			/* OR'ed combination of TCL_READABLE,
126				 * TCL_WRITABLE, and TCL_EXCEPTION:
127				 * indicates conditions caller cares about. */
128{
129    int index, result, type, fd;
130    fd_mask bit;
131
132    fd = (int) Tcl_GetFileInfo(file, &type);
133    if (type != TCL_UNIX_FD) {
134	panic("Tcl_FileReady: unexpected file type");
135    }
136
137    index = fd/(NBBY*sizeof(fd_mask));
138    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
139    result = 0;
140    if ((mask & TCL_READABLE) && (readyMasks[index] & bit)) {
141	result |= TCL_READABLE;
142    }
143    if ((mask & TCL_WRITABLE) && ((readyMasks+MASK_SIZE)[index] & bit)) {
144	result |= TCL_WRITABLE;
145    }
146    if ((mask & TCL_EXCEPTION) && ((readyMasks+(2*MASK_SIZE))[index] & bit)) {
147	result |= TCL_EXCEPTION;
148    }
149    return result;
150}
151
152/*
153 *----------------------------------------------------------------------
154 *
155 * Tcl_WaitForEvent --
156 *
157 *	This procedure does the lowest level wait for events in a
158 *	platform-specific manner.  It uses information provided by
159 *	previous calls to Tcl_WatchFile, plus the timePtr argument,
160 *	to determine what to wait for and how long to wait.
161 *
162 * Results:
163 *	None.
164 *
165 * Side effects:
166 *	May put the process to sleep for a while, depending on timePtr.
167 *	When this procedure returns, an event of interest to the application
168 *	has probably, but not necessarily, occurred.
169 *
170 *----------------------------------------------------------------------
171 */
172
173void
174Tcl_WaitForEvent(timePtr)
175    Tcl_Time *timePtr;		/* Specifies the maximum amount of time
176				 * that this procedure should block before
177				 * returning.  The time is given as an
178				 * interval, not an absolute wakeup time.
179				 * NULL means block forever. */
180{
181    struct timeval timeout, *timeoutPtr;
182    int numFound;
183
184    memcpy((VOID *) readyMasks, (VOID *) checkMasks,
185	    3*MASK_SIZE*sizeof(fd_mask));
186    if (timePtr == NULL) {
187	timeoutPtr = NULL;
188    } else {
189	timeoutPtr = &timeout;
190	timeout.tv_sec = timePtr->sec;
191	timeout.tv_usec = timePtr->usec;
192    }
193    numFound = select(numFdBits, (SELECT_MASK *) &readyMasks[0],
194	    (SELECT_MASK *) &readyMasks[MASK_SIZE],
195	    (SELECT_MASK *) &readyMasks[2*MASK_SIZE], timeoutPtr);
196
197    /*
198     * Some systems don't clear the masks after an error, so
199     * we have to do it here.
200     */
201
202    if (numFound == -1) {
203	memset((VOID *) readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
204    }
205
206    /*
207     * Reset the check masks in preparation for the next call to
208     * select.
209     */
210
211    numFdBits = 0;
212    memset((VOID *) checkMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
213}
214
215/*
216 *----------------------------------------------------------------------
217 *
218 * Tcl_Sleep --
219 *
220 *	Delay execution for the specified number of milliseconds.
221 *
222 * Results:
223 *	None.
224 *
225 * Side effects:
226 *	Time passes.
227 *
228 *----------------------------------------------------------------------
229 */
230
231void
232Tcl_Sleep(ms)
233    int ms;			/* Number of milliseconds to sleep. */
234{
235    static struct timeval delay;
236    Tcl_Time before, after;
237
238    /*
239     * The only trick here is that select appears to return early
240     * under some conditions, so we have to check to make sure that
241     * the right amount of time really has elapsed.  If it's too
242     * early, go back to sleep again.
243     */
244
245    TclGetTime(&before);
246    after = before;
247    after.sec += ms/1000;
248    after.usec += (ms%1000)*1000;
249    if (after.usec > 1000000) {
250	after.usec -= 1000000;
251	after.sec += 1;
252    }
253    while (1) {
254	delay.tv_sec = after.sec - before.sec;
255	delay.tv_usec = after.usec - before.usec;
256	if (delay.tv_usec < 0) {
257	    delay.tv_usec += 1000000;
258	    delay.tv_sec -= 1;
259	}
260
261	/*
262	 * Special note:  must convert delay.tv_sec to int before comparing
263	 * to zero, since delay.tv_usec is unsigned on some platforms.
264	 */
265
266	if ((((int) delay.tv_sec) < 0)
267		|| ((delay.tv_usec == 0) && (delay.tv_sec == 0))) {
268	    break;
269	}
270	(void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0,
271		(SELECT_MASK *) 0, &delay);
272	TclGetTime(&before);
273    }
274}
275
276
277
278
279
280
281
282#if 0 /* WHOLE FILE */
283
284
285
286/* interact (with only one process) - give user keyboard control
287
288Written by: Don Libes, NIST, 2/6/90
289
290Design and implementation of this program was paid for by U.S. tax
291dollars.  Therefore it is public domain.  However, the author and NIST
292would appreciate credit if this program or parts of it are used.
293*/
294
295/* This file exists for deficient versions of UNIX that lack select,
296poll, or some other multiplexing hook.  Instead, this code uses two
297processes per spawned process.  One sends characters from the spawnee
298to the spawner; a second send chars the other way.
299
300This will work on any UNIX system.  The only sacrifice is that it
301doesn't support multiple processes.  Eventually, it should catch
302SIGCHLD on dead processes and do the right thing.  But it is pretty
303gruesome to imagine so many processes to do all this.  If you change
304it successfully, please mail back the changes to me.  - Don
305*/
306
307#include "expect_cf.h"
308#include <stdio.h>
309#include <sys/types.h>
310#include <sys/time.h>
311
312#ifdef HAVE_SYS_WAIT_H
313#include <sys/wait.h>
314#endif
315
316#include "tcl.h"
317#include "exp_prog.h"
318#include "exp_command.h"	/* for struct ExpState defs */
319#include "exp_event.h"
320
321/*ARGSUSED*/
322void
323exp_arm_background_channelhandler(esPtr)
324ExpState *esPtr;
325{
326}
327
328/*ARGSUSED*/
329void
330exp_disarm_background_channelhandler(esPtr)
331ExpState *esPtr;
332{
333}
334
335/*ARGSUSED*/
336void
337exp_disarm_background_channelhandler_force(esPtr)
338ExpState *esPtr;
339{
340}
341
342/*ARGSUSED*/
343void
344exp_unblock_background_channelhandler(esPtr)
345ExpState *esPtr;
346{
347}
348
349/*ARGSUSED*/
350void
351exp_block_background_channelhandler(esPtr)
352ExpState *esPtr;
353{
354}
355
356/*ARGSUSED*/
357void
358exp_event_disarm(fd)
359int fd;
360{
361}
362
363/* returns status, one of EOF, TIMEOUT, ERROR or DATA */
364/*ARGSUSED*/
365int
366exp_get_next_event(interp,esPtrs, n,esPtrOut,timeout,key)
367Tcl_Interp *interp;
368ExpState (*esPtrs)[];
369int n;			/* # of esPtrs */
370ExpState **esPtrOut;	/* 1st event master, not set if none */
371int timeout;		/* seconds */
372int key;
373{
374    ExpState *esPtr;
375
376    if (n > 1) {
377	exp_error(interp,"expect not compiled with multiprocess support");
378	/* select a different INTERACT_TYPE in Makefile */
379	return(TCL_ERROR);
380    }
381
382    esPtr = *esPtrOut = esPtrs[0];
383
384    if (esPtr->key != key) {
385	esPtr->key = key;
386	esPtr->force_read = FALSE;
387	return(EXP_DATA_OLD);
388    } else if ((!esPtr->force_read) && (esPtr->size != 0)) {
389	return(EXP_DATA_OLD);
390    }
391
392    return(EXP_DATA_NEW);
393}
394
395/*ARGSUSED*/
396int
397exp_get_next_event_info(interp,esPtr,ready_mask)
398Tcl_Interp *interp;
399ExpState *esPtr;
400int ready_mask;
401{
402}
403
404/* There is no portable way to do sub-second sleeps on such a system, so */
405/* do the next best thing (without a busy loop) and fake it: sleep the right */
406/* amount of time over the long run.  Note that while "subtotal" isn't */
407/* reinitialized, it really doesn't matter for such a gross hack as random */
408/* scheduling pauses will easily introduce occasional one second delays. */
409int	/* returns TCL_XXX */
410exp_dsleep(interp,sec)
411Tcl_Interp *interp;
412double sec;
413{
414	static double subtotal = 0;
415	int seconds;
416
417	subtotal += sec;
418	if (subtotal < 1) return TCL_OK;
419	seconds = subtotal;
420	subtotal -= seconds;
421 restart:
422	if (Tcl_AsyncReady()) {
423		int rc = Tcl_AsyncInvoke(interp,TCL_OK);
424		if (rc != TCL_OK) return(rc);
425	}
426	sleep(seconds);
427	return TCL_OK;
428}
429
430#if 0
431/* There is no portable way to do sub-second sleeps on such a system, so */
432/* do the next best thing (without a busy loop) and fake it: sleep the right */
433/* amount of time over the long run.  Note that while "subtotal" isn't */
434/* reinitialized, it really doesn't matter for such a gross hack as random */
435/* scheduling pauses will easily introduce occasional one second delays. */
436int	/* returns TCL_XXX */
437exp_usleep(interp,usec)
438Tcl_Interp *interp;
439long usec;		/* microseconds */
440{
441	static subtotal = 0;
442	int seconds;
443
444	subtotal += usec;
445	if (subtotal < 1000000) return TCL_OK;
446	seconds = subtotal/1000000;
447	subtotal = subtotal%1000000;
448 restart:
449	if (Tcl_AsyncReady()) {
450		int rc = Tcl_AsyncInvoke(interp,TCL_OK);
451		if (rc != TCL_OK) return(exp_tcl2_returnvalue(rc));
452	}
453	sleep(seconds);
454	return TCL_OK;
455}
456#endif /*0*/
457
458/* set things up for later calls to event handler */
459void
460exp_init_event()
461{
462	exp_event_exit = 0;
463}
464
465#endif /* WHOLE FILE! */
466