changeset 266:5238e93961a1

nginx-0.0.2-2004-02-23-23:57:12 import
author Igor Sysoev <igor@sysoev.ru>
date Mon, 23 Feb 2004 20:57:12 +0000
parents 6468241715e6
children 83205e0b5522
files auto/configure auto/lib/make auto/lib/md5/conf auto/options auto/sources auto/threads src/core/nginx.c src/core/nginx.h src/core/ngx_atomic.h src/core/ngx_config.h src/core/ngx_core.h src/core/ngx_log.c src/core/ngx_times.c src/event/ngx_event_accept.c src/event/ngx_event_timer.c src/http/ngx_http_cache.h src/os/unix/ngx_freebsd_rfork_thread.c src/os/unix/ngx_process.h src/os/unix/ngx_thread.h
diffstat 19 files changed, 658 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/auto/configure
+++ b/auto/configure
@@ -16,6 +16,10 @@ fi
 . auto/cc
 . auto/lib/conf
 
+if [ "$PLATFORM" != win32 ]; then
+    . auto/threads
+fi
+
 . auto/make
 . auto/lib/make
 
@@ -23,4 +27,6 @@ if [ "$PLATFORM" != win32 ]; then
     . auto/unix
 fi
 
+#have NGX_SMP . auto/have
+
 . auto/summary
--- a/auto/lib/make
+++ b/auto/lib/make
@@ -2,36 +2,60 @@
 if [ "$PLATFORM" != "win32" ]; then
 
     if [ $PCRE != NO ]; then
-        echo "$PCRE/.libs/libpcre.a:"                  >> $MAKEFILE
-        echo "	cd $PCRE \\"                           >> $MAKEFILE
-        echo "	&& ./configure --disable-shared \\"    >> $MAKEFILE
-        echo "	&& \$(MAKE)"                           >> $MAKEFILE
-        echo                                           >> $MAKEFILE
+        echo "$PCRE/.libs/libpcre.a:"                            >> $MAKEFILE
+        echo "	cd $PCRE \\"                                     >> $MAKEFILE
+        echo "	&& ./configure --disable-shared \\"              >> $MAKEFILE
+        echo "	&& \$(MAKE)"                                     >> $MAKEFILE
+        echo                                                     >> $MAKEFILE
     fi
 
 
     if [ $MD5 != NO ]; then
-        echo "$MD5/libmd5.a:"                          >> $MAKEFILE
+        echo "$MD5/libmd5.a:"                                    >> $MAKEFILE
 
         case $PLATFORM in
 
-        SunOS:*)
-            echo "	cd $MD5 && \$(MAKE) x86-solaris" >> $MAKEFILE
-        ;;
+            SunOS:*:i386)
+                echo "	cd $MD5 && \$(MAKE) x86-solaris"         >> $MAKEFILE
+            ;;
+
+            *:i386)
+                echo "	cd $MD5 && \$(MAKE) x86-elf"             >> $MAKEFILE
+            ;;
 
-        *)
-            echo "	cd $MD5 && \$(MAKE) x86-elf"   >> $MAKEFILE
-        ;;
+            *)
+                if [ $CC = gcc ]; then
+                    echo "	cd $MD5 && \$(MAKE) gcc"         >> $MAKEFILE
+                else
+                    echo "	cd $MD5 && \$(MAKE) cc"          >> $MAKEFILE
+                fi
+            ;;
+
         esac
 
-        echo                                           >> $MAKEFILE
+        echo                                                     >> $MAKEFILE
     fi
 
 
     if [ $ZLIB != NO ]; then
-        echo "$ZLIB/libz.a:"                           >> $MAKEFILE
-        echo "	cd $ZLIB && ./configure && \$(MAKE)"   >> $MAKEFILE
-        echo                                           >> $MAKEFILE
+        echo "$ZLIB/libz.a:"                                     >> $MAKEFILE
+
+        case $PLATFORM in
+
+            *:i386)
+                echo "	cd $ZLIB \\"                             >> $MAKEFILE
+                echo "	&& cp contrib/asm686/match.S . \\"       >> $MAKEFILE
+                echo "	&& CFLAGS=\"-O3 -DASMV\" ./configure \\" >> $MAKEFILE
+                echo "	&& \$(MAKE) OBJA=match.o"                >> $MAKEFILE
+            ;;
+
+            *)
+                echo "	cd $ZLIB && ./configure && \$(MAKE)"     >> $MAKEFILE
+            ;;
+
+        esac
+
+        echo                                                     >> $MAKEFILE
     fi
 
 fi
