Fix SourceForge bug #2789732 by discarding pending events for deleted sockets.
[ircu2.10.12-pk.git] / ircd / engine_kqueue.c
index 577cac01d57cd4785dbbe70a26f00b61fda9daa8..b3f535f7d8db52825068a42f959ac06d62cadd9b 100644 (file)
@@ -49,6 +49,12 @@ static struct Socket** sockList;
 static int kqueue_max;
 /** File descriptor for kqueue pseudo-file. */
 static int kqueue_id;
+/** Current array of event descriptors. */
+static struct kevent *events;
+/** Number of ::events elements that have been populated. */
+static int events_used;
+/** Current processing position in ::events. */
+static int events_i;
 
 /** Number of recent errors from kqueue. */
 static int errors = 0;
@@ -101,7 +107,7 @@ engine_signal(struct Signal* sig)
   struct kevent sigevent;
   struct sigaction act;
 
-  assert(0 != signal);
+  assert(0 != sig);
 
   Debug((DEBUG_ENGINE, "kqueue: Adding filter for signal %d [%p]",
         sig_signal(sig), sig));
@@ -276,7 +282,7 @@ engine_events(struct Socket* sock, unsigned int new_events)
 static void
 engine_delete(struct Socket* sock)
 {
-  struct kevent dellist[2];
+  int ii;
 
   assert(0 != sock);
   assert(sock == sockList[s_fd(sock)]);
@@ -284,21 +290,14 @@ engine_delete(struct Socket* sock)
   Debug((DEBUG_ENGINE, "kqueue: Deleting socket %d [%p], state %s",
         s_fd(sock), sock, state_to_name(s_state(sock))));
 
-  dellist[0].ident = s_fd(sock); /* set up the delete list */
-  dellist[0].filter = EVFILT_READ; /* readable filter */
-  dellist[0].flags = EV_DELETE; /* delete it */
-  dellist[0].fflags = 0;
-  dellist[0].data = 0;
-  dellist[0].udata = 0;
-
-  dellist[1].ident = s_fd(sock);
-  dellist[1].filter = EVFILT_WRITE; /* writable filter */
-  dellist[1].flags = EV_DELETE; /* delete it */
-  dellist[1].fflags = 0;
-  dellist[1].data = 0;
-  dellist[1].udata = 0;
-
   sockList[s_fd(sock)] = 0;
+
+  /* Drop any unprocessed events citing this socket. */
+  for (ii = events_i; ii < events_used; ii++) {
+    if (events[ii].ident == s_fd(sock)) {
+      events[ii] = events[--events_used];
+    }
+  }
 }
 
 /** Run engine event loop.
@@ -307,14 +306,13 @@ engine_delete(struct Socket* sock)
 static void
 engine_loop(struct Generators* gen)
 {
-  struct kevent *events;
   int events_count;
+  struct kevent *evt;
   struct Socket* sock;
   struct timespec wait;
-  int nevs;
   int i;
   int errcode;
-  size_t codesize;
+  socklen_t codesize;
 
   if ((events_count = feature_int(FEAT_POLLS_PER_LOOP)) < 20)
     events_count = 20;
@@ -330,16 +328,16 @@ engine_loop(struct Generators* gen)
     wait.tv_sec = timer_next(gen) ? (timer_next(gen) - CurrentTime) : -1;
     wait.tv_nsec = 0;
 
-    Debug((DEBUG_INFO, "kqueue: delay: %Tu (%Tu) %Tu", timer_next(gen),
+    Debug((DEBUG_ENGINE, "kqueue: delay: %Tu (%Tu) %Tu", timer_next(gen),
           CurrentTime, wait.tv_sec));
 
     /* check for active events */
-    nevs = kevent(kqueue_id, 0, 0, events, events_count,
-                 wait.tv_sec < 0 ? 0 : &wait);
+    events_used = kevent(kqueue_id, 0, 0, events, events_count,
+                         wait.tv_sec < 0 ? 0 : &wait);
 
     CurrentTime = time(0); /* set current time... */
 
