4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (c) 1995-1999 by Internet Software Consortium
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 /* ev_files.c - implement asynch file IO for the eventlib
21 * vix 11sep95 [initial]
24 #if !defined(LINT) && !defined(CODECENTER)
25 static const char rcsid
[] = "Id: ev_files.c,v 1.8 2005/07/28 06:51:48 marka Exp";
28 #include "port_before.h"
29 #include "fd_setsize.h"
31 #include <sys/types.h>
33 #include <sys/ioctl.h>
39 #include <isc/eventlib.h>
40 #include "eventlib_p.h"
42 #include "port_after.h"
44 static evFile
*FindFD(const evContext_p
*ctx
, int fd
, int eventmask
);
47 evSelectFD(evContext opaqueCtx
,
54 evContext_p
*ctx
= opaqueCtx
.opaque
;
59 "evSelectFD(ctx %p, fd %d, mask 0x%x, func %p, uap %p)\n",
60 ctx
, fd
, eventmask
, func
, uap
);
61 if (eventmask
== 0 || (eventmask
& ~EV_MASK_ALL
) != 0)
64 if (fd
> ctx
->highestFD
)
67 OK(mode
= fcntl(fd
, F_GETFL
, NULL
)); /*%< side effect: validate fd. */
69 * The first time we touch a file descriptor, we need to check to see
70 * if the application already had it in O_NONBLOCK mode and if so, all
71 * of our deselect()'s have to leave it in O_NONBLOCK. If not, then
72 * all but our last deselect() has to leave it in O_NONBLOCK.
75 /* Make sure both ctx->pollfds[] and ctx->fdTable[] are large enough */
76 if (fd
>= ctx
->maxnfds
&& evPollfdRealloc(ctx
, 1, fd
) != 0)
79 id
= FindFD(ctx
, fd
, EV_MASK_ALL
);
81 if (mode
& PORT_NONBLOCK
)
82 FD_SET(fd
, &ctx
->nonblockBefore
);
84 #ifdef USE_FIONBIO_IOCTL
86 OK(ioctl(fd
, FIONBIO
, (char *)&on
));
88 OK(fcntl(fd
, F_SETFL
, mode
| PORT_NONBLOCK
));
90 FD_CLR(fd
, &ctx
->nonblockBefore
);
95 * If this descriptor is already in use, search for it again to see
96 * if any of the eventmask bits we want to set are already captured.
97 * We cannot usefully capture the same fd event more than once in the
100 if (id
!= NULL
&& FindFD(ctx
, fd
, eventmask
) != NULL
)
101 EV_ERR(ETOOMANYREFS
);
103 /* Allocate and fill. */
108 id
->eventmask
= eventmask
;
111 * Insert at head. Order could be important for performance if we
112 * believe that evGetNext()'s accesses to the fd_sets will be more
113 * serial and therefore more cache-lucky if the list is ordered by
114 * ``fd.'' We do not believe these things, so we don't do it.
116 * The interesting sequence is where GetNext() has cached a select()
117 * result and the caller decides to evSelectFD() on some descriptor.
118 * Since GetNext() starts at the head, it can miss new entries we add
119 * at the head. This is not a serious problem since the event being
120 * evSelectFD()'d for has to occur before evSelectFD() is called for
121 * the file event to be considered "missed" -- a real corner case.
122 * Maintaining a "tail" pointer for ctx->files would fix this, but I'm
123 * not sure it would be ``more correct.''
125 if (ctx
->files
!= NULL
)
126 ctx
->files
->prev
= id
;
128 id
->next
= ctx
->files
;
131 /* Insert into fd table. */
132 if (ctx
->fdTable
[fd
] != NULL
)
133 ctx
->fdTable
[fd
]->fdprev
= id
;
135 id
->fdnext
= ctx
->fdTable
[fd
];
136 ctx
->fdTable
[fd
] = id
;
138 /* Turn on the appropriate bits in the {rd,wr,ex}Next fd_set's. */
139 if (eventmask
& EV_READ
)
140 FD_SET(fd
, &ctx
->rdNext
);
141 if (eventmask
& EV_WRITE
)
142 FD_SET(fd
, &ctx
->wrNext
);
143 if (eventmask
& EV_EXCEPT
)
144 FD_SET(fd
, &ctx
->exNext
);
150 /* Remember the ID if the caller provided us a place for it. */
152 opaqueID
->opaque
= id
;
158 evDeselectFD(evContext opaqueCtx
, evFileID opaqueID
) {
159 evContext_p
*ctx
= opaqueCtx
.opaque
;
160 evFile
*del
= opaqueID
.opaque
;
165 evPrintf(ctx
, 11, "evDeselectFD(NULL) ignored\n");
170 evPrintf(ctx
, 1, "evDeselectFD(fd %d, mask 0x%x)\n",
171 del
->fd
, del
->eventmask
);
173 /* Get the mode. Unless the file has been closed, errors are bad. */
174 mode
= fcntl(del
->fd
, F_GETFL
, NULL
);
175 if (mode
== -1 && errno
!= EBADF
)
178 /* Remove from the list of files. */
179 if (del
->prev
!= NULL
)
180 del
->prev
->next
= del
->next
;
182 ctx
->files
= del
->next
;
183 if (del
->next
!= NULL
)
184 del
->next
->prev
= del
->prev
;
186 /* Remove from the fd table. */
187 if (del
->fdprev
!= NULL
)
188 del
->fdprev
->fdnext
= del
->fdnext
;
190 ctx
->fdTable
[del
->fd
] = del
->fdnext
;
191 if (del
->fdnext
!= NULL
)
192 del
->fdnext
->fdprev
= del
->fdprev
;
195 * If the file descriptor does not appear in any other select() entry,
196 * and if !EV_WASNONBLOCK, and if we got no EBADF when we got the mode
197 * earlier, then: restore the fd to blocking status.
199 if (!(cur
= FindFD(ctx
, del
->fd
, EV_MASK_ALL
)) &&
200 !FD_ISSET(del
->fd
, &ctx
->nonblockBefore
) &&
203 * Note that we won't return an error status to the caller if
204 * this fcntl() fails since (a) we've already done the work
205 * and (b) the caller didn't ask us anything about O_NONBLOCK.
207 #ifdef USE_FIONBIO_IOCTL
209 (void) ioctl(del
->fd
, FIONBIO
, (char *)&off
);
211 (void) fcntl(del
->fd
, F_SETFL
, mode
& ~PORT_NONBLOCK
);
216 * Now find all other uses of this descriptor and OR together an event
217 * mask so that we don't turn off {rd,wr,ex}Next bits that some other
218 * file event is using. As an optimization, stop if the event mask
223 cur
!= NULL
&& eventmask
!= EV_MASK_ALL
;
225 if (cur
->fd
== del
->fd
)
226 eventmask
|= cur
->eventmask
;
228 /* OK, now we know which bits we can clear out. */
229 if (!(eventmask
& EV_READ
)) {
230 FD_CLR(del
->fd
, &ctx
->rdNext
);
231 if (FD_ISSET(del
->fd
, &ctx
->rdLast
)) {
232 FD_CLR(del
->fd
, &ctx
->rdLast
);
236 if (!(eventmask
& EV_WRITE
)) {
237 FD_CLR(del
->fd
, &ctx
->wrNext
);
238 if (FD_ISSET(del
->fd
, &ctx
->wrLast
)) {
239 FD_CLR(del
->fd
, &ctx
->wrLast
);
243 if (!(eventmask
& EV_EXCEPT
)) {
244 FD_CLR(del
->fd
, &ctx
->exNext
);
245 if (FD_ISSET(del
->fd
, &ctx
->exLast
)) {
246 FD_CLR(del
->fd
, &ctx
->exLast
);
251 /* If this was the maxFD, find the new one. */
252 if (del
->fd
== ctx
->fdMax
) {
254 for (cur
= ctx
->files
; cur
; cur
= cur
->next
)
255 if (cur
->fd
> ctx
->fdMax
)
256 ctx
->fdMax
= cur
->fd
;
259 /* If this was the fdNext, cycle that to the next entry. */
260 if (del
== ctx
->fdNext
)
261 ctx
->fdNext
= del
->next
;
263 /* Couldn't free it before now since we were using fields out of it. */
270 FindFD(const evContext_p
*ctx
, int fd
, int eventmask
) {
273 for (id
= ctx
->fdTable
[fd
]; id
!= NULL
; id
= id
->fdnext
)
274 if (id
->fd
== fd
&& (id
->eventmask
& eventmask
) != 0)