--- a/auto/lib/md5/conf
+++ b/auto/lib/md5/conf
@@ -27,11 +27,12 @@ ngx_lib_inc="#include <sys/types.h>
 
     MD5=NO
 
-        # Solaris 8/9
-        ngx_lib="rsaref md5"
-        ngx_lib_test="MD5_CTX md5; MD5Init(&md5)"
-        ngx_libs=-lmd5
-        . auto/lib/test
+    # Solaris 8/9
+
+    ngx_lib="rsaref md5"
+    ngx_lib_test="MD5_CTX md5; MD5Init(&md5)"
+    ngx_libs=-lmd5
+    . auto/lib/test
 
 
     if [ $ngx_found = yes ]; then
@@ -42,6 +43,7 @@ ngx_lib_inc="#include <sys/types.h>
 
     else
         # FreeBSD
+
         ngx_lib="rsaref md"
         ngx_lib_test="MD5_CTX md5; MD5Init(&md5)"
         ngx_libs=-lmd
@@ -56,8 +58,9 @@ ngx_lib_inc="#include <sys/types.h>
         ngx_found=no
 
     else
-        ngx_lib_inc="#include <sys/types.h>
-#include <openssl/md5.h>"
+        # OpenSSL crypto library
+
+        ngx_inc="#include <openssl/md5.h>"
         ngx_lib="OpenSSL md5 crypto"
         ngx_lib_test="MD5_CTX md5; MD5_Init(&md5)"
         ngx_libs=-lcrypto
--- a/auto/options
+++ b/auto/options
@@ -15,6 +15,8 @@ EVENT_SELECT=NO
 EVENT_POLL=NO
 EVENT_AIO=NO
 
+USE_THREADS=NO
+
 HTTP_REWRITE=YES
 HTTP_GZIP=YES
 HTTP_SSI=YES
@@ -51,6 +53,9 @@ do
         --without-poll_module)           EVENT_POLL=NONE            ;;
         --with-aio_module)               EVENT_AIO=YES              ;;
 
+        --with-threads=*)                USE_THREADS="$value"       ;;
+        --with-threads)                  USE_THREADS="pthreads"     ;;
+
         --without-http_rewrite_module)   HTTP_REWRITE=NO            ;;
         --without-http_ssi_module)       HTTP_SSI=NO                ;;
         --without-http_gzip_module)      HTTP_GZIP=NO               ;;
--- a/auto/sources
+++ b/auto/sources
@@ -5,6 +5,7 @@ CORE_INCS="-I src/core"
 
 CORE_DEPS="src/core/nginx.h \
             src/core/ngx_config.h \
+            src/core/ngx_core.h \
             src/core/ngx_atomic.h \
             src/core/ngx_log.h \
             src/core/ngx_alloc.h \
@@ -98,6 +99,7 @@ UNIX_DEPS="$CORE_DEPS $EVENT_DEPS \
             src/os/unix/ngx_errno.h \
             src/os/unix/ngx_files.h \
             src/os/unix/ngx_process.h \
+            src/os/unix/ngx_thread.h \
             src/os/unix/ngx_socket.h \
             src/os/unix/ngx_os.h"
 
@@ -117,7 +119,7 @@ UNIX_SRCS="$CORE_SRCS $EVENT_SRCS \
 FREEBSD_DEPS=src/os/unix/ngx_freebsd_config.h
 FREEBSD_SRCS=src/os/unix/ngx_freebsd_init.c
 FREEBSD_SENDFILE_SRCS=src/os/unix/ngx_freebsd_sendfile_chain.c
-
+FREEBSD_RFORK_SRCS="src/os/unix/ngx_freebsd_rfork_thread.c"
 
 LINUX_DEPS=src/os/unix/ngx_linux_config.h
 LINUX_SRCS=src/os/unix/ngx_linux_init.c
new file mode 100644
--- /dev/null
+++ b/auto/threads
@@ -0,0 +1,7 @@
+
+if [ $USE_THREADS = "rfork" ]; then
+    have=NGX_THREADS . auto/have
+    have=USE_RFORK . auto/have
+    CORE_DEPS="$CORE_DEPS $UNIX_THREADS_DEPS"
+    CORE_SRCS="$CORE_SRCS $FREEBSD_RFORK_SRCS"
+fi
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -27,6 +27,9 @@ typedef struct {
 static void ngx_master_process_cycle(ngx_cycle_t *cycle, ngx_master_ctx_t *ctx);
 static void ngx_master_exit(ngx_cycle_t *cycle, ngx_master_ctx_t *ctx);
 static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data);