-    if (nevs < 0) {
+    if (events_used < 0) {
       if (errno != EINTR) { /* ignore kevent interrupts */
        /* Log the kqueue error */
        log_write(LS_SOCKET, L_ERROR, 0, "kevent() error: %m");
@@ -355,21 +353,22 @@ engine_loop(struct Generators* gen)
       continue;
     }
 
-    for (i = 0; i < nevs; i++) {
-      if (events[i].filter == EVFILT_SIGNAL) {
+    for (events_i = 0; events_i < events_used; events_i++) {
+      evt = &events[events_i];
+
+      if (evt->filter == EVFILT_SIGNAL) {
        /* it's a signal; deal appropriately */
-       event_generate(ET_SIGNAL, events[i].udata, events[i].ident);
+       event_generate(ET_SIGNAL, evt->udata, evt->ident);
        continue; /* skip socket processing loop */
       }
 
-      assert(events[i].filter == EVFILT_READ ||
-            events[i].filter == EVFILT_WRITE);
+      assert(evt->filter == EVFILT_READ || evt->filter == EVFILT_WRITE);
 
-      sock = sockList[events[i].ident];
+      sock = sockList[evt->ident];
       if (!sock) /* slots may become empty while processing events */
        continue;
 
-      assert(s_fd(sock) == events[i].ident);
+      assert(s_fd(sock) == evt->ident);
 
       gen_ref_inc(sock); /* can't have it going away on us */
 
@@ -395,14 +394,14 @@ engine_loop(struct Generators* gen)
 
       switch (s_state(sock)) {
       case SS_CONNECTING:
-       if (events[i].filter == EVFILT_WRITE) { /* connection completed */
+       if (evt->filter == EVFILT_WRITE) { /* connection completed */
          Debug((DEBUG_ENGINE, "kqueue: Connection completed"));
          event_generate(ET_CONNECT, sock, 0);
        }
        break;
 
       case SS_LISTENING:
-       if (events[i].filter == EVFILT_READ) { /* connect. to be accept. */
+       if (evt->filter == EVFILT_READ) { /* connect. to be accept. */
          Debug((DEBUG_ENGINE, "kqueue: Ready for accept"));
          event_generate(ET_ACCEPT, sock, 0);
        }
@@ -410,22 +409,22 @@ engine_loop(struct Generators* gen)
 
       case SS_NOTSOCK: /* doing nothing socket-specific */
       case SS_CONNECTED:
-       if (events[i].filter == EVFILT_READ) { /* data on socket */
+       if (evt->filter == EVFILT_READ) { /* data on socket */
          Debug((DEBUG_ENGINE, "kqueue: EOF or data to be read"));
-         event_generate(events[i].flags & EV_EOF ? ET_EOF : ET_READ, sock, 0);
+         event_generate(evt->flags & EV_EOF ? ET_EOF : ET_READ, sock, 0);
        }
-       if (events[i].filter == EVFILT_WRITE) { /* socket writable */
+       if (evt->filter == EVFILT_WRITE) { /* socket writable */
          Debug((DEBUG_ENGINE, "kqueue: Data can be written"));
          event_generate(ET_WRITE, sock, 0);
        }
        break;
 
       case SS_DATAGRAM: case SS_CONNECTDG:
-       if (events[i].filter == EVFILT_READ) { /* socket readable */
+       if (evt->filter == EVFILT_READ) { /* socket readable */
          Debug((DEBUG_ENGINE, "kqueue: Datagram to be read"));
          event_generate(ET_READ, sock, 0);
        }
-       if (events[i].filter == EVFILT_WRITE) { /* socket writable */
+       if (evt->filter == EVFILT_WRITE) { /* socket writable */
          Debug((DEBUG_ENGINE, "kqueue: Datagram can be written"));
          event_generate(ET_WRITE, sock, 0);
        }