Mercurial
comparison third_party/wrk/src/ae_evport.c @ 178:94705b5986b3
[ThirdParty] Added WRK and luajit for load testing.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Thu, 22 Jan 2026 20:10:30 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 177:24fe8ff94056 | 178:94705b5986b3 |
|---|---|
| 1 /* ae.c module for illumos event ports. | |
| 2 * | |
| 3 * Copyright (c) 2012, Joyent, Inc. All rights reserved. | |
| 4 * | |
| 5 * Redistribution and use in source and binary forms, with or without | |
| 6 * modification, are permitted provided that the following conditions are met: | |
| 7 * | |
| 8 * * Redistributions of source code must retain the above copyright notice, | |
| 9 * this list of conditions and the following disclaimer. | |
| 10 * * Redistributions in binary form must reproduce the above copyright | |
| 11 * notice, this list of conditions and the following disclaimer in the | |
| 12 * documentation and/or other materials provided with the distribution. | |
| 13 * * Neither the name of Redis nor the names of its contributors may be used | |
| 14 * to endorse or promote products derived from this software without | |
| 15 * specific prior written permission. | |
| 16 * | |
| 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
| 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| 27 * POSSIBILITY OF SUCH DAMAGE. | |
| 28 */ | |
| 29 | |
| 30 | |
| 31 #include <assert.h> | |
| 32 #include <errno.h> | |
| 33 #include <port.h> | |
| 34 #include <poll.h> | |
| 35 | |
| 36 #include <sys/types.h> | |
| 37 #include <sys/time.h> | |
| 38 | |
| 39 #include <stdio.h> | |
| 40 | |
| 41 static int evport_debug = 0; | |
| 42 | |
| 43 /* | |
| 44 * This file implements the ae API using event ports, present on Solaris-based | |
| 45 * systems since Solaris 10. Using the event port interface, we associate file | |
| 46 * descriptors with the port. Each association also includes the set of poll(2) | |
| 47 * events that the consumer is interested in (e.g., POLLIN and POLLOUT). | |
| 48 * | |
| 49 * There's one tricky piece to this implementation: when we return events via | |
| 50 * aeApiPoll, the corresponding file descriptors become dissociated from the | |
| 51 * port. This is necessary because poll events are level-triggered, so if the | |
| 52 * fd didn't become dissociated, it would immediately fire another event since | |
| 53 * the underlying state hasn't changed yet. We must re-associate the file | |
| 54 * descriptor, but only after we know that our caller has actually read from it. | |
| 55 * The ae API does not tell us exactly when that happens, but we do know that | |
| 56 * it must happen by the time aeApiPoll is called again. Our solution is to | |
| 57 * keep track of the last fds returned by aeApiPoll and re-associate them next | |
| 58 * time aeApiPoll is invoked. | |
| 59 * | |
| 60 * To summarize, in this module, each fd association is EITHER (a) represented | |
| 61 * only via the in-kernel association OR (b) represented by pending_fds and | |
| 62 * pending_masks. (b) is only true for the last fds we returned from aeApiPoll, | |
| 63 * and only until we enter aeApiPoll again (at which point we restore the | |
| 64 * in-kernel association). | |
| 65 */ | |
| 66 #define MAX_EVENT_BATCHSZ 512 | |
| 67 | |
| 68 typedef struct aeApiState { | |
| 69 int portfd; /* event port */ | |
| 70 int npending; /* # of pending fds */ | |
| 71 int pending_fds[MAX_EVENT_BATCHSZ]; /* pending fds */ | |
| 72 int pending_masks[MAX_EVENT_BATCHSZ]; /* pending fds' masks */ | |
| 73 } aeApiState; | |
| 74 | |
| 75 static int aeApiCreate(aeEventLoop *eventLoop) { | |
| 76 int i; | |
| 77 aeApiState *state = zmalloc(sizeof(aeApiState)); | |
| 78 if (!state) return -1; | |
| 79 | |
| 80 state->portfd = port_create(); | |
| 81 if (state->portfd == -1) { | |
| 82 zfree(state); | |
| 83 return -1; | |
| 84 } | |
| 85 | |
| 86 state->npending = 0; | |
| 87 | |
| 88 for (i = 0; i < MAX_EVENT_BATCHSZ; i++) { | |
| 89 state->pending_fds[i] = -1; | |
| 90 state->pending_masks[i] = AE_NONE; | |
| 91 } | |
| 92 | |
| 93 eventLoop->apidata = state; | |
| 94 return 0; | |
| 95 } | |
| 96 | |
| 97 static int aeApiResize(aeEventLoop *eventLoop, int setsize) { | |
| 98 /* Nothing to resize here. */ | |
| 99 return 0; | |
| 100 } | |
| 101 | |
| 102 static void aeApiFree(aeEventLoop *eventLoop) { | |
| 103 aeApiState *state = eventLoop->apidata; | |
| 104 | |
| 105 close(state->portfd); | |
| 106 zfree(state); | |
| 107 } | |
| 108 | |
| 109 static int aeApiLookupPending(aeApiState *state, int fd) { | |
| 110 int i; | |
| 111 | |
| 112 for (i = 0; i < state->npending; i++) { | |
| 113 if (state->pending_fds[i] == fd) | |
| 114 return (i); | |
| 115 } | |
| 116 | |
| 117 return (-1); | |
| 118 } | |
| 119 | |
| 120 /* | |
| 121 * Helper function to invoke port_associate for the given fd and mask. | |
| 122 */ | |
| 123 static int aeApiAssociate(const char *where, int portfd, int fd, int mask) { | |
| 124 int events = 0; | |
| 125 int rv, err; | |
| 126 | |
| 127 if (mask & AE_READABLE) | |
| 128 events |= POLLIN; | |
| 129 if (mask & AE_WRITABLE) | |
| 130 events |= POLLOUT; | |
| 131 | |
| 132 if (evport_debug) | |
| 133 fprintf(stderr, "%s: port_associate(%d, 0x%x) = ", where, fd, events); | |
| 134 | |
| 135 rv = port_associate(portfd, PORT_SOURCE_FD, fd, events, | |
| 136 (void *)(uintptr_t)mask); | |
| 137 err = errno; | |
| 138 | |
| 139 if (evport_debug) | |
| 140 fprintf(stderr, "%d (%s)\n", rv, rv == 0 ? "no error" : strerror(err)); | |
| 141 | |
| 142 if (rv == -1) { | |
| 143 fprintf(stderr, "%s: port_associate: %s\n", where, strerror(err)); | |
| 144 | |
| 145 if (err == EAGAIN) | |
| 146 fprintf(stderr, "aeApiAssociate: event port limit exceeded."); | |
| 147 } | |
| 148 | |
| 149 return rv; | |
| 150 } | |
| 151 | |
| 152 static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { | |
| 153 aeApiState *state = eventLoop->apidata; | |
| 154 int fullmask, pfd; | |
| 155 | |
| 156 if (evport_debug) | |
| 157 fprintf(stderr, "aeApiAddEvent: fd %d mask 0x%x\n", fd, mask); | |
| 158 | |
| 159 /* | |
| 160 * Since port_associate's "events" argument replaces any existing events, we | |
| 161 * must be sure to include whatever events are already associated when | |
| 162 * we call port_associate() again. | |
| 163 */ | |
| 164 fullmask = mask | eventLoop->events[fd].mask; | |
| 165 pfd = aeApiLookupPending(state, fd); | |
| 166 | |
| 167 if (pfd != -1) { | |
| 168 /* | |
| 169 * This fd was recently returned from aeApiPoll. It should be safe to | |
| 170 * assume that the consumer has processed that poll event, but we play | |
| 171 * it safer by simply updating pending_mask. The fd will be | |
| 172 * re-associated as usual when aeApiPoll is called again. | |
| 173 */ | |
| 174 if (evport_debug) | |
| 175 fprintf(stderr, "aeApiAddEvent: adding to pending fd %d\n", fd); | |
| 176 state->pending_masks[pfd] |= fullmask; | |
| 177 return 0; | |
| 178 } | |
| 179 | |
| 180 return (aeApiAssociate("aeApiAddEvent", state->portfd, fd, fullmask)); | |
| 181 } | |
| 182 | |
| 183 static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { | |
| 184 aeApiState *state = eventLoop->apidata; | |
| 185 int fullmask, pfd; | |
| 186 | |
| 187 if (evport_debug) | |
| 188 fprintf(stderr, "del fd %d mask 0x%x\n", fd, mask); | |
| 189 | |
| 190 pfd = aeApiLookupPending(state, fd); | |
| 191 | |
| 192 if (pfd != -1) { | |
| 193 if (evport_debug) | |
| 194 fprintf(stderr, "deleting event from pending fd %d\n", fd); | |
| 195 | |
| 196 /* | |
| 197 * This fd was just returned from aeApiPoll, so it's not currently | |
| 198 * associated with the port. All we need to do is update | |
| 199 * pending_mask appropriately. | |
| 200 */ | |
| 201 state->pending_masks[pfd] &= ~mask; | |
| 202 | |
| 203 if (state->pending_masks[pfd] == AE_NONE) | |
| 204 state->pending_fds[pfd] = -1; | |
| 205 | |
| 206 return; | |
| 207 } | |
| 208 | |
| 209 /* | |
| 210 * The fd is currently associated with the port. Like with the add case | |
| 211 * above, we must look at the full mask for the file descriptor before | |
| 212 * updating that association. We don't have a good way of knowing what the | |
| 213 * events are without looking into the eventLoop state directly. We rely on | |
| 214 * the fact that our caller has already updated the mask in the eventLoop. | |
| 215 */ | |
| 216 | |
| 217 fullmask = eventLoop->events[fd].mask; | |
| 218 if (fullmask == AE_NONE) { | |
| 219 /* | |
| 220 * We're removing *all* events, so use port_dissociate to remove the | |
| 221 * association completely. Failure here indicates a bug. | |
| 222 */ | |
| 223 if (evport_debug) | |
| 224 fprintf(stderr, "aeApiDelEvent: port_dissociate(%d)\n", fd); | |
| 225 | |
| 226 if (port_dissociate(state->portfd, PORT_SOURCE_FD, fd) != 0) { | |
| 227 perror("aeApiDelEvent: port_dissociate"); | |
| 228 abort(); /* will not return */ | |
| 229 } | |
| 230 } else if (aeApiAssociate("aeApiDelEvent", state->portfd, fd, | |
| 231 fullmask) != 0) { | |
| 232 /* | |
| 233 * ENOMEM is a potentially transient condition, but the kernel won't | |
| 234 * generally return it unless things are really bad. EAGAIN indicates | |
| 235 * we've reached an resource limit, for which it doesn't make sense to | |
| 236 * retry (counter-intuitively). All other errors indicate a bug. In any | |
| 237 * of these cases, the best we can do is to abort. | |
| 238 */ | |
| 239 abort(); /* will not return */ | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { | |
| 244 aeApiState *state = eventLoop->apidata; | |
| 245 struct timespec timeout, *tsp; | |
| 246 int mask, i; | |
| 247 uint_t nevents; | |
| 248 port_event_t event[MAX_EVENT_BATCHSZ]; | |
| 249 | |
| 250 /* | |
| 251 * If we've returned fd events before, we must re-associate them with the | |
| 252 * port now, before calling port_get(). See the block comment at the top of | |
| 253 * this file for an explanation of why. | |
| 254 */ | |
| 255 for (i = 0; i < state->npending; i++) { | |
| 256 if (state->pending_fds[i] == -1) | |
| 257 /* This fd has since been deleted. */ | |
| 258 continue; | |
| 259 | |
| 260 if (aeApiAssociate("aeApiPoll", state->portfd, | |
| 261 state->pending_fds[i], state->pending_masks[i]) != 0) { | |
| 262 /* See aeApiDelEvent for why this case is fatal. */ | |
| 263 abort(); | |
| 264 } | |
| 265 | |
| 266 state->pending_masks[i] = AE_NONE; | |
| 267 state->pending_fds[i] = -1; | |
| 268 } | |
| 269 | |
| 270 state->npending = 0; | |
| 271 | |
| 272 if (tvp != NULL) { | |
| 273 timeout.tv_sec = tvp->tv_sec; | |
| 274 timeout.tv_nsec = tvp->tv_usec * 1000; | |
| 275 tsp = &timeout; | |
| 276 } else { | |
| 277 tsp = NULL; | |
| 278 } | |
| 279 | |
| 280 /* | |
| 281 * port_getn can return with errno == ETIME having returned some events (!). | |
| 282 * So if we get ETIME, we check nevents, too. | |
| 283 */ | |
| 284 nevents = 1; | |
| 285 if (port_getn(state->portfd, event, MAX_EVENT_BATCHSZ, &nevents, | |
| 286 tsp) == -1 && (errno != ETIME || nevents == 0)) { | |
| 287 if (errno == ETIME || errno == EINTR) | |
| 288 return 0; | |
| 289 | |
| 290 /* Any other error indicates a bug. */ | |
| 291 perror("aeApiPoll: port_get"); | |
| 292 abort(); | |
| 293 } | |
| 294 | |
| 295 state->npending = nevents; | |
| 296 | |
| 297 for (i = 0; i < nevents; i++) { | |
| 298 mask = 0; | |
| 299 if (event[i].portev_events & POLLIN) | |
| 300 mask |= AE_READABLE; | |
| 301 if (event[i].portev_events & POLLOUT) | |
| 302 mask |= AE_WRITABLE; | |
| 303 | |
| 304 eventLoop->fired[i].fd = event[i].portev_object; | |
| 305 eventLoop->fired[i].mask = mask; | |
| 306 | |
| 307 if (evport_debug) | |
| 308 fprintf(stderr, "aeApiPoll: fd %d mask 0x%x\n", | |
| 309 (int)event[i].portev_object, mask); | |
| 310 | |
| 311 state->pending_fds[i] = event[i].portev_object; | |
| 312 state->pending_masks[i] = (uintptr_t)event[i].portev_user; | |
| 313 } | |
| 314 | |
| 315 return nevents; | |
| 316 } | |
| 317 | |
| 318 static char *aeApiName(void) { | |
| 319 return "evport"; | |
| 320 } |