+#if (NGX_THREADS)
+static int ngx_worker_thread_cycle(void *data);
+#endif
 static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle, char **envp);
 static ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv);
 static ngx_int_t ngx_getopt(ngx_master_ctx_t *ctx, ngx_cycle_t *cycle);
@@ -87,22 +90,22 @@ ngx_module_t  ngx_core_module = {
 };
 
 
-ngx_int_t   ngx_max_module;
-ngx_uint_t  ngx_connection_counter;
+ngx_int_t     ngx_max_module;
+ngx_atomic_t  ngx_connection_counter;
 
-ngx_int_t   ngx_process;
-ngx_pid_t   ngx_pid;
-ngx_pid_t   ngx_new_binary;
+ngx_int_t     ngx_process;
+ngx_pid_t     ngx_pid;
+ngx_pid_t     ngx_new_binary;
 
-ngx_int_t   ngx_inherited;
-ngx_int_t   ngx_reap;
-ngx_int_t   ngx_timer;
-ngx_int_t   ngx_terminate;
-ngx_int_t   ngx_quit;
-ngx_int_t   ngx_noaccept;
-ngx_int_t   ngx_reconfigure;
-ngx_int_t   ngx_reopen;
-ngx_int_t   ngx_change_binary;
+ngx_int_t     ngx_inherited;
+ngx_int_t     ngx_reap;
+ngx_int_t     ngx_timer;
+ngx_int_t     ngx_terminate;
+ngx_int_t     ngx_quit;
+ngx_int_t     ngx_noaccept;
+ngx_int_t     ngx_reconfigure;
+ngx_int_t     ngx_reopen;
+ngx_int_t     ngx_change_binary;
 
 
 int main(int argc, char *const *argv, char **envp)
