nginx-0.0.1-2003-03-20-19:09:44 import
diff --git a/src/event/modules/ngx_devpoll_module.c b/src/event/modules/ngx_devpoll_module.c
index e2e7750..fbbcf59 100644
--- a/src/event/modules/ngx_devpoll_module.c
+++ b/src/event/modules/ngx_devpoll_module.c
@@ -236,6 +236,7 @@
     if ((int) timer != INFTIM) {
         gettimeofday(&tv, NULL);
         delta = tv.tv_sec * 1000 + tv.tv_usec / 1000 - delta;
+        ngx_event_expire_timers(delta);
 
     } else {
         if (events == 0) {
@@ -305,9 +306,5 @@
         }
     }
 
-    if ((int) timer != INFTIM) {
-        ngx_event_expire_timers(delta);
-    }
-
     return NGX_OK;
 }
diff --git a/src/event/modules/ngx_iocp_module.c b/src/event/modules/ngx_iocp_module.c
index f474c4c..a84daa8 100644
--- a/src/event/modules/ngx_iocp_module.c
+++ b/src/event/modules/ngx_iocp_module.c
@@ -110,6 +110,7 @@
 
     if (timer != INFINITE) {
         delta = ngx_msec() - delta;
+        ngx_event_expire_timers(delta);
     }
 
     if (ovlp) {
@@ -118,6 +119,7 @@
 ngx_log_debug(log, "iocp ev: %08x" _ ev);
 
         if (ev == e) {
+            /* it's not AcceptEx() completion */
             ev->ready = 1;
             ev->available = bytes;
         }
@@ -129,9 +131,5 @@
         }
     }
 
-    if (timer != INFINITE) {
-        ngx_event_expire_timers(delta);
-    }
-
     return NGX_OK;
 }
diff --git a/src/event/modules/ngx_kqueue_module.c b/src/event/modules/ngx_kqueue_module.c
index b49eb62..bc2fe21 100644
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -110,6 +110,13 @@
     ev->active = 1;
     ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1: 0;
 
+    /* The event addition or change should be always passed to a kernel
+       because there can be case when event was passed to a kernel then
+       added again to the change_list and then deleted from the change_list
+       by ngx_kqueue_del_event() so the first event still remains in a kernel */
+
+#if 0
+
     if (nchanges > 0
         && ev->index < nchanges
         && change_list[ev->index].udata == ev)
@@ -118,12 +125,17 @@
         ngx_connection_t *c = (ngx_connection_t *) ev->data;
         ngx_log_debug(ev->log, "kqueue add event: %d: ft:%d" _ c->fd _ event);
 #endif
+
+        /* if the event is still not passed to a kernel we change it */
+
         change_list[ev->index].filter = event;
         change_list[ev->index].flags = flags;
 
         return NGX_OK;
     }
 
+#endif
+
     return ngx_kqueue_set_event(ev, event, EV_ADD | flags);
 }
 
@@ -142,6 +154,9 @@
         ngx_connection_t *c = (ngx_connection_t *) ev->data;
         ngx_log_debug(ev->log, "kqueue del event: %d: ft:%d" _ c->fd _ event);
 #endif
+
+        /* if the event is still not passed to a kernel we will not pass it */
+
         if (ev->index < --nchanges) {
             e = (ngx_event_t *) change_list[nchanges].udata;
             change_list[ev->index] = change_list[nchanges];
@@ -151,6 +166,9 @@
         return NGX_OK;
     }
 
+    /* when a socket is closed kqueue automatically deletes its filters 
+       so we do not need to delete a event explicity before a socket closing */
+
     if (flags & NGX_CLOSE_EVENT) {
         return NGX_OK;
     }
@@ -257,6 +275,11 @@
         gettimeofday(&tv, NULL);
         delta = tv.tv_sec * 1000 + tv.tv_usec / 1000 - delta;
 
