"Fossies" - the Fresh Open Source Software Archive

Member "wrk-4.2.0/src/ae_evport.c" (7 Feb 2021, 10939 Bytes) of package /linux/www/wrk-4.2.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ae_evport.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 4.0.2_vs_4.1.0.

    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 }