changeset 86:3973260705cc

nginx-0.0.1-2003-05-12-19:52:24 import
author Igor Sysoev <igor@sysoev.ru>
date Mon, 12 May 2003 15:52:24 +0000
parents 3549c2bf9eaf
children 5f6d848dcbef
files src/core/nginx.c src/event/modules/ngx_kqueue_module.c src/event/ngx_event.h src/event/ngx_event_accept.c src/event/ngx_event_connect.c src/event/ngx_event_connect.h src/event/ngx_event_timer.c src/http/modules/proxy/ngx_http_event_proxy_handler.c src/http/ngx_http.h src/http/ngx_http_core_module.c src/http/ngx_http_event.c src/http/ngx_http_parse.c src/os/unix/ngx_daemon.c src/os/unix/ngx_freebsd_init.c src/os/unix/ngx_freebsd_init.h src/os/unix/ngx_freebsd_rfork_thread.c src/os/unix/ngx_init.c
diffstat 17 files changed, 762 insertions(+), 357 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -67,6 +67,10 @@ int main(int argc, char *const *argv)
 
 #endif
 
+    if (ngx_os_init(&ngx_log) == NGX_ERROR) {
+        exit(1);
+    }
+
     ngx_init_array(ngx_listening_sockets, ngx_pool, 10, sizeof(ngx_listen_t),
                    1);
 
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -141,7 +141,7 @@ int ngx_kqueue_del_event(ngx_event_t *ev
 
     if (nchanges > 0
         && ev->index < nchanges
-        && change_list[ev->index].udata == ev)
+        && (void *) ((uintptr_t) change_list[ev->index].udata & ~1) == ev)
     {
 #if (NGX_DEBUG_EVENT)
         ngx_connection_t *c = (ngx_connection_t *) ev->data;
@@ -159,8 +159,9 @@ int ngx_kqueue_del_event(ngx_event_t *ev
         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 */
+    /* when the file descriptor is closed a kqueue automatically deletes
+       its filters so we do not need to delete explicity the event
+       before the closing the file descriptor */
 
     if (flags & NGX_CLOSE_EVENT) {
         return NGX_OK;
@@ -200,7 +201,7 @@ int ngx_kqueue_set_event(ngx_event_t *ev
     change_list[nchanges].ident = c->fd;
     change_list[nchanges].filter = filter;
     change_list[nchanges].flags = flags;
-    change_list[nchanges].udata = ev;
+    change_list[nchanges].udata = (void *) ((uintptr_t) ev | ev->instance);
 
 #if (HAVE_LOWAT_EVENT)
 
@@ -230,7 +231,7 @@ int ngx_kqueue_set_event(ngx_event_t *ev
 
 int ngx_kqueue_process_events(ngx_log_t *log)
 {
-    int              events, i;
+    int              events, instance, i;
     ngx_msec_t       timer, delta;
     ngx_event_t      *ev;
     struct timeval   tv;
@@ -310,12 +311,15 @@ int ngx_kqueue_process_events(ngx_log_t 
         }
 
         ev = (ngx_event_t *) event_list[i].udata;
+        instance = (uintptr_t) ev & 1;
+        ev = (void *) ((uintptr_t) ev & ~1);
 
-        /* It's a stale event from a socket
+        /* It's a stale event from a file descriptor
            that was just closed in this iteration */
 
-        if (!ev->active) {
-           continue;
+        if (ev->active == 0 || ev->instance != instance) {
+            ngx_log_debug(log, "stale kevent");
+            continue;
         }
 
         switch (event_list[i].filter) {
@@ -323,28 +327,6 @@ int ngx_kqueue_process_events(ngx_log_t 
         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) {
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -63,7 +63,9 @@ struct ngx_event_s {
 #endif
     unsigned         write:1;
 
-    unsigned         first:1;
+    unsigned         instance:1;  /* used to detect stale events in kqueue,
+                                     rt signals and epoll */
+
     unsigned         active:1;
     unsigned         ready:1;
     unsigned         timedout:1;
@@ -114,6 +116,7 @@ struct ngx_event_s {
 #endif
 };
 
+
 typedef enum {
     NGX_SELECT_EVENT_N = 0,
 #if (HAVE_POLL)
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -16,6 +16,7 @@
 
 int ngx_event_accept(ngx_event_t *ev)
 {
+    int                instance;
     socklen_t          len;
     struct sockaddr   *sa;
     ngx_err_t          err;
@@ -95,6 +96,8 @@ int ngx_event_accept(ngx_event_t *ev)
         wev = &ngx_write_events[s];
         c = &ngx_connections[s];
 
+        instance = rev->instance;
+
         ngx_memzero(rev, sizeof(ngx_event_t));
         ngx_memzero(wev, sizeof(ngx_event_t));
         ngx_memzero(c, sizeof(ngx_connection_t));
@@ -108,6 +111,8 @@ int ngx_event_accept(ngx_event_t *ev)
         c->addr_text_max_len = ls->addr_text_max_len;
         c->post_accept_timeout = ls->post_accept_timeout;
 
+        rev->instance = wev->instance = !instance;
+
         rev->index = wev->index = NGX_INVALID_INDEX;
 
         rev->data = wev->data = c;
@@ -117,7 +122,6 @@ int ngx_event_accept(ngx_event_t *ev)
         c->fd = s;
         c->unexpected_eof = 1;
         wev->write = 1;
-        rev->first = wev->first = 1;
 
 #if (USE_KQUEUE)
         wev->ready = 1;
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_connect.c
@@ -0,0 +1,126 @@
+
+#include <ngx_event_connect.h>
+
+
+int ngx_event_connect_peer(ngx_connect_peer_t *cp)
+{
+
+
+    if (cp->peers->number > 1) {
+
+        /* it's a first try - get current peer */
+
+        if (cp->tries == cp->peers->number) {
+
+            /* Here is the race condition
+               when the peers are shared between
+               the threads or the processes but it should not be serious */
+
+            cp->cur_peer = cp->peers->current++;
+
+            if (cp->peers->current >= cp->peers->number) {
+                cp->peers->current = 0;
+            }
+
+            /* */
+
+#if (NGX_MULTITHREADED || NGX_MULTIPROCESSED)
+            /* eliminate the sequences of the race condition */
+            if (cp->cur_peer >= cp->peers->number) {
+                cp->cur_peer = 0;
+            }
+#endif
+        }
+
+        if (cp->peers->max_fails > 0) {
+
+            for ( ;; ) {
+                peer = &cp->peers->peers[cp->cur_peer];
+
+                /* Here is the race condition
+                   when the peers are shared between
+                   the threads or the processes but it should not be serious */
+
+                if (peer->fails <= cp->peers->max_fails
+                    || (now - peer->accessed > cp->peers->fail_timeout))
+                {
+                    break;
+                }
+
+                /* */
+
+                cp->cur_peer++;
+
+                if (cp->cur_peer >= cp->peers->number) {
+                    cp->cur_peer = 0;
+                }
+
+                cp->tries--;
+
+                if (cp->tries == 0) {
+                    return NGX_ERROR;
+                }
+            }
+        }
+    }
+
+
+
+
+
+    s = ngx_socket(AF_INET, SOCK_STREAM, IPPROTO_IP, 0);
+
+    if (s == -1) {
+        ngx_log_error(NGX_LOG_ALERT, cn->log, ngx_socket_errno,
+                      ngx_socket_n " failed");
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if (cn->rcvbuf) {
+        if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
+                       (const void *) &cn->rcvbuf, sizeof(int)) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cn->log, ngx_socket_errno,
+                          "setsockopt(SO_RCVBUF) failed");
+
+            if (ngx_close_socket(s) == -1) {
+                ngx_log_error(NGX_LOG_ALERT, cn->log, ngx_socket_errno,
+                              ngx_close_socket_n " failed");
+            }
+
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    if (ngx_nonblocking(s) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, cn->log, ngx_socket_errno,
+                      ngx_nonblocking_n " failed");
+
+        if (ngx_close_socket(s) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cn->log, ngx_socket_errno,
+                          ngx_close_socket_n " failed");
+        }
+
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    c = &ngx_connections[s];
+    rev = &ngx_read_events[s];
+    wev = &ngx_write_events[s];
+
+    instance = rev->instance;
+
+    ngx_memzero(c, sizeof(ngx_connection_t));
+    ngx_memzero(rev, sizeof(ngx_event_t));
+    ngx_memzero(wev, sizeof(ngx_event_t));
+
+    rev->index = wev->index = NGX_INVALID_INDEX;
+    rev->data = wev->data = c;
+    c->read = rev;
+    c->write = wev;
+
+    rev->instance = wev->instance = !instance;
+
+    rev->log = wev->log = c->log = cn->log;
+    c->fd = s;
+    wev->close_handler = rev->close_handler = ngx_event_close_connection;
+
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_connect.h
@@ -0,0 +1,43 @@
+#ifndef _NGX_EVENT_CONNECT_H_INCLUDED_
+#define _NGX_EVENT_CONNECT_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_string.h>
+#include <ngx_log.h>
+#include <ngx_event.h>
+#include <ngx_connection.h>
+
+
+typedef struct {
+    u_int32_t          addr;
+    ngx_str_t          host;
+    int                port;
+    ngx_str_t          addr_port_name;
+
+    int                fails;
+    time_t             accessed;
+} ngx_peer_t;
+
+
+typedef struct {
+    int                current;
+    int                number;
+    int                max_fails;
+    int                fail_timeout;
+
+ /* ngx_mutex_t       *mutex; */
+ /* ngx_connection_t  *cached; */
+
+    ngx_peer_t         peers[1];
+} ngx_peers_t;
+
+
+typedef struct {
+    ngx_peers_t       *peers;
+    int                cur_peer;
+    int                tries;
+} ngx_connect_peer_t;
+
+
+#endif /* _NGX_EVENT_CONNECT_H_INCLUDED_ */
--- a/src/event/ngx_event_timer.c
+++ b/src/event/ngx_event_timer.c
@@ -45,7 +45,8 @@ void ngx_event_add_timer(ngx_event_t *ev
 
 #if (NGX_DEBUG_EVENT)
     ngx_connection_t *c = (ngx_connection_t *) ev->data;
-    ngx_log_debug(ev->log, "set timer: %d:%d" _ c->fd _ timer);
+    ngx_log_debug(ev->log, "set timer: %d:%d, slot: %d" _
+                  c->fd _ timer _ ngx_timer_cur_queue);
 #endif
 
     if (ev->timer_next || ev->timer_prev) {
@@ -53,10 +54,6 @@ void ngx_event_add_timer(ngx_event_t *ev
         return;
     }
 
-#if (NGX_DEBUG_EVENT)
-    ngx_log_debug(ev->log, "timer slot: %d" _ ngx_timer_cur_queue);
-#endif
-
     for (e = ngx_timer_queue[ngx_timer_cur_queue].timer_next;
          e != &ngx_timer_queue[ngx_timer_cur_queue] && timer > e->timer_delta;
          e = e->timer_next)
--- a/src/http/modules/proxy/ngx_http_event_proxy_handler.c
+++ b/src/http/modules/proxy/ngx_http_event_proxy_handler.c
@@ -481,7 +481,7 @@ static int ngx_http_proxy_process_upstre
 
 static int ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p)
 {
-    int                         rc, event;
+    int                         rc, event, instance;
     struct sockaddr_in         *addr;
     ngx_err_t                   err;
     ngx_socket_t                s;
@@ -590,6 +590,8 @@ static int ngx_http_proxy_connect(ngx_ht
     rev = &ngx_read_events[s];
     wev = &ngx_write_events[s];
 
+    instance = rev->instance;
+
     ngx_memzero(c, sizeof(ngx_connection_t));
     ngx_memzero(rev, sizeof(ngx_event_t));
     ngx_memzero(wev, sizeof(ngx_event_t));
@@ -598,7 +600,9 @@ static int ngx_http_proxy_connect(ngx_ht
     rev->data = wev->data = c;
     c->read = rev;
     c->write = wev;
-    rev->first = wev->first = 1;
+
+    rev->instance = wev->instance = !instance;
+
     rev->log = wev->log = c->log = p->log;
     c->fd = s;
     wev->close_handler = rev->close_handler = ngx_event_close_connection;
@@ -1002,7 +1006,7 @@ static int ngx_http_proxy_process_upstre
     r = p->request;
 
     for ( ;; ) {
-        rc = ngx_read_http_header_line(r, p->header_in);
+        rc = ngx_parse_http_header_line(r, p->header_in);
 
         /* a header line has been parsed successfully */
 
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -231,9 +231,9 @@ typedef int (*ngx_http_output_body_filte
 int ngx_http_init(ngx_pool_t *pool, ngx_log_t *log);
 /**/
 
-int ngx_http_init_connection(ngx_connection_t *c);
-int ngx_read_http_request_line(ngx_http_request_t *r);
-int ngx_read_http_header_line(ngx_http_request_t *r, ngx_hunk_t *h);
+void ngx_http_init_connection(ngx_connection_t *c);
+int ngx_parse_http_request_line(ngx_http_request_t *r);
+int ngx_parse_http_header_line(ngx_http_request_t *r, ngx_hunk_t *h);
 int ngx_http_handler(ngx_http_request_t *r);
 
 
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -568,9 +568,9 @@ int ngx_http_close_request(ngx_http_requ
         c->write->timer_set = 0;
     }
 
-    ngx_log_debug(c->log, "http closed");
+    ngx_log_debug(c->log, "http request closed");
 
-    return NGX_ERROR;  /* to close connection */
+    return 0;
 }
 
 
--- a/src/http/ngx_http_event.c
+++ b/src/http/ngx_http_event.c
@@ -18,10 +18,17 @@
 #include <ngx_http_output_filter.h>
 
 
-static int ngx_http_init_request(ngx_event_t *ev);
+static void ngx_http_init_request(ngx_event_t *ev);
+static void ngx_http_process_request_line(ngx_event_t *rev);
+static void ngx_http_process_request_headers(ngx_event_t *rev);
+
+
+
+static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);
+
+
+
 static int ngx_http_process_request(ngx_event_t *ev);
-static int ngx_http_process_request_line(ngx_http_request_t *r);
-static int ngx_http_process_request_headers(ngx_http_request_t *r);
 static int ngx_http_process_request_header_line(ngx_http_request_t *r);
 static int ngx_http_request_handler(ngx_http_request_t *r, int error);
 
@@ -33,11 +40,12 @@ static int ngx_http_keepalive_handler(ng
 static int ngx_http_set_lingering_close(ngx_http_request_t *r);
 static int ngx_http_lingering_close_handler(ngx_event_t *ev);
 
-static int ngx_http_close_connection(ngx_event_t *ev);
+static int ngx_http_close_connection(ngx_connection_t *c);
 static void ngx_http_header_parse_error(ngx_http_request_t *r, int parse_err);
 static size_t ngx_http_log_error(void *data, char *buf, size_t len);
 
 
+/* NGX_HTTP_PARSE_ ... errors */
 
 static char *header_errors[] = {
     "client %s sent invalid method",
@@ -67,127 +75,142 @@ static ngx_http_header_t headers_in[] = 
 };
 
 
-int ngx_http_init_connection(ngx_connection_t *c)
+void ngx_http_init_connection(ngx_connection_t *c)
 {
+    int                  event;
     ngx_event_t         *rev;
-    ngx_http_log_ctx_t  *ctx;
+    ngx_http_log_ctx_t  *lcx;
+
+    c->addr_text.data = ngx_palloc(c->pool, c->addr_text_max_len);
+    if (c->addr_text.data == NULL) {
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    c->addr_text.len = ngx_sock_ntop(c->family, c->sockaddr,
+                                     c->addr_text.data, c->addr_text_max_len);
+    if (c->addr_text.len == 0) {
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    lcx = ngx_pcalloc(c->pool, sizeof(ngx_http_log_ctx_t));
+    if (lcx == NULL) {
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    lcx->client = c->addr_text.data;
+    lcx->action = "reading client request line";
+    c->log->data = lcx;
+    c->log->handler = ngx_http_log_error;
 
     rev = c->read;
     rev->event_handler = ngx_http_init_request;
 
-    rev->close_handler = ngx_http_close_connection;
-    c->write->close_handler = ngx_http_close_connection;
-
-    ngx_test_null(c->addr_text.data, ngx_palloc(c->pool, c->addr_text_max_len),
-                  NGX_ERROR);
-
-    c->addr_text.len = ngx_sock_ntop(c->family, c->sockaddr,
-                                     c->addr_text.data, c->addr_text_max_len);
-
-    if (c->addr_text.len == 0) {
-        return NGX_ERROR;
+    if (rev->ready) {
+        /* deferred accept */
+        ngx_http_init_request(rev);
+        return;
     }
 
-    ngx_test_null(ctx, ngx_pcalloc(c->pool, sizeof(ngx_http_log_ctx_t)),
-                  NGX_ERROR);
-    ctx->client = c->addr_text.data;
-    ctx->action = "reading client request line";
-    c->log->data = ctx;
-    c->log->handler = ngx_http_log_error;
-
-#if (HAVE_DEFERRED_ACCEPT)
-
-    if (rev->ready) {
-        return ngx_http_init_request(rev);
-    }
-
-#endif
-
     ngx_add_timer(rev, c->post_accept_timeout);
     rev->timer_set = 1;
 
-#if (USE_KQUEUE)
-
-    return ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT);
-
-#else
-
-    /* kqueue */
-
-    if (ngx_event_flags & NGX_HAVE_CLEAR_EVENT) {
-        return ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT);
+    if (ngx_event_flags & (NGX_HAVE_AIO_EVENT|NGX_HAVE_EDGE_EVENT)) {
+        /* aio, iocp, epoll */
+        ngx_http_init_request(rev);
+        return;
     }
 
-    /* aio, iocp, epoll */
+    if (ngx_event_flags & NGX_HAVE_CLEAR_EVENT) {
+        /* kqueue */
+        event = NGX_CLEAR_EVENT;
 
-    if (ngx_event_flags & (NGX_HAVE_AIO_EVENT|NGX_HAVE_EDGE_EVENT)) {
-        return ngx_http_init_request(rev);
+    } else {
+        /* select, poll, /dev/poll */
+        event = NGX_LEVEL_EVENT;
     }
 
-    /* select, poll, /dev/poll */
+    if (ngx_add_event(rev, NGX_READ_EVENT, event) == NGX_ERROR) {
+        ngx_http_close_connection(c);
+    }
 
-    return ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT);
-
-#endif /* USE_KQUEUE */
+    return;
 }
 
 
-static int ngx_http_init_request(ngx_event_t *rev)
+static void ngx_http_init_request(ngx_event_t *rev)
 {
     ngx_connection_t     *c;
     ngx_http_request_t   *r;
     ngx_http_conf_ctx_t  *ctx;
 
     c = (ngx_connection_t *) rev->data;
-    c->sent = 0;
-
-    ngx_test_null(r, ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)),
-                  NGX_ERROR);
-
-    c->data = r;
-    r->connection = c;
-    r->file.fd = NGX_INVALID_FILE;
 
     if (c->buffer == NULL) {
-        ngx_test_null(c->buffer,
-                      ngx_create_temp_hunk(c->pool,
-                                           ngx_http_client_header_buffer_size,
-                                           0, 0),
-                      NGX_ERROR);
-    } else {
-        r->header_read = 1;
+        c->buffer = ngx_create_temp_hunk(c->pool,
+                                         ngx_http_client_header_buffer_size,
+                                         0, 0);
+        if (c->buffer == NULL) {
+            ngx_http_close_connection(c);
+            return;
+        }
+    }
+
+    r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t));
+    if (r == NULL) {
+        ngx_http_close_connection(c);
+        return;
     }
 
-    r->pipeline = c->pipeline;
-    r->header_in = c->buffer;
+    r->pool = ngx_create_pool(ngx_http_request_pool_size, c->log);
+    if (r->pool == NULL) {
+        ngx_http_close_connection(c);
+        return;
+    }
 
-    ngx_test_null(r->pool, ngx_create_pool(ngx_http_request_pool_size, c->log),
-                  NGX_ERROR);
+    r->headers_out.headers = ngx_create_table(r->pool, 10);
+    if (r->headers_out.headers == NULL) {
+        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        ngx_http_close_connection(c);
+        return;
+    }
 
-    ngx_test_null(r->ctx,
-                  ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module),
-                  ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR));
+    r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
+    if (r->ctx == NULL) {
+        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        ngx_http_close_connection(c);
+        return;
+    }
 
     ctx = (ngx_http_conf_ctx_t *) c->ctx;
     r->srv_conf = ctx->srv_conf;
     r->loc_conf = ctx->loc_conf;
 
+    c->sent = 0;
+    c->data = r;
+    r->connection = c;
+    r->pipeline = c->pipeline;
+    r->header_in = c->buffer;
+
+    r->file.fd = NGX_INVALID_FILE;
+
     r->headers_in.content_length_n = -1;
-    r->headers_out.headers = ngx_create_table(r->pool, 10);
     r->headers_out.content_length = -1;
     r->headers_out.last_modified_time = -1;
 
-    rev->event_handler = ngx_http_process_request;
-    r->state_handler = ngx_http_process_request_line;
+    rev->event_handler = ngx_http_process_request_line;
+    ngx_http_process_request_line(rev);
 
-    return ngx_http_process_request(rev);
+    return;
 }
 
 
-static int ngx_http_process_request(ngx_event_t *rev)
+static void ngx_http_process_request_line(ngx_event_t *rev)
 {
-    int                  n, rc;
-    ngx_event_t         *wev;
+    int                  rc, offset;
+    ssize_t              n;
     ngx_connection_t    *c;
     ngx_http_request_t  *r;
     ngx_http_log_ctx_t  *lcx;
@@ -195,139 +218,35 @@ static int ngx_http_process_request(ngx_
     c = (ngx_connection_t *) rev->data;
     r = (ngx_http_request_t *) c->data;
 
-    ngx_log_debug(c->log, "http process request");
+    ngx_log_debug(rev->log, "http process request line");
 
     if (rev->timedout) {
-        return ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+        ngx_http_close_connection(c);
+        return;
     }
 
-    do {
-
-        if (r->header_read) {
-            r->header_read = 0;
-            ngx_log_debug(c->log, "http preread %d" _
-                          r->header_in->last - r->header_in->pos);
-
-        } else {
-            n = ngx_event_recv(c, r->header_in->last,
-                               r->header_in->end - r->header_in->last);
-
-            if (n == NGX_AGAIN) {
-                if (!r->header_timeout_set) {
-
-                    if (rev->timer_set) {
-                        ngx_del_timer(rev);
-                    } else {
-                        rev->timer_set = 1;
-                    }
-
-                    ngx_add_timer(rev, ngx_http_client_header_timeout);
-                    r->header_timeout_set = 1;
-                }
-
-                return NGX_AGAIN;
-            }
-
-            if (n == NGX_ERROR) {
-                return ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);
-            }
-
-            ngx_log_debug(c->log, "http read %d" _ n);
-
-            if (n == 0) {
-                ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                              "client closed prematurely connection");
-                return ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);
-            }
-
-            r->header_in->last += n;
-        }
+    n = ngx_http_read_request_header(r);
 
-        /* the state handlers are called in the following order:
-            ngx_http_process_request_line(r)
-            ngx_http_process_request_headers(r) */
-
-        do {
-            rc = r->state_handler(r);
-
-        } while (rc == NGX_AGAIN && r->header_in->pos < r->header_in->last);
-
-    } while (rc == NGX_AGAIN
-             && (rev->ready || ngx_event_flags & NGX_HAVE_AIO_EVENT));
-
-    if (rc >= NGX_OK) {
-
-        /* HTTP header done */
-
-        rev->event_handler = ngx_http_block_read;
-
-        if (rc != NGX_OK) {
-            return ngx_http_finalize_request(r, rc);
-        }
-
-        lcx = c->log->data;
-        lcx->action = "processing client request";
-
-#if 0
-        wev = c->write;
-        ngx_add_timer(wev, 5000);
-        wev->delayed = 1;
-        wev->timer_set = 1;
-#endif
-
-        rc = ngx_http_handler(r);
-
-        /* a handler does its own processing */
-        if (rc == NGX_DONE) {
-            return rc;
-        }
-
-        if (rc == NGX_ERROR) {
-            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
-        }
-
-        return ngx_http_finalize_request(r, rc);
-
+    if (n == NGX_AGAIN || n == NGX_ERROR) {
+        return;
     }
 
-    /* NGX_AGAIN */
-
-    if (!r->header_timeout_set) {
-
-        if (rev->timer_set) {
-            ngx_del_timer(rev);
-        } else {
-            rev->timer_set = 1;
-        }
-
-        ngx_add_timer(rev, ngx_http_client_header_timeout);
-        r->header_timeout_set = 1;
-    }
-
-    return rc;
-}
-
-
-static int ngx_http_process_request_line(ngx_http_request_t *r)
-{
-    int                  rc, offset;
-    ngx_connection_t    *c;
-    ngx_http_log_ctx_t  *lcx;
-
-    rc = ngx_read_http_request_line(r);
-
-    c = r->connection;
-
-    /* a request line has been parsed successfully */
+    rc = ngx_parse_http_request_line(r);
 
     if (rc == NGX_OK) {
 
+        /* the request line has been parsed successfully */
+
         if (r->http_version >= NGX_HTTP_VERSION_10
             && ngx_http_large_client_header == 0
             && r->header_in->pos == r->header_in->end)
         {
+            /* no space for "\r\n" at the end of the header */
+
             ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_URI);
-            return NGX_HTTP_REQUEST_URI_TOO_LARGE;
+            ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);
+            return;
         }
 
         /* copy URI */
@@ -338,20 +257,28 @@ static int ngx_http_process_request_line
             r->uri.len = r->uri_end - r->uri_start;
         }
 
-        ngx_test_null(r->uri.data, ngx_palloc(r->pool, r->uri.len + 1),
-                      NGX_HTTP_INTERNAL_SERVER_ERROR);
+        r->uri.data = ngx_palloc(r->pool, r->uri.len + 1);
+        if (r->uri.data == NULL) {
+            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            ngx_http_close_connection(c);
+            return;
+        }
 
         ngx_cpystrn(r->uri.data, r->uri_start, r->uri.len + 1);
 
         r->request_line.len = r->request_end - r->request_start;
 
-        /* if the large client headers are enabled then
+        /* if the large client header is enabled then
            we need to copy a request line */
 
         if (ngx_http_large_client_header) {
-            ngx_test_null(r->request_line.data,
-                          ngx_palloc(r->pool, r->request_line.len + 1),
-                          NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+            r->request_line.data = ngx_palloc(r->pool, r->request_line.len + 1);
+            if (r->request_line.data == NULL) {
+                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                ngx_http_close_connection(c);
+                return;
+            }
 
             ngx_cpystrn(r->request_line.data, r->request_start,
                         r->request_line.len + 1);
@@ -370,9 +297,12 @@ static int ngx_http_process_request_line
                 r->exten.len = r->uri_end - r->uri_ext;
             }
 
-            ngx_test_null(r->exten.data,
-                          ngx_palloc(r->pool, r->exten.len + 1), 
-                          NGX_HTTP_INTERNAL_SERVER_ERROR);
+            r->exten.data = ngx_palloc(r->pool, r->exten.len + 1);
+            if (r->exten.data == NULL) {
+                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                ngx_http_close_connection(c);
+                return;
+            }
 
             ngx_cpystrn(r->exten.data, r->uri_ext, r->exten.len + 1);
         }
@@ -382,67 +312,59 @@ static int ngx_http_process_request_line
         if (r->args_start && r->uri_end > r->args_start) {
             r->args.len = r->uri_end - r->args_start;
 
-            ngx_test_null(r->args.data,
-                          ngx_palloc(r->pool, r->args.len + 1),
-                          NGX_HTTP_INTERNAL_SERVER_ERROR);
+            r->args.data = ngx_palloc(r->pool, r->args.len + 1);
+            if (r->args.data == NULL) {
+                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                ngx_http_close_connection(c);
+                return;
+            }
 
             ngx_cpystrn(r->args.data, r->args_start, r->args.len + 1);
         }
 
-#if 1
-        if (r->exten.data == NULL) {
-            r->exten.data = "";
-        }
-        if (r->args.data == NULL) {
-            r->args.data = "";
-        }
-        ngx_log_debug(r->connection->log, "HTTP: %d, %d, '%s', '%s', '%s'" _
+#if 1 /* DEBUG */
+        if (r->exten.data == NULL) { r->exten.data = ""; }
+        if (r->args.data == NULL) { r->args.data = ""; }
+        ngx_log_debug(c->log, "HTTP: %d, %d, '%s', '%s', '%s'" _
                       r->method _ r->http_version _
                       r->uri.data _ r->exten.data _ r->args.data);
-        if (r->exten.data[0] == '\0') {
-            r->exten.data = NULL;
-        }
-        if (r->args.data[0] == '\0') {
-            r->args.data = NULL;
-        }
+        if (r->exten.data[0] == '\0') { r->exten.data = NULL; }
+        if (r->args.data[0] == '\0') { r->args.data = NULL; }
 #endif
 
-        lcx = r->connection->log->data;
+        lcx = c->log->data;
 
         if (ngx_http_url_in_error_log) {
-            ngx_test_null(lcx->url,
-                          ngx_palloc(r->pool, r->uri_end - r->uri_start + 1),
-                          NGX_HTTP_INTERNAL_SERVER_ERROR);
+            lcx->url = ngx_palloc(r->pool, r->uri_end - r->uri_start + 1);
+            if (lcx->url == NULL) {
+                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                ngx_http_close_connection(c);
+                return;
+            }
 
             ngx_cpystrn(lcx->url, r->uri_start, r->uri_end - r->uri_start + 1);
         }
 
-        /* if we need to parse the headers then return NGX_AGAIN
-           becuase of HTTP/0.9 has no headers so return NGX_OK */
-
         if (r->http_version == NGX_HTTP_VERSION_9) {
-            r->state_handler = NULL;
-            return NGX_OK;
+            /* STUB */ return;
         }
 
+        lcx->action = "reading client request headers";
         r->headers_in.headers = ngx_create_table(r->pool, 10);
 
-        r->state_handler = ngx_http_process_request_headers;
-        lcx->action = "reading client request headers";
-
         if (ngx_http_large_client_header
             && r->header_in->pos == r->header_in->last)
         {
             r->header_in->pos = r->header_in->last = r->header_in->start;
         }
 
-        return NGX_AGAIN;
+    } else if (rc != NGX_AGAIN) {
 
-    /* there was error while a request line parsing */
+        /* there was error while a request line parsing */
 
-    } else if (rc != NGX_AGAIN) {
         ngx_http_header_parse_error(r, rc);
-        return NGX_HTTP_BAD_REQUEST;
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return;
     }
 
     /* NGX_AGAIN: a request line parsing is still not complete */
@@ -450,17 +372,18 @@ static int ngx_http_process_request_line
     if (r->header_in->last == r->header_in->end) {
 
         /* If it's a pipelined request and a request line is not complete
-           then we need to copy it to the start of r->header_in hunk.
+           then we need to copy it to the start of the r->header_in hunk.
            We need to copy it here only if the large client headers
            are enabled otherwise a request line had been already copied
-           to the start of r->header_in hunk in ngx_http_set_keepalive() */
+           to the start of the r->header_in hunk in ngx_http_set_keepalive() */
 
         if (ngx_http_large_client_header) {
             offset = r->request_start - r->header_in->start;
 
             if (offset == 0) {
                 ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_URI);
-                return NGX_HTTP_REQUEST_URI_TOO_LARGE;
+                ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);
+                return;
             }
 
             ngx_memcpy(r->header_in->start, r->request_start,
@@ -481,22 +404,41 @@ static int ngx_http_process_request_line
 
         } else {
             ngx_http_header_parse_error(r, NGX_HTTP_PARSE_TOO_LONG_URI);
-            return NGX_HTTP_REQUEST_URI_TOO_LARGE;
+            ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);
+            return;
         }
     }
 
-    return NGX_AGAIN;
+    rev->event_handler = ngx_http_process_request_headers;
+    ngx_http_process_request_headers(rev);
+
+    return;
 }
 
 
-static int ngx_http_process_request_headers(ngx_http_request_t *r)
+static void ngx_http_process_request_headers(ngx_event_t *rev)
 {
     int                  rc, offset;
     size_t               len;
+    ssize_t              n;
+    ngx_connection_t    *c;
+    ngx_http_request_t  *r;
     ngx_http_log_ctx_t  *ctx;
 
+    if (rev->timedout) {
+        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+        ngx_http_close_connection(c);
+        return;
+    }
+
+    n = ngx_http_read_request_header(r);
+
+    if (n == NGX_AGAIN || n == NGX_ERROR) {
+        return;
+    }
+
     for ( ;; ) {
-        rc = ngx_read_http_header_line(r, r->header_in);
+        rc = ngx_parse_http_header_line(r, r->header_in);
 
         /* a header line has been parsed successfully */
 
@@ -792,6 +734,53 @@ static int ngx_http_writer(ngx_event_t *
 }
 
 
+static ssize_t ngx_http_read_request_header(ngx_http_request_t *r)
+{
+    ssize_t       n;
+    ngx_event_t  *rev;
+
+    n = r->header_in->last - r->header_in->pos;
+
+    if (n > 0) {
+        return n;
+    }
+
+    n = ngx_event_recv(r->connection, r->header_in->last,
+                       r->header_in->end - r->header_in->last);
+
+    if (n == NGX_AGAIN) {
+        if (!r->header_timeout_set) {
+            rev = r->connection->read;
+
+            if (rev->timer_set) {
+                ngx_del_timer(rev);
+            } else {
+                rev->timer_set = 1;
+            }
+
+            ngx_add_timer(rev, ngx_http_client_header_timeout);
+            r->header_timeout_set = 1;
+        }
+
+        return NGX_AGAIN;
+    }
+
+    if (n == 0) {
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                      "client closed prematurely connection");
+
+    if (n == 0 || n == NGX_ERROR) {
+        ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);
+        ngx_http_close_connection(c);
+        return NGX_ERROR;
+    }
+
+    r->header_in->last += n;
+
+    return n;
+}
+
+
 static int ngx_http_block_read(ngx_event_t *ev)
 {
     ngx_log_debug(ev->log, "http read blocked");
@@ -1144,7 +1133,46 @@ static int ngx_http_lingering_close_hand
 }
 
 
-static int ngx_http_close_connection(ngx_event_t *ev)
+static void ngx_http_close_connection(ngx_connection_t *c)
+{
+    ngx_log_debug(c->log, "close connection: %d" _ c->fd);
+
+    if (c->fd == -1) {
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0, "connection already closed");
+        return;
+    }
+
+    if (c->read->timer_set) {
+        ngx_del_timer(c->read);
+        c->read->timer_set = 0;
+    }
+
+    if (c->read->active) {
+        ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
+    }
+
+    if (c->write->timer_set) {
+        ngx_del_timer(c->write);
+        c->write->timer_set = 0;
+    }
+
+    if (c->write->active) {
+        ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);
+    }
+
+    if (ngx_close_socket(c->fd) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,
+                      ngx_close_socket_n " failed");
+    }
+
+    c->fd = -1;
+
+    ngx_destroy_pool(c->pool);
+
+    return;
+}
+
+static int ngx_http_close_connection0(ngx_event_t *ev)
 {
     return ngx_event_close_connection(ev);
 }
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -3,7 +3,7 @@
 #include <ngx_core.h>
 #include <ngx_http.h>
 
-int ngx_read_http_request_line(ngx_http_request_t *r)
+int ngx_parse_http_request_line(ngx_http_request_t *r)
 {
     char   ch;
     char  *p;
@@ -326,7 +326,7 @@ int ngx_read_http_request_line(ngx_http_
     }
 }
 
-int ngx_read_http_header_line(ngx_http_request_t *r, ngx_hunk_t *h)
+int ngx_parse_http_header_line(ngx_http_request_t *r, ngx_hunk_t *h)
 {
     char   c, ch;
     char  *p;
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_daemon.c
@@ -0,0 +1,80 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_log.h>
+
+/* daemon in Linux */
+
+int ngx_daemon(ngx_log_t *log)
+{
+    int  fd;
+
+    switch (fork()) {
+    case -1:
+        ngx_log_error(NGX_LOG_ALERT, log, errno, "fork() failed");
+        return NGX_ERROR;
+
+    case 0:
+        break;
+
+    default:
+        exit(0);
+    }
+
+    if (setsid() == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, errno, "setsid() failed");
+        return NGX_ERROR;
+    }
+
+#if (__SVR4 || linux)
+
+    /* need HUP IGN ? check in Solaris and Linux */
+
+    switch (fork()) {
+    case -1:
+        ngx_log_error(NGX_LOG_ALERT, log, errno, "fork() failed");
+        return NGX_ERROR;
+
+    case 0:
+        break;
+
+    default:
+        exit(0);
+    }
+
+#endif
+
+    umask(0);
+
+#if 0
+    fd = open("/dev/null", O_RDWR);
+    if (fd == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, errno, "open(\"/dev/null\") failed");
+        return NGX_ERROR;
+    }
+
+    if (dup2(fd, STDIN_FILENO) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, errno, "dup2(STDIN) failed");
+        return NGX_ERROR;
+    }
+
+    if (dup2(fd, STDOUT_FILENO) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, errno, "dup2(STDOUT) failed");
+        return NGX_ERROR;
+    }
+
+    if (dup2(fd, STDERR_FILENO) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, errno, "dup2(STDERR) failed");
+        return NGX_ERROR;
+    }
+
+    if (fd > STDERR_FILENO) {
+        if (close(fd) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, log, errno, "close() failed");
+            return NGX_ERROR;
+        }
+    }
+#endif
+
+    return NGX_OK;
+}
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_freebsd_init.c
@@ -0,0 +1,67 @@
+
+#include <ngx_freebsd_init.h>
+
+
+int freebsd_kern_osreldate;
+int freebsd_hw_ncpu;
+
+int freebsd_sendfile_nbytes_bug;
+
+
+int ngx_os_init(ngx_log_t *log)
+{
+    size_t  size;
+
+    size = 4;
+    if (sysctlbyname("kern.osreldate",
+                     &freebsd_kern_osreldate, &size, NULL, 0) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, errno,
+                      "sysctlbyname(kern.osreldate) failed");
+        return NGX_ERROR;
+    }
+
+    ngx_log_error(NGX_LOG_INFO, log, 0,
+                  "kern.osreldate: %d, built on %d",
+                  freebsd_kern_osreldate, __FreeBSD_version);
+
+
+#if HAVE_FREEBSD_SENDFILE
+
+    /* The determination of the sendfile() nbytes bug is complex enough.
+       There're two sendfile() syscalls: a new 393 has no bug while
+       an old 336 has the bug in some versions and has not in others.
+       libc_r wrapper also emulates the bug in some versions.
+       There's no way to say exactly if a given FreeBSD version has bug.
+       Here is the algorithm that work at least for RELEASEs
+       and for syscalls only (not libc_r wrapper). */
+
+    /* detect was the new sendfile() version available at the compile time
+       to allow an old binary to run correctly on an updated FreeBSD system. */
+
+#if (__FreeBSD__ == 4 && __FreeBSD_version >= 460102) \
+    || __FreeBSD_version == 460002 || __FreeBSD_version >= 500039
+
+    /* a new syscall without the bug */
+    freebsd_sendfile_nbytes_bug = 0;
+
+#else
+
+    /* an old syscall that can have the bug */
+    freebsd_sendfile_nbytes_bug = 1;
+
+#endif
+
+#endif /* HAVE_FREEBSD_SENDFILE */
+
+
+    size = 4;
+    if (sysctlbyname("hw.ncpu", &freebsd_hw_ncpu, &size, NULL, 0) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, errno,
+                      "sysctlbyname(hw.ncpu) failed");
+        return NGX_ERROR;
+    }
+
+    ngx_log_error(NGX_LOG_INFO, log, 0, "hw.ncpu: %d", freebsd_hw_ncpu);
+
+    return NGX_OK;
+}
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_freebsd_init.h
@@ -0,0 +1,18 @@
+#ifndef _NGX_OS_INIT_H_INCLUDED_
+#define _NGX_OS_INIT_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_log.h>
+#include <sys/sysctl.h>
+
+
+int ngx_os_init(ngx_log_t *log);
+
+
+extern int freebsd_kern_osreldate;
+extern int freebsd_hw_ncpu;
+
+
+#endif /* _NGX_OS_INIT_H_INCLUDED_ */
--- a/src/os/unix/ngx_freebsd_rfork_thread.c
+++ b/src/os/unix/ngx_freebsd_rfork_thread.c
@@ -11,57 +11,76 @@ extern int   __isthreaded;
 
 typedef int  ngx_tid_t;
 
