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