]>
git.saurik.com Git - redis.git/blob - src/ae_evport.c
4a3334203aae6a69aa49da11020fe4503bd016e1
1 /* ae.c module for illumos event ports.
2 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
3 * Released under the BSD license. See the COPYING file for more info. */
10 #include <sys/types.h>
18 * This file implements the ae API using event ports, present on Solaris-based
19 * systems since Solaris 10. Using the event port interface, we associate file
20 * descriptors with the port. Each association also includes the set of poll(2)
21 * events that the consumer is interested in (e.g., POLLIN and POLLOUT).
23 * There's one tricky piece to this implementation: when we return events via
24 * aeApiPoll, the corresponding file descriptor becomes dissociated from the
25 * port. This is necessary because poll events are level-triggered, so if the
26 * fd didn't become dissociated, it would immediately fire another event since
27 * the underlying state hasn't changed yet. We must reassociate the file
28 * descriptor, but only after we know that our caller has actually read from it.
29 * The ae API does not tell us exactly when that happens, but we do know that
30 * it must happen by the time aeApiPoll is called again. Our solution is to
31 * keep track of the last fd returned by aeApiPoll and reassociate it next time
32 * aeApiPoll is invoked.
34 * To summarize, in this module, each fd association is EITHER (a) represented
35 * only via the in-kernel assocation OR (b) represented by pending_fd and
36 * pending_mask. (b) is only true for the last fd we returned from aeApiPoll,
37 * and only until we enter aeApiPoll again (at which point we restore the
38 * in-kernel association).
40 * We currently only return one fd event per call to aeApiPoll. This could be
41 * extended to return more than one by extending the corresponding pending
42 * fields and using port_getn().
44 typedef struct aeApiState
{
45 int portfd
; /* event port */
46 int pending_fd
; /* pending fd */
47 int pending_mask
; /* pending fd's mask */
50 static int aeApiCreate(aeEventLoop
*eventLoop
) {
51 aeApiState
*state
= zmalloc(sizeof(aeApiState
));
52 if (!state
) return -1;
54 state
->portfd
= port_create();
55 if (state
->portfd
== -1) {
60 state
->pending_fd
= -1;
61 state
->pending_mask
= AE_NONE
;
63 eventLoop
->apidata
= state
;
67 static void aeApiFree(aeEventLoop
*eventLoop
) {
68 aeApiState
*state
= eventLoop
->apidata
;
75 * Helper function to invoke port_associate for the given fd and mask.
77 static int aeApiAssociate(const char *where
, int portfd
, int fd
, int mask
)
82 if (mask
& AE_READABLE
)
84 if (mask
& AE_WRITABLE
)
88 fprintf(stderr
, "%s: port_associate(%d, 0x%x) = ", where
, fd
, events
);
90 rv
= port_associate(portfd
, PORT_SOURCE_FD
, fd
, events
, (void *)mask
);
94 fprintf(stderr
, "%d (%s)\n", rv
, rv
== 0 ? "no error" : strerror(err
));
97 fprintf(stderr
, "%s: port_associate: %s\n", where
, strerror(err
));
100 fprintf(stderr
, "aeApiAssociate: event port limit exceeded.");
106 static int aeApiAddEvent(aeEventLoop
*eventLoop
, int fd
, int mask
) {
107 aeApiState
*state
= eventLoop
->apidata
;
111 fprintf(stderr
, "aeApiAddEvent: fd %d mask 0x%x\n", fd
, mask
);
114 * Since port_associate's "events" argument replaces any existing events, we
115 * must be sure to include whatever events are already associated when
116 * we call port_associate() again.
118 fullmask
= mask
| eventLoop
->events
[fd
].mask
;
120 if (fd
== state
->pending_fd
) {
122 * This fd was recently returned from aeApiPoll. It should be safe to
123 * assume that the consumer has processed that poll event, but we play
124 * it safer by simply updating pending_mask. The fd will be
125 * reassociated as usual when aeApiPoll is called again.
128 fprintf(stderr
, "aeApiAddEvent: adding to pending fd %d\n", fd
);
129 state
->pending_mask
|= fullmask
;
133 return (aeApiAssociate("aeApiAddEvent", state
->portfd
, fd
, fullmask
));
136 static void aeApiDelEvent(aeEventLoop
*eventLoop
, int fd
, int mask
) {
137 aeApiState
*state
= eventLoop
->apidata
;
141 fprintf(stderr
, "del fd %d mask 0x%x\n", fd
, mask
);
143 if (fd
== state
->pending_fd
) {
145 fprintf(stderr
, "deleting event from pending fd %d\n", fd
);
148 * This fd was just returned from aeApiPoll, so it's not currently
149 * associated with the port. All we need to do is update
150 * pending_mask appropriately.
152 state
->pending_mask
&= ~mask
;
154 if (state
->pending_mask
== AE_NONE
)
155 state
->pending_fd
= -1;
161 * The fd is currently associated with the port. Like with the add case
162 * above, we must look at the full mask for the file descriptor before
163 * updating that association. We don't have a good way of knowing what the
164 * events are without looking into the eventLoop state directly. We rely on
165 * the fact that our caller has already updated the mask in the eventLoop.
168 fullmask
= eventLoop
->events
[fd
].mask
;
169 if (fullmask
== AE_NONE
) {
171 * We're removing *all* events, so use port_dissociate to remove the
172 * association completely. Failure here indicates a bug.
175 fprintf(stderr
, "aeApiDelEvent: port_dissociate(%d)\n", fd
);
177 if (port_dissociate(state
->portfd
, PORT_SOURCE_FD
, fd
) != 0) {
178 perror("aeApiDelEvent: port_dissociate");
179 abort(); /* will not return */
181 } else if (aeApiAssociate("aeApiDelEvent", state
->portfd
, fd
,
184 * ENOMEM is a potentially transient condition, but the kernel won't
185 * generally return it unless things are really bad. EAGAIN indicates
186 * we've reached an resource limit, for which it doesn't make sense to
187 * retry (counterintuitively). All other errors indicate a bug. In any
188 * of these cases, the best we can do is to abort.
190 abort(); /* will not return */
194 static int aeApiPoll(aeEventLoop
*eventLoop
, struct timeval
*tvp
) {
195 aeApiState
*state
= eventLoop
->apidata
;
196 struct timespec timeout
, *tsp
;
201 * If we've returned an fd event before, we must reassociated that fd with
202 * the port now, before calling port_get(). See the block comment at the
203 * top of this file for an explanation of why.
205 if (state
->pending_mask
!= AE_NONE
) {
206 if (aeApiAssociate("aeApiPoll", state
->portfd
, state
->pending_fd
,
207 state
->pending_mask
) != 0) {
208 /* See aeApiDelEvent for why this case is fatal. */
212 state
->pending_mask
= AE_NONE
;
213 state
->pending_fd
= -1;
217 timeout
.tv_sec
= tvp
->tv_sec
;
218 timeout
.tv_nsec
= tvp
->tv_usec
* 1000;
224 if (port_get(state
->portfd
, &event
, tsp
) == -1) {
225 if (errno
== ETIME
|| errno
== EINTR
)
228 /* Any other error indicates a bug. */
229 perror("aeApiPoll: port_get");
234 if (event
.portev_events
& POLLIN
)
236 if (event
.portev_events
& POLLOUT
)
239 eventLoop
->fired
[0].fd
= event
.portev_object
;
240 eventLoop
->fired
[0].mask
= mask
;
243 fprintf(stderr
, "aeApiPoll: fd %d mask 0x%x\n", event
.portev_object
,
246 state
->pending_fd
= event
.portev_object
;
247 state
->pending_mask
= (uintptr_t)event
.portev_user
;
252 static char *aeApiName(void) {