1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3
4 /*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14 #include "sfhdr.h"
15
16 /* Poll a set of streams to see if any is available for I/O.
17 ** Ready streams are moved to front of array but retain the
18 ** same relative order.
19 **
20 ** Written by Kiem-Phong Vo.
21 */
22
23 /**
24 * @param fa array of streams to poll
25 * @param n number of streams in array
26 * @param tm the amount of time in ms to wait for selecting
27 */
sfpoll(Sfio_t ** fa,reg int n,int tm)28 int sfpoll(Sfio_t ** fa, reg int n, int tm)
29 {
30 reg int r, c, m;
31 reg Sfio_t *f;
32 reg Sfdisc_t *d;
33 reg int *status, *check;
34
35 if (n <= 0 || !fa)
36 return -1;
37
38 if (!(status = (int *) malloc(2 * n * sizeof(int))))
39 return -1;
40 else
41 check = status + n;
42
43 /* this loop partitions the streams into 3 sets: Check, Ready, Notready */
44 retry:for (r = c = 0; r < n; ++r) {
45 f = fa[r];
46
47 /* this loop pops a stream stack as necessary */
48 for (;;) { /* check accessibility */
49 m = f->mode & SF_RDWR;
50 if ((int) f->mode != m && _sfmode(f, m, 0) < 0)
51 goto do_never;
52
53 /* clearly ready */
54 if (f->next < f->endb)
55 goto do_ready;
56
57 /* has discipline, ask its opinion */
58 for (d = f->disc; d; d = d->disc)
59 if (d->exceptf)
60 break;
61 if (d) {
62 if ((m = (*d->exceptf) (f, SF_DPOLL, &tm, d)) < 0)
63 goto do_never;
64 else if (m > 0)
65 goto do_ready;
66 /*else check file descriptor */
67 }
68
69 /* unseekable stream, must check for blockability */
70 if (f->extent < 0)
71 goto do_check;
72
73 /* string/regular streams with no possibility of blocking */
74 if (!f->push)
75 goto do_ready;
76
77 /* stacked regular file stream with I/O possibility */
78 if (!(f->flags & SF_STRING) &&
79 ((f->mode & SF_WRITE) || f->here < f->extent))
80 goto do_ready;
81
82 /* at an apparent eof, pop stack if ok, then recheck */
83 SETLOCAL(f);
84 switch (_sfexcept(f, f->mode & SF_RDWR, 0, f->disc)) {
85 case SF_EDONE:
86 if (f->flags & SF_STRING)
87 goto do_never;
88 else
89 goto do_ready;
90 case SF_EDISC:
91 if (f->flags & SF_STRING)
92 goto do_ready;
93 case SF_ESTACK:
94 case SF_ECONT:
95 continue;
96 }
97 }
98
99 do_check: /* local function to set a stream for further checking */
100 {
101 status[r] = 0;
102 check[c] = r;
103 c += 1;
104 continue;
105 }
106
107 do_ready: /* local function to set the ready streams */
108 {
109 status[r] = 1;
110 continue;
111 }
112
113 do_never: /* local function to set the not-ready streams */
114 {
115 status[r] = -1;
116 continue;
117 }
118 }
119
120 #if _lib_poll
121 if (c > 0) {
122 struct pollfd *fds;
123
124 /* construct the poll array */
125 if (!(fds = (struct pollfd *) malloc(c * sizeof(struct pollfd))))
126 return -1;
127 for (r = 0; r < c; r++) {
128 fds[r].fd = fa[check[r]]->file;
129 fds[r].events =
130 (fa[check[r]]->mode & SF_READ) ? POLLIN : POLLOUT;
131 fds[r].revents = 0;
132 }
133
134 for (;;) { /* this loop takes care of interrupts */
135 if ((r = SFPOLL(fds, c, tm)) == 0)
136 break;
137 else if (r < 0) {
138 if (errno == EINTR || errno == EAGAIN) {
139 errno = 0;
140 continue;
141 } else
142 break;
143 }
144
145 for (r = 0; r < c; ++r) {
146 f = fa[check[r]];
147 if (((f->mode & SF_READ) && (fds[r].revents & POLLIN)) ||
148 ((f->mode & SF_WRITE) && (fds[r].revents & POLLOUT)))
149 status[check[r]] = 1;
150 }
151 break;
152 }
153
154 free((void *) fds);
155 }
156 #endif /*_lib_poll*/
157
158 #ifdef HAVE_SELECT
159 if (c > 0) {
160 fd_set rd, wr;
161 struct timeval tmb, *tmp;
162
163 FD_ZERO(&rd);
164 FD_ZERO(&wr);
165 m = 0;
166 for (r = 0; r < c; ++r) {
167 f = fa[check[r]];
168 if (f->file > m)
169 m = f->file;
170 if (f->mode & SF_READ)
171 FD_SET(f->file, &rd);
172 else
173 FD_SET(f->file, &wr);
174 }
175 if (tm < 0)
176 tmp = NIL(struct timeval *);
177 else {
178 tmp = &tmb;
179 tmb.tv_sec = tm / SECOND;
180 tmb.tv_usec = (tm % SECOND) * SECOND;
181 }
182 for (;;) {
183 if ((r = select(m + 1, &rd, &wr, NIL(fd_set *), tmp)) == 0)
184 break;
185 else if (r < 0) {
186 if (errno == EINTR)
187 continue;
188 else
189 break;
190 }
191
192 for (r = 0; r < c; ++r) {
193 f = fa[check[r]];
194 if (((f->mode & SF_READ) && FD_ISSET(f->file, &rd)) ||
195 ((f->mode & SF_WRITE) && FD_ISSET(f->file, &wr)))
196 status[check[r]] = 1;
197 }
198 break;
199 }
200 }
201 #endif /*HAVE_SELECT*/
202
203 /* call exception functions */
204 for (c = 0; c < n; ++c) {
205 if (status[c] <= 0)
206 continue;
207 if ((d = fa[c]->disc) && d->exceptf) {
208 if ((r = (*d->exceptf) (fa[c], SF_READY, (void *) 0, d)) < 0)
209 goto done;
210 else if (r > 0)
211 goto retry;
212 }
213 }
214
215 /* move ready streams to the front */
216 for (r = c = 0; c < n; ++c) {
217 if (status[c] > 0) {
218 if (c > r) {
219 f = fa[r];
220 fa[r] = fa[c];
221 fa[c] = f;
222 }
223 r += 1;
224 }
225 }
226
227 done:
228 free((void *) status);
229 return r;
230 }
231