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.3.2.2 2004/03/09 09:17:35 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)
61 if (fd
> ctx
->highestFD
)
63 OK(mode
= fcntl(fd
, F_GETFL
, NULL
)); /* side effect: validate fd. */
66 * The first time we touch a file descriptor, we need to check to see
67 * if the application already had it in O_NONBLOCK mode and if so, all
68 * of our deselect()'s have to leave it in O_NONBLOCK. If not, then
69 * all but our last deselect() has to leave it in O_NONBLOCK.
71 id
= FindFD(ctx
, fd
, EV_MASK_ALL
);
73 if (mode
& PORT_NONBLOCK
)
74 FD_SET(fd
, &ctx
->nonblockBefore
);
76 #ifdef USE_FIONBIO_IOCTL
78 OK(ioctl(fd
, FIONBIO
, (char *)&on
));
80 OK(fcntl(fd
, F_SETFL
, mode
| PORT_NONBLOCK
));
82 FD_CLR(fd
, &ctx
->nonblockBefore
);
87 * If this descriptor is already in use, search for it again to see
88 * if any of the eventmask bits we want to set are already captured.
89 * We cannot usefully capture the same fd event more than once in the
92 if (id
!= NULL
&& FindFD(ctx
, fd
, eventmask
) != NULL
)
95 /* Allocate and fill. */
100 id
->eventmask
= eventmask
;
103 * Insert at head. Order could be important for performance if we
104 * believe that evGetNext()'s accesses to the fd_sets will be more
105 * serial and therefore more cache-lucky if the list is ordered by
106 * ``fd.'' We do not believe these things, so we don't do it.
108 * The interesting sequence is where GetNext() has cached a select()
109 * result and the caller decides to evSelectFD() on some descriptor.
110 * Since GetNext() starts at the head, it can miss new entries we add
111 * at the head. This is not a serious problem since the event being
112 * evSelectFD()'d for has to occur before evSelectFD() is called for
113 * the file event to be considered "missed" -- a real corner case.
114 * Maintaining a "tail" pointer for ctx->files would fix this, but I'm
115 * not sure it would be ``more correct.''
117 if (ctx
->files
!= NULL
)
118 ctx
->files
->prev
= id
;
120 id
->next
= ctx
->files
;
123 /* Insert into fd table. */
124 if (ctx
->fdTable
[fd
] != NULL
)
125 ctx
->fdTable
[fd
]->fdprev
= id
;
127 id
->fdnext
= ctx
->fdTable
[fd
];
128 ctx
->fdTable
[fd
] = id
;
130 /* Turn on the appropriate bits in the {rd,wr,ex}Next fd_set's. */
131 if (eventmask
& EV_READ
)
132 FD_SET(fd
, &ctx
->rdNext
);
133 if (eventmask
& EV_WRITE
)
134 FD_SET(fd
, &ctx
->wrNext
);
135 if (eventmask
& EV_EXCEPT
)
136 FD_SET(fd
, &ctx
->exNext
);
142 /* Remember the ID if the caller provided us a place for it. */
144 opaqueID
->opaque
= id
;
147 "evSelectFD(fd %d, mask 0x%x): new masks: 0x%lx 0x%lx 0x%lx\n",
149 (u_long
)ctx
->rdNext
.fds_bits
[0],
150 (u_long
)ctx
->wrNext
.fds_bits
[0],
151 (u_long
)ctx
->exNext
.fds_bits
[0]);
157 evDeselectFD(evContext opaqueCtx
, evFileID opaqueID
) {
158 evContext_p
*ctx
= opaqueCtx
.opaque
;
159 evFile
*del
= opaqueID
.opaque
;
164 evPrintf(ctx
, 11, "evDeselectFD(NULL) ignored\n");
169 evPrintf(ctx
, 1, "evDeselectFD(fd %d, mask 0x%x)\n",
170 del
->fd
, del
->eventmask
);
172 /* Get the mode. Unless the file has been closed, errors are bad. */
173 mode
= fcntl(del
->fd
, F_GETFL
, NULL
);
174 if (mode
== -1 && errno
!= EBADF
)
177 /* Remove from the list of files. */
178 if (del
->prev
!= NULL
)
179 del
->prev
->next
= del
->next
;
181 ctx
->files
= del
->next
;
182 if (del
->next
!= NULL
)
183 del
->next
->prev
= del
->prev
;
185 /* Remove from the fd table. */
186 if (del
->fdprev
!= NULL
)
187 del
->fdprev
->fdnext
= del
->fdnext
;
189 ctx
->fdTable
[del
->fd
] = del
->fdnext
;
190 if (del
->fdnext
!= NULL
)
191 del
->fdnext
->fdprev
= del
->fdprev
;
194 * If the file descriptor does not appear in any other select() entry,
195 * and if !EV_WASNONBLOCK, and if we got no EBADF when we got the mode
196 * earlier, then: restore the fd to blocking status.
198 if (!(cur
= FindFD(ctx
, del
->fd
, EV_MASK_ALL
)) &&
199 !FD_ISSET(del
->fd
, &ctx
->nonblockBefore
) &&
202 * Note that we won't return an error status to the caller if
203 * this fcntl() fails since (a) we've already done the work
204 * and (b) the caller didn't ask us anything about O_NONBLOCK.
206 #ifdef USE_FIONBIO_IOCTL
208 (void) ioctl(del
->fd
, FIONBIO
, (char *)&off
);
210 (void) fcntl(del
->fd
, F_SETFL
, mode
& ~PORT_NONBLOCK
);
215 * Now find all other uses of this descriptor and OR together an event
216 * mask so that we don't turn off {rd,wr,ex}Next bits that some other
217 * file event is using. As an optimization, stop if the event mask
222 cur
!= NULL
&& eventmask
!= EV_MASK_ALL
;
224 if (cur
->fd
== del
->fd
)
225 eventmask
|= cur
->eventmask
;
227 /* OK, now we know which bits we can clear out. */
228 if (!(eventmask
& EV_READ
)) {
229 FD_CLR(del
->fd
, &ctx
->rdNext
);
230 if (FD_ISSET(del
->fd
, &ctx
->rdLast
)) {
231 FD_CLR(del
->fd
, &ctx
->rdLast
);
235 if (!(eventmask
& EV_WRITE
)) {
236 FD_CLR(del
->fd
, &ctx
->wrNext
);
237 if (FD_ISSET(del
->fd
, &ctx
->wrLast
)) {
238 FD_CLR(del
->fd
, &ctx
->wrLast
);
242 if (!(eventmask
& EV_EXCEPT
)) {
243 FD_CLR(del
->fd
, &ctx
->exNext
);
244 if (FD_ISSET(del
->fd
, &ctx
->exLast
)) {
245 FD_CLR(del
->fd
, &ctx
->exLast
);
250 /* If this was the maxFD, find the new one. */
251 if (del
->fd
== ctx
->fdMax
) {
253 for (cur
= ctx
->files
; cur
; cur
= cur
->next
)
254 if (cur
->fd
> ctx
->fdMax
)
255 ctx
->fdMax
= cur
->fd
;
258 /* If this was the fdNext, cycle that to the next entry. */
259 if (del
== ctx
->fdNext
)
260 ctx
->fdNext
= del
->next
;
263 "evDeselectFD(fd %d, mask 0x%x): new masks: 0x%lx 0x%lx 0x%lx\n",
265 (u_long
)ctx
->rdNext
.fds_bits
[0],
266 (u_long
)ctx
->wrNext
.fds_bits
[0],
267 (u_long
)ctx
->exNext
.fds_bits
[0]);
269 /* Couldn't free it before now since we were using fields out of it. */
276 FindFD(const evContext_p
*ctx
, int fd
, int eventmask
) {
279 for (id
= ctx
->fdTable
[fd
]; id
!= NULL
; id
= id
->fdnext
)
280 if (id
->fd
== fd
&& (id
->eventmask
& eventmask
) != 0)