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.
8 * Copyright (c) 1995 Sun Microsystems, Inc.
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 static char sccsid[] = "@(#) tclUnixNotify.c 1.27 96/01/19 10:30:23";
21 * The information below is used to provide read, write, and
22 * exception masks to select during calls to Tcl_DoOneEvent.
25 static 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
30 static 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. */
34 static int numFdBits; /* Number of valid bits in checkMasks
35 * (one more than highest fd for which
36 * Tcl_WatchFile has been called). */
39 *----------------------------------------------------------------------
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.
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
60 *----------------------------------------------------------------------
64 Tcl_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
74 fd = (int) Tcl_GetFileInfo(file, &type);
76 if (type != TCL_UNIX_FD) {
77 panic("Tcl_WatchFile: unexpected file type");
80 if (fd >= FD_SETSIZE) {
81 panic("Tcl_WatchFile can't handle file id %d", fd);
84 index = fd/(NBBY*sizeof(fd_mask));
85 bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
86 if (mask & TCL_READABLE) {
87 checkMasks[index] |= bit;
89 if (mask & TCL_WRITABLE) {
90 (checkMasks+MASK_SIZE)[index] |= bit;
92 if (mask & TCL_EXCEPTION) {
93 (checkMasks+2*(MASK_SIZE))[index] |= bit;
95 if (numFdBits <= fd) {
101 *----------------------------------------------------------------------
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.
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
119 *----------------------------------------------------------------------
123 Tcl_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. */
129 int index, result, type, fd;
132 fd = (int) Tcl_GetFileInfo(file, &type);
133 if (type != TCL_UNIX_FD) {
134 panic("Tcl_FileReady: unexpected file type");
137 index = fd/(NBBY*sizeof(fd_mask));
138 bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
140 if ((mask & TCL_READABLE) && (readyMasks[index] & bit)) {
141 result |= TCL_READABLE;
143 if ((mask & TCL_WRITABLE) && ((readyMasks+MASK_SIZE)[index] & bit)) {
144 result |= TCL_WRITABLE;
146 if ((mask & TCL_EXCEPTION) && ((readyMasks+(2*MASK_SIZE))[index] & bit)) {
147 result |= TCL_EXCEPTION;
153 *----------------------------------------------------------------------
155 * Tcl_WaitForEvent --
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.
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.
170 *----------------------------------------------------------------------
174 Tcl_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. */
181 struct timeval timeout, *timeoutPtr;
184 memcpy((VOID *) readyMasks, (VOID *) checkMasks,
185 3*MASK_SIZE*sizeof(fd_mask));
186 if (timePtr == NULL) {
189 timeoutPtr = &timeout;
190 timeout.tv_sec = timePtr->sec;
191 timeout.tv_usec = timePtr->usec;
193 numFound = select(numFdBits, (SELECT_MASK *) &readyMasks[0],
194 (SELECT_MASK *) &readyMasks[MASK_SIZE],
195 (SELECT_MASK *) &readyMasks[2*MASK_SIZE], timeoutPtr);
198 * Some systems don't clear the masks after an error, so
199 * we have to do it here.
202 if (numFound == -1) {
203 memset((VOID *) readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
207 * Reset the check masks in preparation for the next call to
212 memset((VOID *) checkMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
216 *----------------------------------------------------------------------
220 * Delay execution for the specified number of milliseconds.
228 *----------------------------------------------------------------------
233 int ms; /* Number of milliseconds to sleep. */
235 static struct timeval delay;
236 Tcl_Time before, after;
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.
247 after.sec += ms/1000;
248 after.usec += (ms%1000)*1000;
249 if (after.usec > 1000000) {
250 after.usec -= 1000000;
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;
262 * Special note: must convert delay.tv_sec to int before comparing
263 * to zero, since delay.tv_usec is unsigned on some platforms.
266 if ((((int) delay.tv_sec) < 0)
267 || ((delay.tv_usec == 0) && (delay.tv_sec == 0))) {
270 (void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0,
271 (SELECT_MASK *) 0, &delay);
282 #if 0 /* WHOLE FILE */
286 /* interact (with only one process) - give user keyboard control
288 Written by: Don Libes, NIST, 2/6/90
290 Design and implementation of this program was paid for by U.S. tax
291 dollars. Therefore it is public domain. However, the author and NIST
292 would appreciate credit if this program or parts of it are used.
295 /* This file exists for deficient versions of UNIX that lack select,
296 poll, or some other multiplexing hook. Instead, this code uses two
297 processes per spawned process. One sends characters from the spawnee
298 to the spawner; a second send chars the other way.
300 This will work on any UNIX system. The only sacrifice is that it
301 doesn't support multiple processes. Eventually, it should catch
302 SIGCHLD on dead processes and do the right thing. But it is pretty
303 gruesome to imagine so many processes to do all this. If you change
304 it successfully, please mail back the changes to me. - Don
307 #include "expect_cf.h"
309 #include <sys/types.h>
310 #include <sys/time.h>
312 #ifdef HAVE_SYS_WAIT_H
313 #include <sys/wait.h>
317 #include "exp_prog.h"
318 #include "exp_command.h" /* for struct ExpState defs */
319 #include "exp_event.h"
323 exp_arm_background_channelhandler(esPtr)
330 exp_disarm_background_channelhandler(esPtr)
337 exp_disarm_background_channelhandler_force(esPtr)
344 exp_unblock_background_channelhandler(esPtr)
351 exp_block_background_channelhandler(esPtr)
363 /* returns status, one of EOF, TIMEOUT, ERROR or DATA */
366 exp_get_next_event(interp,esPtrs, n,esPtrOut,timeout,key)
368 ExpState (*esPtrs)[];
369 int n; /* # of esPtrs */
370 ExpState **esPtrOut; /* 1st event master, not set if none */
371 int timeout; /* seconds */
377 exp_error(interp,"expect not compiled with multiprocess support");
378 /* select a different INTERACT_TYPE in Makefile */
382 esPtr = *esPtrOut = esPtrs[0];
384 if (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);
392 return(EXP_DATA_NEW);
397 exp_get_next_event_info(interp,esPtr,ready_mask)
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. */
409 int /* returns TCL_XXX */
410 exp_dsleep(interp,sec)
414 static double subtotal = 0;
418 if (subtotal < 1) return TCL_OK;
422 if (Tcl_AsyncReady()) {
423 int rc = Tcl_AsyncInvoke(interp,TCL_OK);
424 if (rc != TCL_OK) return(rc);
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. */
436 int /* returns TCL_XXX */
437 exp_usleep(interp,usec)
439 long usec; /* microseconds */
445 if (subtotal < 1000000) return TCL_OK;
446 seconds = subtotal/1000000;
447 subtotal = subtotal%1000000;
449 if (Tcl_AsyncReady()) {
450 int rc = Tcl_AsyncInvoke(interp,TCL_OK);
451 if (rc != TCL_OK) return(exp_tcl2_returnvalue(rc));
458 /* set things up for later calls to event handler */
465 #endif /* WHOLE FILE! */