+        /* Expired timers must be deleted before the events processing
+           because the new timers can be added during the processing */
+
+        ngx_event_expire_timers(delta);
+
     } else {
         if (events == 0) {
             ngx_log_error(NGX_LOG_ALERT, log, 0,
@@ -295,6 +318,9 @@
 
         ev = (ngx_event_t *) event_list[i].udata;
 
+        /* It's a stale event from a socket
+           that was just closed in this iteration */
+
         if (!ev->active) {
            continue;
         }
@@ -303,6 +329,29 @@
 
         case EVFILT_READ:
         case EVFILT_WRITE:
+
+            if (ev->first) {
+                if (nchanges > 0
+                    && ev->index < nchanges
+                    && change_list[ev->index].udata == ev) {
+
+                    /* It's a stale event from a socket that was just closed
+                       in this iteration and during processing another socket
+                       was opened with the same number by accept() or socket()
+                       and its event has been added the event to the change_list
+                       but has not been passed to a kernel.  Nevertheless
+                       there's small chance that ngx_kqueue_set_event() has
+                       flushed the new event if the change_list was filled up.
+                       In this very rare case we would get EAGAIN while
+                       a reading or a writing */
+
+                    continue;
+
+                } else {
+                    ev->first = 0;
+                }
+            }
+ 
             ev->available = event_list[i].data;
 
             if (event_list[i].flags & EV_EOF) {
@@ -332,9 +381,5 @@
         }
     }
 
-    if (timer) {
-        ngx_event_expire_timers(delta);
-    }
-
     return NGX_OK;
 }
diff --git a/src/event/modules/ngx_poll_module.c b/src/event/modules/ngx_poll_module.c
index 25319c8..23bcc3f 100644
--- a/src/event/modules/ngx_poll_module.c
+++ b/src/event/modules/ngx_poll_module.c
@@ -175,6 +175,7 @@
 
     if ((int) timer != INFTIM) {
         delta = ngx_msec() - delta;
+        ngx_event_expire_timers(delta);
 
     } else {
         if (ready == 0) {
@@ -259,9 +260,5 @@
         ngx_log_error(NGX_LOG_ALERT, log, 0, "poll ready != events");
     }
 
-    if ((int) timer != INFTIM) {
-        ngx_event_expire_timers(delta);
-    }
-
     return NGX_OK;
 }
diff --git a/src/event/modules/ngx_select_module.c b/src/event/modules/ngx_select_module.c
index 50d030c..e7abfaa 100644
--- a/src/event/modules/ngx_select_module.c
+++ b/src/event/modules/ngx_select_module.c
@@ -243,6 +243,7 @@
 
     if (timer) {
         delta = ngx_msec() - delta;
+        ngx_event_expire_timers(delta);
 
     } else {
         if (ready == 0) {
@@ -312,9 +313,5 @@
         ngx_log_error(NGX_LOG_ALERT, log, 0, "select ready != events");
     }
 
-    if (timer) {
-        ngx_event_expire_timers(delta);
-    }
-
     return NGX_OK;
 }
diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h
index c36306d..4c553c0 100644
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -63,6 +63,7 @@
 #endif
     unsigned         write:1;
 
+    unsigned         first:1;
     unsigned         active:1;
     unsigned         ready:1;
     unsigned         timedout:1;
@@ -179,8 +180,8 @@
 #define NGX_USE_LEVEL_EVENT     0x00010000
 
 
-/* Event filter is deleted before closing file. Has no meaning
-   for select, poll, epoll.
+/* Event filter is deleted before closing file.
+   Has no meaning for select, poll, epoll.
 
    kqueue:     kqueue deletes event filters for file that closed
                so we need only to delete filters in user-level batch array
@@ -193,16 +194,24 @@
 #define NGX_READ_EVENT     EVFILT_READ
 #define NGX_WRITE_EVENT    EVFILT_WRITE
 
+#define NGX_ENABLE_EVENT   EV_ENABLE
+#define NGX_DISABLE_EVENT  EV_DISABLE
+
+/* NGX_CLOSE_EVENT is the module flag and it would not go into a kernel
+   so we need to choose the value that would not interfere with any existent
+   and future flags. kqueue has such values - EV_FLAG1, EV_EOF and EV_ERROR.
+   They are reserved and cleared on a kernel entrance */
+#undef  NGX_CLOSE_EVENT
+#define NGX_CLOSE_EVENT    EV_FLAG1
+
 #define NGX_LEVEL_EVENT    0
 #define NGX_ONESHOT_EVENT  EV_ONESHOT
+#define NGX_CLEAR_EVENT    EV_CLEAR
 
 #ifndef HAVE_CLEAR_EVENT
 #define HAVE_CLEAR_EVENT   1
 #endif
 
-#if (HAVE_CLEAR_EVENT)
-#define NGX_CLEAR_EVENT    EV_CLEAR
-#endif
 
 #elif (HAVE_POLL) || (HAVE_DEVPOLL)
 
diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c
index 3bb141e..56e90a3 100644
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -117,6 +117,7 @@
         c->fd = s;
         c->unexpected_eof = 1;
         wev->write = 1;
+        rev->first = wev->first = 1;
 
 #if (HAVE_AIO_EVENT)
         if (!(ngx_event_flags & NGX_HAVE_AIO_EVENT)) {
diff --git a/src/event/ngx_event_acceptex.c b/src/event/ngx_event_acceptex.c
index 0654c80..07d9993 100644
--- a/src/event/ngx_event_acceptex.c
+++ b/src/event/ngx_event_acceptex.c
@@ -104,6 +104,7 @@
 
         c->unexpected_eof = 1;
         wev->write = 1;
+        rev->first = wev->first = 1;
 
         c->handler = ls->handler;
         rev->event_handler = ngx_event_acceptex;
diff --git a/src/event/ngx_event_recv.c b/src/event/ngx_event_recv.c
index 46c165a..c083e8f 100644
--- a/src/event/ngx_event_recv.c
+++ b/src/event/ngx_event_recv.c
@@ -8,18 +8,13 @@
 
 ssize_t ngx_event_recv_core(ngx_connection_t *c, char *buf, size_t size)
 {
-    int                n;
-    ngx_err_t          err;
-    ngx_event_t       *ev;
+    ssize_t       n;
+    ngx_err_t     err;
+    ngx_event_t  *ev;
 
     ev = c->read;
 
-    if (ev->timedout) {
-        ngx_set_socket_errno(NGX_ETIMEDOUT);
-        ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, "recv() failed");
-        return NGX_ERROR;
-    }
-
+/* DEBUG */
 #if (HAVE_KQUEUE)
     if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
         ngx_log_debug(c->log, "ngx_event_recv: eof:%d, avail:%d, err:%d" _
@@ -30,54 +25,32 @@
 #if (USE_KQUEUE)
 
     if (ev->eof && ev->available == 0) {
-        if (ev->error) {
-            ngx_set_socket_errno(ev->error);
 
-            if (ev->error == NGX_ECONNRESET && ev->ignore_econnreset) {
-                return 0;
-            }
-
-            ngx_log_error(NGX_LOG_ERR, c->log, ev->error,
-                          "recv() failed");
-            return NGX_ERROR;
-        }
-
-        return 0;
-    }
-
-#elif (HAVE_KQUEUE)
-
-    if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
-        if (ev->eof && ev->available == 0) {
-            if (ev->error) {
-                ngx_set_socket_errno(ev->error);
-
-                if (ev->error == NGX_ECONNRESET && ev->ignore_econnreset) {
-                    return 0;
-                }
-
-                ngx_log_error(NGX_LOG_ERR, c->log, ev->error,
-                              "recv() failed");
-                return NGX_ERROR;
-            }
-
+        if (ev->error == 0) {
             return 0;
         }
+
+        ngx_set_socket_errno(ev->error);
+        err = ev->error;
+        n = -1;
+
+    } else {
+        n = ngx_recv(c->fd, buf, size, 0);
+
+        if (n == -1) {
+            err = ngx_socket_errno;
+        }
     }
 
-#endif
-
-    n = ngx_recv(c->fd, buf, size, 0);
-
     if (n == -1) {
-        err = ngx_socket_errno;
+        ev->ready = 0;
 
-        if (ev->error == NGX_ECONNRESET && ev->ignore_econnreset) {
+        if (err == NGX_ECONNRESET && ev->ignore_econnreset) {
             return 0;
         }
 
         if (err == NGX_EAGAIN) {
-            ngx_log_error(NGX_LOG_INFO, c->log, err, "recv() returns EAGAIN");
+            ngx_log_error(NGX_LOG_INFO, c->log, err, "recv() returned EAGAIN");
             return NGX_AGAIN;
         }
 
@@ -85,17 +58,90 @@
         return NGX_ERROR;
     }
 
-#if (USE_KQUEUE)
-
     ev->available -= n;
+    if (ev->available == 0) {
+        ev->ready = 0;
+    }
+
+    return n;
 
 #elif (HAVE_KQUEUE)
 
-    if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
-        ev->available -= n;
+    if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT)
+        && ev->eof && ev->available == 0) {
+
+        if (ev->error == 0) {
+            return 0;
+        }
+
+        ngx_set_socket_errno(ev->error);
+        err = ev->error;
+        n = -1;
+
+    } else {
+        n = ngx_recv(c->fd, buf, size, 0);
+ngx_log_debug(c->log, "ngx_event_recv: read:%d:%d" _ n _ size);
+
+        if (n == -1) {
+            err = ngx_socket_errno;
+        }
     }
 
-#endif
+    if (n == -1) {
+        ev->ready = 0;
+
+        if (err == NGX_ECONNRESET && ev->ignore_econnreset) {
+            return 0;
+        }
+
+        if (err == NGX_EAGAIN) {
+            ngx_log_error(NGX_LOG_INFO, c->log, err, "recv() returned EAGAIN");
+            return NGX_AGAIN;
+        }
+
+        ngx_log_error(NGX_LOG_ERR, c->log, err, "recv() failed");
+        return NGX_ERROR;
+    }
+
+    if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
+        ev->available -= n;
+        if (ev->available == 0) {
+            ev->ready = 0;
+        }
+
+    } else if ((size_t) n < size) {
+        ev->ready = 0;
+    }
 
     return n;
+
+#else /* not kqueue */
+
+    n = ngx_recv(c->fd, buf, size, 0);
+
+    if (n == -1) {
+        err = ngx_socket_errno;
+
+        ev->ready = 0;
+
+        if (err == NGX_ECONNRESET && ev->ignore_econnreset) {
+            return 0;
+        }
+
+        if (err == NGX_EAGAIN) {
+            ngx_log_error(NGX_LOG_INFO, c->log, err, "recv() returned EAGAIN");
+            return NGX_AGAIN;
+        }
+
+        ngx_log_error(NGX_LOG_ERR, c->log, err, "recv() failed");
+        return NGX_ERROR;
+    }
+
+    if ((size_t) n < size) {
+        ev->ready = 0;
+    }
+
+    return n;
+
+#endif
 }