@@ -577,6 +580,9 @@ static void ngx_worker_process_cycle(ngx
     ngx_int_t         i;
     ngx_listening_t  *ls;
     ngx_core_conf_t  *ccf;
+#if (NGX_THREADS)
+    ngx_tid_t         tid;
+#endif
 
     ngx_process = NGX_PROCESS_WORKER;
     ngx_last_process = 0;
@@ -641,7 +647,15 @@ static void ngx_worker_process_cycle(ngx
 
     ngx_setproctitle("worker process");
 
-    /* TODO: threads: start ngx_worker_thread_cycle() */
+#if (NGX_THREADS)
+
+    ngx_init_threads(5, 128 * 1024 * 1024, cycle->log);
+
+    for (i = 0; i < 1; i++) {
+        ngx_create_thread(&tid, ngx_worker_thread_cycle, cycle, cycle->log);
+    }
+
+#endif
 
     for ( ;; ) {
         ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
@@ -688,6 +702,35 @@ static void ngx_worker_process_cycle(ngx
 }
 
 
+#if (NGX_THREADS)
+
+int ngx_worker_thread_cycle(void *data)
+{
+    ngx_cycle_t *cycle = data;
+
+    struct timeval  tv;
+
+    /* STUB */
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, ngx_errno,
+                   "thread %d started", ngx_thread_self());
+
+    ngx_setproctitle("worker thread");
+
+    sleep(5);
+
+    ngx_gettimeofday(&tv);
+    ngx_time_update(tv.tv_sec);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, ngx_errno,
+                   "thread %d done", ngx_thread_self());
+
+    return 1;
+}
+
+#endif
+
+
 static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle, char **envp)
 {
     char                *p, *v;
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,7 +13,7 @@
 
 extern ngx_module_t        ngx_core_module;
 
-extern ngx_uint_t          ngx_connection_counter;
+extern ngx_atomic_t        ngx_connection_counter;
 
 extern ngx_int_t           ngx_process;
 
--- a/src/core/ngx_atomic.h
+++ b/src/core/ngx_atomic.h
@@ -6,9 +6,78 @@
 #include <ngx_core.h>
 
 
+#ifdef __i386__
+
+typedef uint32_t  ngx_atomic_t;
+
+#if (NGX_SMP)
+#define NGX_SMP_LOCK  "lock"
+#else
+#define NGX_SMP_LOCK
+#endif
+
+
+static ngx_inline uint32_t ngx_atomic_inc(ngx_atomic_t *value)
+{
+    uint32_t  old;
+
+    __asm__ __volatile ("
+
+        movl   $1, %0
+    "   NGX_SMP_LOCK
+    "   xaddl  %0, %1
+
+    ": "=a" (old) : "m" (*value));
+
+    return old;
+}
+
+
+static ngx_inline uint32_t ngx_atomic_dec(ngx_atomic_t *value)
+{
+    uint32_t  old;
+
+    __asm__ __volatile ("
+
+        movl   $-1, %0
+    "   NGX_SMP_LOCK
+    "   xaddl  %0, %1
+
+    ": "=a" (old) : "m" (*value));
+
+    return old;
+}
+
+
+static ngx_inline uint32_t ngx_atomic_cmp_set(ngx_atomic_t *lock,
+                                              ngx_atomic_t old,
+                                              ngx_atomic_t set)
+{
+    uint32_t  res;
+
+    __asm__ __volatile ("
+
+    "   NGX_SMP_LOCK
+    "   cmpxchgl  %3, %1
+        setzb     %%al
+        movzbl    %%al, %0
+
+    ": "=a" (res) : "m" (*lock), "a" (old), "q" (set));
+
+    return res;
+}
+
+#else
+
+typedef uint32_t  ngx_atomic_t;
+
 /* STUB */
 #define ngx_atomic_inc(x)   x++;
 #define ngx_atomic_dec(x)   x--;
+#define ngx_atomic_cmp_set(lock, old, set)   1;
+/**/
+
+#endif
 
 
 #endif /* _NGX_ATOMIC_H_INCLUDED_ */
--- a/src/core/ngx_config.h
+++ b/src/core/ngx_config.h
@@ -38,11 +38,6 @@
 #endif
 
 
-/* STUB: ngx_mutex.h */
-#define ngx_mutex_lock(m)
-#define ngx_mutex_unlock(m)
-
-
 /* STUB: autoconf */
 typedef int    ngx_int_t;
 typedef u_int  ngx_uint_t;
@@ -71,7 +66,7 @@ typedef int    ngx_flag_t;
 /* TODO: #ifndef */
 #define NGX_SHUTDOWN_SIGNAL      QUIT
 #define NGX_TERMINATE_SIGNAL     TERM
-#define NGX_NOACCEPT_SIGNAL      ABRT
+#define NGX_NOACCEPT_SIGNAL      WINCH
 #define NGX_RECONFIGURE_SIGNAL   HUP
 #define NGX_REOPEN_SIGNAL        USR1
 #define NGX_CHANGEBIN_SIGNAL     USR2
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -20,6 +20,7 @@ typedef struct ngx_connection_s  ngx_con
 #include <ngx_socket.h>
 #include <ngx_errno.h>
 #include <ngx_process.h>
+#include <ngx_thread.h>
 #include <ngx_string.h>
 #include <ngx_parse.h>
 #include <ngx_log.h>
--- a/src/core/ngx_log.c
+++ b/src/core/ngx_log.c
@@ -79,7 +79,7 @@ void ngx_log_error_core(int level, ngx_l
 
     /* pid#tid */
     len += ngx_snprintf(errstr + len, max - len,
-                        PID_T_FMT "#%d: ", ngx_pid, /* STUB */ 0);
+                        PID_T_FMT "#" TID_T_FMT ": ", ngx_log_pid, ngx_log_tid);
 
     if (log->data && *(int *) log->data != -1) {
         len += ngx_snprintf(errstr + len, max - len,
--- a/src/core/ngx_times.c
+++ b/src/core/ngx_times.c
@@ -3,6 +3,11 @@
 #include <ngx_core.h>
 
 
+#if (NGX_THREADS)
+static ngx_mutex_t  *ngx_time_mutex;
+#endif
+
+
 time_t            ngx_cached_time;
 ngx_epoch_msec_t  ngx_elapsed_msec;
 ngx_epoch_msec_t  ngx_old_elapsed_msec;
@@ -46,6 +51,13 @@ void ngx_time_init()
     ngx_elapsed_msec = 0;
 
     ngx_time_update(tv.tv_sec);
+
+#if (NGX_THREADS0)
+    if (!(ngx_time_mutex = ngx_mutex_init(log, NGX_MUTEX_LIGHT);
+        return 0;
+    }
+#endif
+
 }
 
 
@@ -57,6 +69,12 @@ void ngx_time_update(time_t s)
         return;
     }
 
+#if (NGX_THREADS0)
+    if (ngx_mutex_trylock(ngx_time_mutex) != NGX_OK) {
+        return;
+    }
+#endif
+
     ngx_cached_time = s;
 
     ngx_gmtime(ngx_cached_time, &ngx_cached_gmtime);
@@ -90,6 +108,11 @@ void ngx_time_update(time_t s)
                                        tm.ngx_tm_hour,
                                        tm.ngx_tm_min,
                                        tm.ngx_tm_sec);
+
+#if (NGX_THREADS0)
+    ngx_mutex_unlock(ngx_time_mutex);
+#endif
+
 }
 
 
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -254,17 +254,17 @@ void ngx_event_accept(ngx_event_t *ev)
 
         /*
          * TODO: MT: - atomic increment (x86: lock xadd)
-         *             or protection by critical section or mutex
+         *             or protection by critical section or light mutex
          *
          * TODO: MP: - allocated in a shared memory
          *           - atomic increment (x86: lock xadd)
-         *             or protection by critical section or mutex
+         *             or protection by critical section or light mutex
          */
 
-        c->number = ngx_connection_counter++;
+        c->number = ngx_atomic_inc(&ngx_connection_counter);
 
         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
-                       "accept: %d, %d", s, c->number);
+                       "accept: fd:%d c:%d", s, c->number);
 
         if (ngx_add_conn) {
             if (ngx_add_conn(c) == NGX_ERROR) {
@@ -292,8 +292,6 @@ void ngx_event_accept(ngx_event_t *ev)
         accepted++;
 
     } while (ev->available);
-
-    return;
 }
 
 
--- a/src/event/ngx_event_timer.c
+++ b/src/event/ngx_event_timer.c
@@ -9,6 +9,10 @@
  * protected by the single mutex
  */
 
+#if (NGX_THREADS)
+static ngx_mutex_t  *ngx_event_timer_mutex;
+#endif
+
 
 ngx_rbtree_t  *ngx_event_timer_rbtree;
 ngx_rbtree_t   ngx_event_timer_sentinel;
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -61,6 +61,9 @@ typedef struct {
     size_t                    nelts;
     time_t                    life;
     time_t                    update;
+#if (NGX_THREADS)
+    ngx_mutex_t               mutex;
+#endif
     ngx_pool_t               *pool;
 } ngx_http_cache_hash_t;
 
--- a/src/os/unix/ngx_freebsd_rfork_thread.c
+++ b/src/os/unix/ngx_freebsd_rfork_thread.c
@@ -2,31 +2,44 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 
-
-extern int   __isthreaded;
+/*
+ * The threads implementation uses the rfork(RFPROC|RFTHREAD|RFMEM)
+ * to create threads.  All threads use the stacks of the same size mmap()ed
+ * below the main stack.  Thus the stack pointer is used to determine
+ * the current thread id.
+ *
+ * The mutex implementation uses the ngx_atomic_cmp_set() operation
+ * to acquire mutex and the SysV semaphore to wait on a mutex or to wake up
+ * the waiting threads.
+ *
+ * The condition variable implementation uses the SysV semaphore set of two
+ * semaphores. The first is used by the CV mutex, and the second is used
+ * by CV itself.
+ */
 
 
-typedef int  ngx_tid_t;
+extern int  __isthreaded;
 
 
 static inline int ngx_gettid();
 
 
 static char        *usrstack;
-static int          red_zone = 4096;
+static size_t       rz_size = /* STUB: PAGE_SIZE */ 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;
+static ngx_uint_t   nthreads;
+static ngx_uint_t   max_threads;
+static ngx_tid_t   *tids;  /* the threads tids array */
+
 
 /* the thread-safe errno */
 
 static int   errno0;   /* the main thread's errno */
-static int  *errnos;
+static int  *errnos;   /* the threads errno's array */
 
 int *__error()
 {
@@ -41,20 +54,22 @@ int *__error()
 int ngx_create_thread(ngx_tid_t *tid, int (*func)(void *arg), void *arg,
                       ngx_log_t *log)
 {
-    int         id, err;
-    char       *stack, *stack_top;
+    int    id, err;
+    char  *stack, *stack_top;
 
-    if (threads >= nthreads) {
+    if (nthreads >= max_threads) {
         ngx_log_error(NGX_LOG_CRIT, log, 0,
-                      "no more than %d threads can be created", nthreads);
+                      "no more than %d threads can be created", max_threads);
         return NGX_ERROR;
     }
 
     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,
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                       "mmap(%08X:%d, MAP_STACK) thread stack failed",
                       last_stack, usable_stack_size);
         return NGX_ERROR;
@@ -66,7 +81,8 @@ int ngx_create_thread(ngx_tid_t *tid, in
 
     stack_top = stack + usable_stack_size;
 
-printf("stack: %08X-%08X\n", stack, stack_top);
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0,
+                   "thread stack: %08X-%08X", stack, stack_top);
 
 #if 1
     id = rfork_thread(RFPROC|RFTHREAD|RFMEM, stack_top, func, arg);
@@ -78,92 +94,81 @@ printf("stack: %08X-%08X\n", stack, stac
     id = rfork(RFFDG|RFCFDG);
 #endif
 
-    err = errno;
+    err = ngx_errno;
 
     if (id == -1) {
         ngx_log_error(NGX_LOG_ALERT, log, err, "rfork() failed");
 
     } else {
         *tid = id;
-        threads = (usrstack - stack_top) / stack_size;
-        tids[threads] = id;
+        nthreads = (usrstack - stack_top) / stack_size;
+        tids[nthreads] = id;
 
-        /* allow the spinlock in libc malloc() */
-        __isthreaded = 1;
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "rfork()ed thread: %d", id);
     }
 
     return err;
 }
 
 
-int ngx_init_thread_env(int n, size_t size, ngx_log_t *log)
+ngx_int_t ngx_init_threads(int n, size_t size, ngx_log_t *log)
 {
     int    len;
-    char  *rz, *zone;
+    char  *red_zone, *zone;
 
-    nthreads = n;
+    max_threads = n;
 
     len = 4;
     if (sysctlbyname("kern.usrstack", &usrstack, &len, NULL, 0) == -1) {
-        ngx_log_error(NGX_LOG_ALERT, log, errno,
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                       "sysctlbyname(kern.usrstack) failed");
         return NGX_ERROR;
     }
 
-printf("usrstack: %08X\n", usrstack);
+    /* the main thread stack red zone */
+    red_zone = usrstack - (size + rz_size);
 
-    /* red zone */
-    rz = usrstack - (size + red_zone);
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0,
+                   "usrstack: %08X, red zone: %08X", usrstack, red_zone);
 
-printf("red zone: %08X\n", rz);
-
-    zone = mmap(rz, red_zone, PROT_NONE, MAP_ANON, -1, 0);
+    zone = mmap(red_zone, rz_size, PROT_NONE, MAP_ANON, -1, 0);
     if (zone == MAP_FAILED) {
-        ngx_log_error(NGX_LOG_ALERT, log, errno,
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                       "mmap(%08X:%d, PROT_NONE, MAP_ANON) red zone failed",
-                      rz, red_zone);
+                      red_zone, rz_size);
         return NGX_ERROR;
     }
 
