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