-#define NGX_MAX_THREADS  10
-
 
 static inline int ngx_gettid();
 
 
-static char        *stacks_start;
-static char        *stacks_end;
-static size_t       stack_size;
-static char        *last_stack;
-static int          last_thread;
-
-static ngx_log_t   *log;
-
-static ngx_tid_t    tids[NGX_MAX_THREADS];
-
+static char        *usrstack;
 static int          red_zone = 4096;
 
+static size_t       stack_size;
+static size_t       usable_stack_size;
+static char        *last_stack;
+
+static int          threads;
+static int          nthreads;
+static ngx_tid_t   *tids;
 
 /* the thread-safe errno */
 
-static int   errnos[NGX_MAX_THREADS];
+static int   errno0;   /* the main thread's errno */
+static int  *errnos;
 
 int *__error()
 {
-    return &errnos[ngx_gettid()];
+    int  tid;
+
+    tid = ngx_gettid();
+
+    return tid ? &errnos[tid - 1] : &errno0;
 }
 
 
-int ngx_create_thread(ngx_tid_t *tid, int (*func)(void *arg), void *arg)
+int ngx_create_thread(ngx_tid_t *tid, int (*func)(void *arg), void *arg,
+                      ngx_log_t *log)
 {
     int         id, err;
-    char       *stack_top;
+    char       *stack, *stack_top;
 
-    last_stack += stack_size;
-    stack_top = last_stack - red_zone;
-
-    if (stack_top > stacks_end) {
-        ngx_log_error(NGX_LOG_CRIT, log, 0, "no more threads allocated");
+    if (threads >= nthreads) {
+        ngx_log_error(NGX_LOG_CRIT, log, 0,
+                      "no more than %d threads can be created", nthreads);
         return NGX_ERROR;
     }
 
-#if 0
-    id = rfork(RFFDG|RFCFDG);
-#elif 0
+    last_stack -= stack_size;
+    stack = mmap(last_stack, usable_stack_size, PROT_READ|PROT_WRITE,
+                 MAP_STACK, -1, 0);
+    if (stack == MAP_FAILED) {
+        ngx_log_error(NGX_LOG_ALERT, log, errno,
+                      "mmap(%08X:%d, MAP_STACK) thread stack failed",
+                      last_stack, usable_stack_size);
+        return NGX_ERROR;
+    }
+
+    if (stack != last_stack) {
+        ngx_log_error(NGX_LOG_ALERT, log, 0, "stack address was changed");
+    }
+
+    stack_top = stack + usable_stack_size;
+
+printf("stack: %08X-%08X\n", stack, stack_top);
+
+#if 1
+    id = rfork_thread(RFPROC|RFTHREAD|RFMEM, stack_top, func, arg);
+#elif 1
+    id = rfork_thread(RFPROC|RFMEM, stack_top, func, arg);
+#elif 1
     id = rfork_thread(RFFDG|RFCFDG, stack_top, func, arg);
-#elif 0
-    id = rfork_thread(RFPROC|RFMEM, stack_top, func, arg);
 #else
-    id = rfork_thread(RFPROC|RFTHREAD|RFMEM, stack_top, func, arg);
+    id = rfork(RFFDG|RFCFDG);
 #endif
+
     err = errno;
 
     if (id == -1) {
@@ -69,7 +88,8 @@ int ngx_create_thread(ngx_tid_t *tid, in
 
     } else {
         *tid = id;
-        tids[last_thread++] = id;
+        threads = (usrstack - stack_top) / stack_size;
+        tids[threads] = id;
 
         /* allow the spinlock in libc malloc() */
         __isthreaded = 1;
@@ -79,14 +99,12 @@ int ngx_create_thread(ngx_tid_t *tid, in
 }
 
 
-int ngx_init_thread_env(int n, size_t size, ngx_log_t *lg)
+int ngx_init_thread_env(int n, size_t size, ngx_log_t *log)
 {
-    int    len, i;
-    char  *usrstack, *zone;
+    int    len;
+    char  *rz, *zone;
 
-    log = lg;
-
-    /* create the thread stacks */
+    nthreads = n;
 
     len = 4;
     if (sysctlbyname("kern.usrstack", &usrstack, &len, NULL, 0) == -1) {
@@ -96,41 +114,37 @@ int ngx_init_thread_env(int n, size_t si
     }
 
 printf("usrstack: %08X\n", usrstack);
-printf("red zone: %08X\n", usrstack - (size + red_zone));
+
+    /* red zone */
+    rz = usrstack - (size + red_zone);
 
-#if 1
-    /* red zone */
-    zone = mmap(usrstack - (size + red_zone), red_zone,
-                PROT_NONE, MAP_ANON, -1, 0);
+printf("red zone: %08X\n", rz);
+
+    zone = mmap(rz, red_zone, PROT_NONE, MAP_ANON, -1, 0);
     if (zone == MAP_FAILED) {
         ngx_log_error(NGX_LOG_ALERT, log, errno,
-                      "mmap(%d, PROT_NONE, MAP_ANON) failed", red_zone);
+                      "mmap(%08X:%d, PROT_NONE, MAP_ANON) red zone failed",
+                      rz, red_zone);
         return NGX_ERROR;
     }
-#else
-    zone = usrstack - (size + red_zone);
-#endif
+
+    if (zone != rz) {
+        ngx_log_error(NGX_LOG_ALERT, log, 0, "red zone address was changed");
+    }
+
+    /* create the thread errno array */
+    ngx_test_null(errnos, ngx_calloc(n * sizeof(int), log), NGX_ERROR);
+
+    /* create the thread tid array */
+    ngx_test_null(tids, ngx_calloc((n + 1) * sizeof(ngx_tid_t), log),
+                  NGX_ERROR);
+
+    tids[0] = ngx_getpid();
+    threads = 1;
 
     last_stack = zone + red_zone;
-
-    for (i = 0; i < n; i++) {
-        last_stack -= size + red_zone;
-printf("stack: %08X\n", last_stack);
-        last_stack = mmap(last_stack, size, PROT_READ|PROT_WRITE,
-                          MAP_STACK, -1, 0);
-        if (last_stack == MAP_FAILED) {
-            ngx_log_error(NGX_LOG_ALERT, log, errno,
-                          "mmap(%d, MAP_STACK) failed", size);
-            return NGX_ERROR;
-        }
-    }
-
-    stacks_start = last_stack;
+    usable_stack_size = size;
     stack_size = size + red_zone;
-    stacks_end = stacks_start + n * stack_size;
-
-    tids[0] = ngx_getpid();
-    last_thread = 1;
 
     return NGX_OK;
 }
@@ -138,7 +152,18 @@ printf("stack: %08X\n", last_stack);
 
 ngx_tid_t ngx_thread_self()
 {
-    return tids[ngx_gettid()];
+    int        tid;
+    ngx_tid_t  pid;
+
+    tid = ngx_gettid();
+
+    if (tids[tid] == 0) {
+        pid = ngx_getpid();
+        tids[tid] = pid;
+        return pid;
+    }
+
+    return tids[tid];
 }
 
 
@@ -146,7 +171,11 @@ static inline int ngx_gettid()
 {   
     char  *sp;
 
+    if (stack_size == 0) {
+        return 0;
+    }
+
     __asm__ ("mov %%esp, %0" : "=q" (sp));
 
-    return (sp > stacks_end) ? 0 : ((sp - stacks_start) / stack_size  + 1);
+    return (usrstack - sp) / stack_size;
 }
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_init.c
@@ -0,0 +1,20 @@
+
+
+int ngx_unix_init(ngx_log_t *log)
+{
+    struct rlimit  rlmt;
+
+    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, errno,
+                      "getrlimit(RLIMIT_NOFILE) failed)");
+        return NGX_ERROR;
+    }
+
+    ngx_log_error(NGX_LOG_INFO, log, 0,
+                  "getrlimit(RLIMIT_NOFILE): %d", rlmt.rlim_cur);
+
+    RLIM_INFINITY
+    max_connections =< rlmt.rlim_cur;
+
+    return NGX_OK;
+}