1 /*
2 * Copyright 1993 Network Computing Devices, Inc.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name Network Computing Devices, Inc. not be
9 * used in advertising or publicity pertaining to distribution of this
10 * software without specific, written prior permission.
11 *
12 * THIS SOFTWARE IS PROVIDED `AS-IS'. NETWORK COMPUTING DEVICES, INC.,
13 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
14 * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
15 * PARTICULAR PURPOSE, OR NONINFRINGEMENT. IN NO EVENT SHALL NETWORK
16 * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
17 * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
18 * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
19 * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 *
22 * $NCDId: @(#)WaitFor.c,v 1.10 1996/04/24 17:15:18 greg Exp $
23 */
24 /***********************************************************
25 Some portions derived from:
26
27 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
28 and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
29
30 All Rights Reserved
31
32 Permission to use, copy, modify, and distribute this software and its
33 documentation for any purpose and without fee is hereby granted,
34 provided that the above copyright notice appear in all copies and that
35 both that copyright notice and this permission notice appear in
36 supporting documentation, and that the names of Digital or MIT not be
37 used in advertising or publicity pertaining to distribution of the
38 software without specific, written prior permission.
39
40 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
41 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
42 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
43 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
44 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
45 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
46 SOFTWARE.
47
48 ******************************************************************/
49
50 /*****************************************************************
51 * OS Depedent input routines:
52 *
53 * WaitForSomething, GetEvent
54 *
55 *****************************************************************/
56
57 #if defined(__CYGWIN__)
58 #define ioctl ioctlsocket
59 #define FIOSNBIO FIONBIO
60 #include <fcntl.h>
61 #include <sys/types.h>
62 #endif
63
64 #include <audio/audio.h>
65 #include <audio/Aproto.h>
66 #include <audio/Aos.h>
67
68 #include <errno.h>
69 #include <stdio.h>
70 #include "misc.h"
71
72 #ifndef _MINIX
73 #include <sys/param.h>
74 #endif /* !_MINIX */
75 #include <signal.h>
76
77 #ifdef _MINIX
78 #include <assert.h>
79 #endif /* _MINIX */
80
81 /* JET - an attempt to get us out of a race */
82 #ifdef hpux
83 #define WATCHDOG 60
84 #endif
85
86 #include "osdep.h"
87 #include "dixstruct.h"
88 #include "opaque.h"
89
90 #ifndef _MINIX
91 extern long AllSockets[];
92 extern long AllClients[];
93 extern long LastSelectMask[];
94 extern long WellKnownConnections;
95 extern long ClientsWithInput[];
96 extern long ClientsWriteBlocked[];
97 extern long OutputPending[];
98 extern int ConnectionTranslation[];
99 #else /* _MINIX */
100 extern asio_fd_set_t InprogressFdSet;
101 extern asio_fd_set_t ListenFdSet;
102 extern asio_fd_set_t ClientFdSet;
103 extern asio_fd_set_t CompletedFdSet;
104 extern asio_fd_set_t IgnoreFdSet;
105 extern asio_fd_set_t GrabFdSet;
106
107 extern Bool AnyClientsWithInput;
108 extern int GrabInProgress;
109
110 void EnqueueNewConnection();
111 void UpdateClientIOStatus();
112 #endif /* _MINIX */
113
114 extern Bool NewOutputPending;
115 #ifndef _MINIX
116 extern Bool AnyClientsWriteBlocked;
117 #endif
118
119 extern WorkQueuePtr workQueue;
120
121 #ifdef STARTSERVER
122 extern int timeoutWithNoClients;
123 #endif /* STARTSERVER */
124
125 extern void CheckConnections();
126 extern Bool EstablishNewConnections();
127 extern void ResetOsBuffers();
128
129 #ifdef apollo
130 extern long apInputMask[];
131
132 static long LastWriteMask[mskcnt];
133 #endif
134
135 #ifdef XTESTEXT1
136 /*
137 * defined in xtestext1dd.c
138 */
139 extern int playback_on;
140 #endif /* XTESTEXT1 */
141
142 #if defined(SYSV) || defined(SVR4)
143 #ifdef hpux
144 #define signal _local_signal
145 #define sigset _local_signal
146
147 #ifndef NeedFunctionPrototypes
148 static void (*_local_signal(sig, action)) ()
149 int
150 sig;
151 void (*action) ();
152 #else /* NeedFunctionPrototypes */
_local_signal(int sig,void (* action)(int))153 static void (*_local_signal(int sig, void (*action) (int))) (int)
154 #endif /* NeedFunctionPrototypes */
155 {
156 struct sigvec vec;
157 struct sigvec ovec;
158
159 vec.sv_handler = action;
160 vec.sv_flags = 0;
161 sigvector(sig, &vec, &ovec);
162
163 return (ovec.sv_handler);
164 }
165 #else
166 #define signal sigset
167 #endif
168 #endif
169
170 /*****************
171 * WaitForSomething:
172 * Make the server suspend until there is
173 * 1. data from clients or
174 * 2. clients that have buffered replies/events are ready
175 *
176 *****************/
177
178 #if !defined(AMOEBA) || !defined(_MINIX)
179 int
WaitForSomething(int * pClientsReady)180 WaitForSomething(int *pClientsReady)
181 {
182 int i;
183 struct timeval waittime, *wt;
184 #if defined(STARTSERVER) || defined(hpux)
185 struct timeval wtim;
186 #endif /* STARTSERVER */
187 long timeout;
188 long clientsReadable[mskcnt];
189 long clientsWritable[mskcnt];
190 long curclient;
191 int selecterr;
192 int nready;
193
194 CLEARBITS(clientsReadable);
195
196 /* We need a while loop here to handle
197 crashed connections and the screen saver timeout */
198 while (1) {
199 /* deal with any blocked jobs */
200 if (workQueue)
201 ProcessWorkQueue();
202
203 if (ANYSET(ClientsWithInput)) {
204 COPYBITS(ClientsWithInput, clientsReadable);
205 break;
206 }
207 #ifdef STARTSERVER
208 /* timeout if no clients */
209 if (timeoutWithNoClients > 0 && currentMaxClients < 2) {
210 wtim.tv_sec = timeoutWithNoClients;
211 wtim.tv_usec = 0;
212 wt = &wtim;
213 } else
214 #endif /* STARTSERVER */
215 wt = NULL;
216 /* XXX need to have ResetOsBuffers() somewhere */
217 COPYBITS(AllSockets, LastSelectMask);
218 #ifdef apollo
219 COPYBITS(apInputMask, LastWriteMask);
220 #endif
221 if (NewOutputPending)
222 FlushAllOutput();
223 /* keep this check close to select() call to minimize race */
224 if (dispatchException)
225 i = -1;
226 else if (AnyClientsWriteBlocked) {
227 COPYBITS(ClientsWriteBlocked, clientsWritable);
228 #ifdef hpux
229 i = select(MAXSOCKS, (int *) LastSelectMask,
230 (int *) clientsWritable, (int *) NULL, wt);
231 #else
232 i = select(MAXSOCKS, (fd_set *) LastSelectMask,
233 (fd_set *) clientsWritable, (fd_set *) NULL, wt);
234 #endif
235 } else
236 #ifdef apollo
237 i = select(MAXSOCKS, (int *) LastSelectMask,
238 (fd_set *) LastWriteMask, (fd_set *) NULL, wt);
239 #else
240 #ifdef hpux
241 /* JET - 12/30/99 - let's try to time out once and awhile */
242
243 wtim.tv_sec = WATCHDOG;
244 wtim.tv_usec = 0;
245 wt = &wtim;
246
247 i = select(MAXSOCKS, (int *) LastSelectMask,
248 (int *) NULL, (int *) NULL, wt);
249 #else
250 i = select(MAXSOCKS, (fd_set *) LastSelectMask,
251 (fd_set *) NULL, (fd_set *) NULL, wt);
252 #endif
253 #endif
254 selecterr = errno;
255 if (i <= 0) { /* An error or timeout occurred */
256
257 if (dispatchException)
258 return 0;
259 CLEARBITS(clientsWritable);
260 if (i < 0) {
261 if (selecterr == EBADF) { /* Some client disconnected */
262 CheckConnections();
263 if (!ANYSET(AllClients))
264 return 0;
265 } else if (selecterr != EINTR)
266 ErrorF("WaitForSomething(): select: errno=%d\n",
267 selecterr);
268 }
269 #ifdef STARTSERVER
270 else {
271 /* terminate if timed out */
272 dispatchException |= DE_TERMINATE;
273 isItTimeToYield = TRUE;
274 return (0);
275 }
276 #endif /* STARTSERVER */
277
278 {
279 extern pointer AuEventQueue;
280
281 if (AuEventQueue)
282 return 0;
283 }
284 } else {
285 if (AnyClientsWriteBlocked && ANYSET(clientsWritable)) {
286 NewOutputPending = TRUE;
287 ORBITS(OutputPending, clientsWritable, OutputPending);
288 UNSETBITS(ClientsWriteBlocked, clientsWritable);
289 if (!ANYSET(ClientsWriteBlocked))
290 AnyClientsWriteBlocked = FALSE;
291 }
292
293 MASKANDSETBITS(clientsReadable, LastSelectMask, AllClients);
294 if (LastSelectMask[0] & WellKnownConnections)
295 QueueWorkProc(EstablishNewConnections, NULL,
296 (pointer) LastSelectMask[0]);
297 if (ANYSET(clientsReadable))
298 break;
299 }
300 }
301
302 nready = 0;
303 if (ANYSET(clientsReadable)) {
304 for (i = 0; i < mskcnt; i++) {
305 while (clientsReadable[i]) {
306 curclient = ffs(clientsReadable[i]) - 1;
307 pClientsReady[nready++] =
308 ConnectionTranslation[curclient + (i << 5)];
309 clientsReadable[i] &= ~(1 << curclient);
310 }
311 }
312 }
313 return nready;
314 }
315
316 #ifndef ANYSET
317 /*
318 * This is not always a macro.
319 */
ANYSET(long * src)320 ANYSET(long *src)
321 {
322 int i;
323
324 for (i = 0; i < mskcnt; i++)
325 if (src[i])
326 return (TRUE);
327 return (FALSE);
328 }
329 #endif
330
331 #else /* AMOEBA || _MINIX */
332 #ifdef AMOEBA
333 int init_waiters = 0;
334 semaphore init_sema;
335
336 /*
337 * Force caller thread to wait until main has
338 * finished the initialization
339 */
340 void
WaitForInitialization(void)341 WaitForInitialization(void)
342 {
343 init_waiters++;
344 sema_down(&init_sema);
345 }
346
347 static semaphore main_sema;
348
349 /*
350 * The audio-server consists of one main thread, running the non re-entrant
351 * audio code, and a number of auxilary threads that take care of reading
352 * the input streams, and input devices. The following set of routines
353 * wake up the main thread when it has something to do.
354 */
355 void
InitMainThread(void)356 InitMainThread(void)
357 {
358 sema_init(&main_sema, 0);
359 }
360
361 void
WakeUpMainThread(void)362 WakeUpMainThread(void)
363 {
364 sema_up(&main_sema);
365 }
366
367 static int
SleepMainThread(interval timo)368 SleepMainThread(interval timo)
369 {
370
371 return (sema_trydown(&main_sema, timo) == 0) ? 0 : -1;
372 }
373
374 int
WaitForSomething(int * pClientsReady)375 WaitForSomething(int *pClientsReady)
376 {
377 int i, wt, nt;
378 struct timeval *wtp;
379 long alwaysCheckForInput[2];
380 int nready;
381
382 /*
383 * First, wakeup threads in initial sleep
384 */
385 if (init_waiters > 0) {
386 while (init_waiters-- > 0)
387 sema_up(&init_sema);
388 }
389
390 /*
391 * Be sure to check for input on every sweep in the dispatcher.
392 * This routine should be in InitInput, but since this is more
393 * or less a device dependent routine, and the semantics of it
394 * are device independent I decided to put it here.
395 */
396 alwaysCheckForInput[0] = 0;
397 alwaysCheckForInput[1] = 1;
398 SetInputCheck(&alwaysCheckForInput[0], &alwaysCheckForInput[1]);
399
400 while (TRUE) {
401 /*
402 * Deal with any blocked jobs
403 */
404 if (workQueue)
405 ProcessWorkQueue();
406
407 wt = -1;
408
409 /*
410 * Check for new clients. We do this here and not in the
411 * listener() threads because we cannot be sure that dix
412 * is re-entrant, and we need to call some dix routines
413 * during startup.
414 */
415 if (nNewConns)
416 EstablishNewConnections();
417
418 /*
419 * Every device driver can install handlers which are called after
420 * a certain amount of time. These handlers implement, for example,
421 * key repeat on a Sun. The following construction will determine
422 * the correct time-out value, given the available handlers. For
423 * compatibility the timeout is a timeval structure.
424 */
425 wtp = (struct timeval *) NULL;
426 BlockHandler((pointer) & wtp, (pointer) NULL);
427 if (wtp)
428 wt = (wtp->tv_sec * 1000) + (wtp->tv_usec / 1000);
429
430 /*
431 * Check for clients needing attention. They might want to
432 * die or they might have input. In the second case, only
433 * accept from the grabClient (if there is one).
434 */
435 for (i = 0, nready = 0; i < maxClient; i++) {
436 if (Clients[i] && Clients[i]->osPrivate) {
437 OsCommPtr oc = (OsCommPtr) Clients[i]->osPrivate;
438 int n;
439
440 if (oc->status & CONN_KILLED) {
441 CloseDownClient(Clients[i]);
442 if (maxClient == i)
443 maxClient--;
444 continue;
445 }
446 if ((n = am_avail(oc, VC_IN)) < 0) {
447 CloseDownClient(Clients[i]);
448 if (maxClient == i)
449 maxClient--;
450 continue;
451 }
452 if (n > 0 || oc->status & REQ_PUSHBACK) {
453 if (grabClient == NULL || grabClient == Clients[i]) {
454 *pClientsReady++ = Clients[i]->index;
455 nready++;
456 }
457 }
458 }
459 }
460
461 /*
462 * Well, if we found some work, or the hardware has
463 * events available, we return.
464 */
465 if (nready || AmoebaEventsAvailable())
466 break;
467
468 /*
469 * Nothing interesting is available. Go to sleep with a
470 * timeout and the other threads will wake us when needed.
471 */
472 if (dispatchException)
473 return 0;
474 i = SleepMainThread(wt);
475
476 /*
477 * Wake up any of the sleeping handlers
478 */
479 WakeupHandler((unsigned long) 0, (pointer) NULL);
480 if (dispatchException)
481 return 0;
482
483 /*
484 * An error or timeout occurred
485 */
486 if (i == -1)
487 return 0;
488 }
489 return nready;
490 }
491 #endif /* AMOEBA */
492
493 #ifdef _MINIX
494 static int timed_fwait();
495
496 int
WaitForSomething(int * pClientsReady)497 WaitForSomething(int *pClientsReady)
498 {
499 long timeout;
500 struct timeval waittime[2];
501 int nready;
502 struct fwait fw;
503 asio_fd_set_t wait_fd_set;
504 int i, r;
505 int fw_fd, fw_operation, fw_result, fw_errno;
506
507 /* We need a while loop here to handle
508 crashed connections and the screen saver timeout */
509 nready = 0;
510 while (1) {
511 /* deal with any blocked jobs */
512 if (workQueue)
513 ProcessWorkQueue();
514
515 wait_fd_set = InprogressFdSet;
516
517 /* Let's fill in the fwait structure */
518 fw.fw_flags = FWF_MORE; /* Ignored by the kernel, but makes loop
519 * termination easier
520 */
521
522 fw.fw_bits = wait_fd_set.afds_bits;
523 fw.fw_maxfd = ASIO_FD_SETSIZE;
524
525 if (AnyClientsWithInput)
526 fw.fw_flags |= FWF_NONBLOCK;
527
528 BlockHandler((pointer) waittime, (pointer) & wait_fd_set);
529 if (NewOutputPending)
530 FlushAllOutput();
531 for (; fw.fw_flags & FWF_MORE; fw.fw_flags |= FWF_NONBLOCK) {
532 /* keep this check close to select() call to minimize race */
533 if (dispatchException) {
534 r = -1;
535 errno = EAGAIN;
536 } else if (waittime[0].tv_sec != 0) {
537 assert(waittime[0].tv_sec >= waittime[1].tv_sec);
538 r = timed_fwait(&fw, &waittime[0]);
539 } else
540 r = fwait(&fw);
541 if (r == -1)
542 break;
543 fw_fd = fw.fw_fd;
544 fw_operation = fw.fw_operation;
545 fw_result = fw.fw_result;
546 fw_errno = fw.fw_errno;
547 if (ASIO_FD_ISSET(fw_fd, fw_operation, &ListenFdSet)) {
548 /* Got a new connection */
549 EnqueueNewConnection(fw_fd, fw_operation, fw_result,
550 fw_errno);
551 fw.fw_fd = -1;
552 continue;
553 }
554 if (ASIO_FD_ISSET(fw_fd, fw_operation, &ClientFdSet)) {
555 /* The read or write of a client is done */
556 UpdateClientIOStatus(fw_fd, fw_operation, fw_result,
557 fw_errno);
558 fw.fw_fd = -1;
559
560 /* Store the client in the ready set if the operation was a
561 * read
562 */
563 if (fw_operation == ASIO_READ) {
564 pClientsReady[nready++] = ConnectionTranslation[fw_fd];
565 }
566 continue;
567 }
568
569 /* Only thing left is a device, let's break out of this loop */
570 break;
571 }
572 if (r == -1) {
573 if (errno != EINTR && errno != EAGAIN)
574 FatalError("WaitForSomething(): fwait error: %s\n",
575 strerror(errno));
576 WakeupHandler(0, NULL); /* Nothing happend to a device */
577 } else {
578 WakeupHandler(0, (pointer) & fw);
579 if (fw.fw_fd != -1)
580 FatalError("Unable to locate module for completed I/O\n");
581 }
582 if (r < 0) { /* An error or timeout occurred */
583
584 if (dispatchException)
585 return 0;
586 if (*checkForInput[0] != *checkForInput[1])
587 return 0;
588 }
589
590 break;
591 }
592
593 if (AnyClientsWithInput) {
594 /* These were left over from the previous round */
595 AnyClientsWithInput = FALSE;
596 for (i = 0; i < ASIO_FD_SETSIZE; i++) {
597 if (ASIO_FD_ISSET(i, ASIO_READ, &CompletedFdSet)) {
598 if (GrabInProgress && !ASIO_FD_ISSET(i, ASIO_READ,
599 &GrabFdSet)) {
600 continue;
601 }
602 if (ASIO_FD_ISSET(i, ASIO_READ, &IgnoreFdSet))
603 continue;
604 pClientsReady[nready++] = ConnectionTranslation[i];
605 }
606 }
607 }
608 return nready;
609 }
610
611 static int timer_running = FALSE;
612 static struct timeval timer_value;
613 static int tf_critical = FALSE;
614 static int tf_expired = FALSE;
615
616 void
tf_handler(int sig)617 tf_handler(int sig)
618 {
619 struct timeval tv;
620
621 tf_expired = TRUE;
622 if (!tf_critical) {
623 timer_running = FALSE;
624 return;
625 }
626 signal(SIGALRM, tf_handler);
627 sysutime(UTIME_TIMEOFDAY, &tv);
628 tv.tv_sec++;
629 /* Fake a high value to force to timer to be reloaded */
630 timer_value.tv_sec = tv.tv_sec + 1000000;
631 sysutime(UTIME_SETALARM, &tv);
632 }
633
634 static int
timed_fwait(struct fwait * fwp,struct timeval * tvp)635 timed_fwait(struct fwait *fwp, struct timeval *tvp)
636 {
637 struct timeval tv;
638 int r;
639
640 #if DEBUG & 256
641 {
642 sysutime(UTIME_TIMEOFDAY, &tv);
643 tv.tv_sec = tvp->tv_sec - tv.tv_sec;
644 tv.tv_usec = tvp->tv_usec - tv.tv_usec;
645 if (tv.tv_usec < 0) {
646 tv.tv_usec += 1000000;
647 tv.tv_sec--;
648 }
649 ErrorF("timed_fwait: timeout at now+%d.%06d\n", tv.tv_sec,
650 tv.tv_usec);
651 }
652 #endif
653
654 /* Assume that only timed_fwait is allowed to let the timer running
655 * between calls.
656 */
657 tv = *tvp;
658 if (!timer_running) {
659 signal(SIGALRM, tf_handler);
660 sysutime(UTIME_SETALARM, &tv);
661 assert(tv.tv_sec == 0);
662 timer_value = *tvp;
663 timer_running = TRUE;
664 } else if (tv.tv_sec < timer_value.tv_sec ||
665 (tv.tv_sec == timer_value.tv_sec &&
666 tv.tv_usec < timer_value.tv_usec)) {
667 /* If the previous timer expires just before we set the
668 * new timer, the handler is lost unless tf_critical is set,
669 * then the handler will set a new new timer 1 second later.
670 */
671 tf_critical = TRUE;
672 sysutime(UTIME_SETALARM, &tv);
673 timer_value = *tvp;
674 tf_critical = FALSE;
675 }
676 tf_critical = TRUE;
677 if (tf_expired) {
678 tf_critical = FALSE;
679 tf_expired = FALSE;
680 errno = EINTR;
681 return -1;
682 }
683 r = fwait(fwp);
684 tf_critical = FALSE;
685 return r;
686 }
687 #endif /* _MINIX */
688 #endif /* AMOEBA || _MINIX */
689