2 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (c) 1995-1999 by Internet Software Consortium
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 /* ev_files.c - implement asynch file IO for the eventlib
19 * vix 11sep95 [initial]
22 #if !defined(LINT) && !defined(CODECENTER)
23 static const char rcsid
[] = "$Id: ev_files.c,v 1.8 2005/07/28 06:51:48 marka Exp $";
26 #include "port_before.h"
27 #include "fd_setsize.h"
29 #include <sys/types.h>
31 #include <sys/ioctl.h>
37 #include <isc/eventlib.h>
38 #include "eventlib_p.h"
40 #include "port_after.h"
42 static evFile
*FindFD(const evContext_p
*ctx
, int fd
, int eventmask
);
45 evSelectFD(evContext opaqueCtx
,
52 evContext_p
*ctx
= opaqueCtx
.opaque
;
57 "evSelectFD(ctx %p, fd %d, mask 0x%x, func %p, uap %p)\n",
58 ctx
, fd
, eventmask
, func
, uap
);
59 if (eventmask
== 0 || (eventmask
& ~EV_MASK_ALL
) != 0)
62 if (fd
> ctx
->highestFD
)
65 OK(mode
= fcntl(fd
, F_GETFL
, NULL
)); /*%< side effect: validate fd. */
67 * The first time we touch a file descriptor, we need to check to see
68 * if the application already had it in O_NONBLOCK mode and if so, all
69 * of our deselect()'s have to leave it in O_NONBLOCK. If not, then
70 * all but our last deselect() has to leave it in O_NONBLOCK.
73 /* Make sure both ctx->pollfds[] and ctx->fdTable[] are large enough */
74 if (fd
>= ctx
->maxnfds
&& evPollfdRealloc(ctx
, 1, fd
) != 0)
77 id
= FindFD(ctx
, fd
, EV_MASK_ALL
);
79 if (mode
& PORT_NONBLOCK
)
80 FD_SET(fd
, &ctx
->nonblockBefore
);
82 #ifdef USE_FIONBIO_IOCTL
84 OK(ioctl(fd
, FIONBIO
, (char *)&on
));
86 OK(fcntl(fd
, F_SETFL
, mode
| PORT_NONBLOCK
));
88 FD_CLR(fd
, &ctx
->nonblockBefore
);
93 * If this descriptor is already in use, search for it again to see
94 * if any of the eventmask bits we want to set are already captured.
95 * We cannot usefully capture the same fd event more than once in the
98 if (id
!= NULL
&& FindFD(ctx
, fd
, eventmask
) != NULL
)
101 /* Allocate and fill. */
106 id
->eventmask
= eventmask
;
109 * Insert at head. Order could be important for performance if we
110 * believe that evGetNext()'s accesses to the fd_sets will be more
111 * serial and therefore more cache-lucky if the list is ordered by
112 * ``fd.'' We do not believe these things, so we don't do it.
114 * The interesting sequence is where GetNext() has cached a select()
115 * result and the caller decides to evSelectFD() on some descriptor.
116 * Since GetNext() starts at the head, it can miss new entries we add
117 * at the head. This is not a serious problem since the event being
118 * evSelectFD()'d for has to occur before evSelectFD() is called for
119 * the file event to be considered "missed" -- a real corner case.
120 * Maintaining a "tail" pointer for ctx->files would fix this, but I'm
121 * not sure it would be ``more correct.''
123 if (ctx
->files
!= NULL
)
124 ctx
->files
->prev
= id
;
126 id
->next
= ctx
->files
;
129 /* Insert into fd table. */
130 if (ctx
->fdTable
[fd
] != NULL
)
131 ctx
->fdTable
[fd
]->fdprev
= id
;
133 id
->fdnext
= ctx
->fdTable
[fd
];
134 ctx
->fdTable
[fd
] = id
;
136 /* Turn on the appropriate bits in the {rd,wr,ex}Next fd_set's. */
137 if (eventmask
& EV_READ
)
138 FD_SET(fd
, &ctx
->rdNext
);
139 if (eventmask
& EV_WRITE
)
140 FD_SET(fd
, &ctx
->wrNext
);
141 if (eventmask
& EV_EXCEPT
)
142 FD_SET(fd
, &ctx
->exNext
);
148 /* Remember the ID if the caller provided us a place for it. */
150 opaqueID
->opaque
= id
;
156 evDeselectFD(evContext opaqueCtx
, evFileID opaqueID
) {
157 evContext_p
*ctx
= opaqueCtx
.opaque
;
158 evFile
*del
= opaqueID
.opaque
;
163 evPrintf(ctx
, 11, "evDeselectFD(NULL) ignored\n");
168 evPrintf(ctx
, 1, "evDeselectFD(fd %d, mask 0x%x)\n",
169 del
->fd
, del
->eventmask
);
171 /* Get the mode. Unless the file has been closed, errors are bad. */
172 mode
= fcntl(del
->fd
, F_GETFL
, NULL
);
173 if (mode
== -1 && errno
!= EBADF
)
176 /* Remove from the list of files. */
177 if (del
->prev
!= NULL
)
178 del
->prev
->next
= del
->next
;
180 ctx
->files
= del
->next
;
181 if (del
->next
!= NULL
)
182 del
->next
->prev
= del
->prev
;
184 /* Remove from the fd table. */
185 if (del
->fdprev
!= NULL
)
186 del
->fdprev
->fdnext
= del
->fdnext
;
188 ctx
->fdTable
[del
->fd
] = del
->fdnext
;
189 if (del
->fdnext
!= NULL
)
190 del
->fdnext
->fdprev
= del
->fdprev
;
193 * If the file descriptor does not appear in any other select() entry,
194 * and if !EV_WASNONBLOCK, and if we got no EBADF when we got the mode
195 * earlier, then: restore the fd to blocking status.
197 if (!(cur
= FindFD(ctx
, del
->fd
, EV_MASK_ALL
)) &&
198 !FD_ISSET(del
->fd
, &ctx
->nonblockBefore
) &&
201 * Note that we won't return an error status to the caller if
202 * this fcntl() fails since (a) we've already done the work
203 * and (b) the caller didn't ask us anything about O_NONBLOCK.
205 #ifdef USE_FIONBIO_IOCTL
207 (void) ioctl(del
->fd
, FIONBIO
, (char *)&off
);
209 (void) fcntl(del
->fd
, F_SETFL
, mode
& ~PORT_NONBLOCK
);
214 * Now find all other uses of this descriptor and OR together an event
215 * mask so that we don't turn off {rd,wr,ex}Next bits that some other
216 * file event is using. As an optimization, stop if the event mask
221 cur
!= NULL
&& eventmask
!= EV_MASK_ALL
;
223 if (cur
->fd
== del
->fd
)
224 eventmask
|= cur
->eventmask
;
226 /* OK, now we know which bits we can clear out. */
227 if (!(eventmask
& EV_READ
)) {
228 FD_CLR(del
->fd
, &ctx
->rdNext
);
229 if (FD_ISSET(del
->fd
, &ctx
->rdLast
)) {
230 FD_CLR(del
->fd
, &ctx
->rdLast
);
234 if (!(eventmask
& EV_WRITE
)) {
235 FD_CLR(del
->fd
, &ctx
->wrNext
);
236 if (FD_ISSET(del
->fd
, &ctx
->wrLast
)) {
237 FD_CLR(del
->fd
, &ctx
->wrLast
);
241 if (!(eventmask
& EV_EXCEPT
)) {
242 FD_CLR(del
->fd
, &ctx
->exNext
);
243 if (FD_ISSET(del
->fd
, &ctx
->exLast
)) {
244 FD_CLR(del
->fd
, &ctx
->exLast
);
249 /* If this was the maxFD, find the new one. */
250 if (del
->fd
== ctx
->fdMax
) {
252 for (cur
= ctx
->files
; cur
; cur
= cur
->next
)
253 if (cur
->fd
> ctx
->fdMax
)
254 ctx
->fdMax
= cur
->fd
;
257 /* If this was the fdNext, cycle that to the next entry. */
258 if (del
== ctx
->fdNext
)
259 ctx
->fdNext
= del
->next
;
261 /* Couldn't free it before now since we were using fields out of it. */
268 FindFD(const evContext_p
*ctx
, int fd
, int eventmask
) {
271 for (id
= ctx
->fdTable
[fd
]; id
!= NULL
; id
= id
->fdnext
)
272 if (id
->fd
== fd
&& (id
->eventmask
& eventmask
) != 0)