-    if (zone != rz) {
+    if (zone != red_zone) {
         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 threads errno array */
 
-    /* create the thread tid array */
-    ngx_test_null(tids, ngx_calloc((n + 1) * sizeof(ngx_tid_t), log),
-                  NGX_ERROR);
+    if (!(errnos = ngx_calloc(n * sizeof(int), log))) {
+        return NGX_ERROR;
+    }
+
+    /* create the threads tid array */
 
-    tids[0] = ngx_getpid();
-    threads = 1;
+    if (!(tids = ngx_calloc((n + 1) * sizeof(ngx_tid_t), log))) {
+        return NGX_ERROR;
+    }
+
+    tids[0] = ngx_pid;
+    nthreads = 1;
 
-    last_stack = zone + red_zone;
+    last_stack = zone + rz_size;
     usable_stack_size = size;
-    stack_size = size + red_zone;
+    stack_size = size + rz_size;
+
+    /* allow the spinlock in libc malloc() */
+    __isthreaded = 1;
 
     return NGX_OK;
 }
 
 
-ngx_tid_t ngx_thread_self()
-{
-    int        tid;
-    ngx_tid_t  pid;
-
-    tid = ngx_gettid();
-
-    if (tids[tid] == 0) {
-        pid = ngx_getpid();
-        tids[tid] = pid;
-        return pid;
-    }
-
-    return tids[tid];
-}
-
-
 static inline int ngx_gettid()
 {
     char  *sp;
@@ -176,3 +181,286 @@ static inline int ngx_gettid()
 
     return (usrstack - sp) / stack_size;
 }
+
+
+ngx_tid_t ngx_thread_self()
+{
+    int        tid;
+    ngx_tid_t  pid;
+
+    tid = ngx_gettid();
+
+    if (tids == NULL) {
+        return ngx_pid;
+    }
+
+#if 0
+    if (tids[tid] == 0) {
+        pid = ngx_pid;
+        tids[tid] = pid;
+        return pid;
+    }
+#endif
+
+    return tids[tid];
+}
+
+
+ngx_mutex_t *ngx_mutex_init(ngx_log_t *log, uint flags)
+{
+    int           nsem, i;
+    ngx_mutex_t  *m;
+    union semun   op;
+
+    if (!(m = ngx_alloc(sizeof(ngx_mutex_t), log))) {
+        return NULL;
+    }
+
+    m->lock = 0;
+    m->log = log;
+
+    if (flags & NGX_MUTEX_LIGHT) {
+        m->semid = -1;
+        return m;
+    }
+
+    nsem = flags & NGX_MUTEX_CV ? 2 : 1;
+
+    m->semid = semget(IPC_PRIVATE, nsem, SEM_R|SEM_A);
+    if (m->semid == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "semget() failed");
+        return NULL;
+    }
+
+    op.val = 0;
+    for (i = 0; i < nsem; i++) {
+        if (semctl(m->semid, i, SETVAL, op) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+                          "semctl(SETVAL) failed");
+
+            if (semctl(m->semid, 0, IPC_RMID) == -1) {
+                ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,
+                              "semctl(IPC_RMID) failed");
+            }
+
+            return NULL;
+        }
+    }
+
+    return m;
+}
+
+
+void ngx_mutex_done(ngx_mutex_t *m)
+{
+    if (semctl(m->semid, 0, IPC_RMID) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,
+                      "semctl(IPC_RMID) failed");
+    }
+
+    ngx_free(m);
+}
+
+
+ngx_int_t ngx_mutex_do_lock(ngx_mutex_t *m, ngx_int_t try)
+{
+    uint32_t       lock, new, old;
+    ngx_uint_t     tries;
+    struct sembuf  op;
+
+#if (NGX_DEBUG)
+    if (try) {
+        ngx_log_debug2(NGX_LOG_DEBUG_CORE, m->log, 0,
+                       "try lock mutex %08X lock:%X", m, m->lock);
+    } else {
+        ngx_log_debug2(NGX_LOG_DEBUG_CORE, m->log, 0,
+                       "lock mutex %08X lock:%X", m, m->lock);
+    }
+#endif
+
+    old = m->lock;
+    tries = 0;
+
+    for ( ;; ) {
+        if (old & NGX_MUTEX_LOCK_BUSY) {
+
+            if (try) {
+                return NGX_AGAIN;
+            }
+
+            if (ngx_freebsd_hw_ncpu > 1 && tries++ < 1000) {
+
+                /* the spinlock is used only on the SMP system */
+
+                old = m->lock;
+                continue;
+            }
+
+            if (m->semid == -1) {
+                sched_yield();
+
+                tries = 0;
+                old = m->lock;
+                continue;
+            }
+
+            ngx_log_debug2(NGX_LOG_DEBUG_CORE, m->log, 0,
+                           "mutex %08X lock:%X", m, m->lock);
+
+            /*
+             * The mutex is locked so we increase a number
+             * of the threads that are waiting on the mutex
+             */
+
+            lock = old + 1;
+
+            if ((lock & ~NGX_MUTEX_LOCK_BUSY) > nthreads) {
+                ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,
+                              "%d threads wait for mutex %0X, "
+                              "while only %d threads are available",
+                              lock & ~NGX_MUTEX_LOCK_BUSY, m, nthreads);
+                return NGX_ERROR;
+            }
+
+            if (ngx_atomic_cmp_set(&m->lock, old, lock)) {
+
+                ngx_log_debug2(NGX_LOG_DEBUG_CORE, m->log, 0,
+                               "wait mutex %08X lock:%X", m, m->lock);
+
+                /*
+                 * The number of the waiting threads has been increased
+                 * and we would wait on the SysV semaphore.
+                 * A semaphore should wake up us more efficiently than
+                 * a simple usleep().
+                 */
+
+                op.sem_num = 0;
+                op.sem_op = -1;
+                op.sem_flg = SEM_UNDO;
+
+                if (semop(m->semid, &op, 1) == -1) {
+                    ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,
+                                  "semop() failed while waiting "
+                                  "on mutex %08X", m);
+                    return NGX_ERROR;
+                }
+
+                tries = 0;
+                old = m->lock;
+                continue;
+            }
+
+            old = m->lock;
+
+        } else {
+            lock = old | NGX_MUTEX_LOCK_BUSY;
+
+            if (ngx_atomic_cmp_set(&m->lock, old, lock)) {
+
+                /* we locked the mutex */
+
+                break;
+            }
+
+            old = m->lock;
+        }
+
+        if (tries++ > 1000) {
+
+            ngx_log_debug1(NGX_LOG_DEBUG_CORE, m->log, 0,
+                           "mutex %08X is contested", m);
+
+            /* the mutex is probably contested so we are giving up now */
+
+            sched_yield();
+
+            tries = 0;
+            old = m->lock;
+        }
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, m->log, 0,
+                   "mutex %08X is locked, lock:%X", m, m->lock);
+
+    return NGX_OK;
+}
+
+
+ngx_int_t ngx_mutex_unlock(ngx_mutex_t *m)
+{
+    uint32_t       lock, new, old;
+    struct sembuf  op;
+
+    old = m->lock;
+
+    if (!(old & NGX_MUTEX_LOCK_BUSY)) {
+        ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,
+                      "tring to unlock the free mutex %0X", m);
+        return NGX_ERROR;
+    }
+
+    /* free the mutex */
+
+    for ( ;; ) {
+        lock = old & ~NGX_MUTEX_LOCK_BUSY;
+
+        if (ngx_atomic_cmp_set(&m->lock, old, lock)) {
+            break;
+        }
+
+        old = m->lock;
+    }
+
+    if (m->semid == -1) {
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, m->log, 0,
+                       "mutex %08X is unlocked", m);
+
+        return NGX_OK;
+    }
+
+    /* check weather we need to wake up a waiting thread */
+
+    old = m->lock;
+
+    for ( ;; ) {
+        if (old & NGX_MUTEX_LOCK_BUSY) {
+
+            /* the mutex is just locked by another thread */
+
+            break;
+        }
+
+        if (old == 0) {
+            break;
+        }
+
+        /* there are the waiting threads */
+
+        lock = old - 1;
+
+        if (ngx_atomic_cmp_set(&m->lock, old, lock)) {
+
+            /* wake up the thread that waits on semaphore */
+
+            op.sem_num = 0;
+            op.sem_op = 1;
+            op.sem_flg = SEM_UNDO;
+
+            if (semop(m->semid, &op, 1) == -1) {
+                ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,
+                              "semop() failed while waking up on mutex %08X",
+                              m);
+                return NGX_ERROR;
+            }
+
+            break;
+        }
+
+        old = m->lock;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, m->log, 0,
+                   "mutex %08X is unlocked", m);
+
+    return NGX_OK;
+}
--- a/src/os/unix/ngx_process.h
+++ b/src/os/unix/ngx_process.h
@@ -40,7 +40,8 @@ typedef struct {
 #define NGX_PROCESS_DETACHED   -3
 
 
-#define ngx_getpid  getpid
+#define ngx_getpid   getpid
+#define ngx_log_pid  ngx_pid
 
 ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle,
                             ngx_spawn_proc_pt proc, void *data,
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_thread.h
@@ -0,0 +1,74 @@
+#ifndef _NGX_THREAD_H_INCLUDED_
+#define _NGX_THREAD_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+#if (NGX_THREADS)
+
+#if (USE_RFORK)
+
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sched.h>
+
+typedef pid_t  ngx_tid_t;
+
+#define TID_T_FMT      PID_T_FMT
+
+#define ngx_log_tid    0
+
+#undef ngx_log_pid
+#define ngx_log_pid    ngx_thread_self()
+
+
+#define NGX_MUTEX_LIGHT      1
+#define NGX_MUTEX_CV         2
+
+#define NGX_MUTEX_LOCK_BUSY  0x80000000
+
+typedef struct {
+    ngx_atomic_t  lock;
+    ngx_log_t    *log;
+    int           semid;
+} ngx_mutex_t;
+
+
+#else /* use pthreads */
+
+#include <pthread.h>
+
+typedef pthread_t  ngx_tid_t;
+
+#define ngx_log_tid    ngx_thread_self()
+
+#endif
+
+
+ngx_int_t ngx_init_threads(int n, size_t size, ngx_log_t *log);
+int ngx_create_thread(ngx_tid_t *tid, int (*func)(void *arg), void *arg,
+                      ngx_log_t *log);
+ngx_tid_t ngx_thread_self();
+
+ngx_mutex_t *ngx_mutex_init(ngx_log_t *log, uint flags);
+void ngx_mutex_done(ngx_mutex_t *m);
+
+#define ngx_mutex_trylock(m)  ngx_mutex_do_lock(m, 1)
+#define ngx_mutex_lock(m)     ngx_mutex_do_lock(m, 0)
+ngx_int_t ngx_mutex_do_lock(ngx_mutex_t *m, ngx_int_t try);
+ngx_int_t ngx_mutex_unlock(ngx_mutex_t *m);
+
+
+#else /* !NGX_THREADS */
+
+#define ngx_log_tid  0
+#define TID_T_FMT    "%d"
+
+#define ngx_mutex_lock(m)
+#define ngx_mutex_unlock(m)
+
+#endif
+
+
+#endif /* _NGX_THREAD_H_INCLUDED_ */