POSIX semaphores patch

Igor Sysoev igor at sysoev.ru
Wed May 4 15:08:17 MSD 2011


The attache patch enables POSIX semaphores usage during shared memory access.
It should decrease CPU usage on start-up or online upgrade of installations
with large caches or large number of workers.

I tested the patch on FreeBSD 8.2. There is no sense to test it on
FreeBSD prior to 7.2 version, since semaphores does not work on these systems.
You can ensure that there are enough semaphores:
   sysctl p1003_1b.sem_nsems_max
By default there should be 32 semaphores. On 8.2+ you can increase them
dynamically. You need one semaphores per shared zone.
"sysctl p1003_1b.nsems" shows current number of semaphores.

It would be intresting to test the patch on other platforms - Linux
and Solaris. Please draw attention to alerts in error_log.
Note also that nginx does not destroy semaphores on exit. They should
deleted by kernel automatically. Please check that there is no
semaphore leakage while nginx work.


-- 
Igor Sysoev
-------------- next part --------------
Index: src/event/ngx_event.c
===================================================================
--- src/event/ngx_event.c	(revision 3907)
+++ src/event/ngx_event.c	(working copy)
@@ -519,6 +519,7 @@
     shared = shm.addr;
 
     ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
+    ngx_accept_mutex.spin = (ngx_uint_t) -1;
 
     if (ngx_shmtx_create(&ngx_accept_mutex, shared, cycle->lock_file.data)
         != NGX_OK)
Index: src/os/unix/ngx_posix_config.h
===================================================================
--- src/os/unix/ngx_posix_config.h	(revision 3907)
+++ src/os/unix/ngx_posix_config.h	(working copy)
@@ -96,6 +96,11 @@
 #include <ngx_auto_config.h>
 
 
+#if (NGX_HAVE_POSIX_SEM)
+#include <semaphore.h>
+#endif
+
+
 #if (NGX_HAVE_POLL)
 #include <poll.h>
 #endif
Index: src/os/unix/ngx_linux_config.h
===================================================================
--- src/os/unix/ngx_linux_config.h	(revision 3907)
+++ src/os/unix/ngx_linux_config.h	(working copy)
@@ -58,6 +58,11 @@
 #include <ngx_auto_config.h>
 
 
+#if (NGX_HAVE_POSIX_SEM)
+#include <semaphore.h>
+#endif
+
+
 #if (NGX_HAVE_SYS_PRCTL_H)
 #include <sys/prctl.h>
 #endif
Index: src/os/unix/ngx_freebsd_config.h
===================================================================
--- src/os/unix/ngx_freebsd_config.h	(revision 3907)
+++ src/os/unix/ngx_freebsd_config.h	(working copy)
@@ -68,6 +68,11 @@
 #include <ngx_auto_config.h>
 
 
+#if (NGX_HAVE_POSIX_SEM)
+#include <semaphore.h>
+#endif
+
+
 #if (NGX_HAVE_POLL)
 #include <poll.h>
 #endif
Index: src/os/unix/ngx_solaris_config.h
===================================================================
--- src/os/unix/ngx_solaris_config.h	(revision 3907)
+++ src/os/unix/ngx_solaris_config.h	(working copy)
@@ -57,6 +57,11 @@
 #include <ngx_auto_config.h>
 
 
+#if (NGX_HAVE_POSIX_SEM)
+#include <semaphore.h>
+#endif
+
+
 #if (NGX_HAVE_POLL)
 #include <poll.h>
 #endif
Index: src/os/unix/ngx_darwin_config.h
===================================================================
--- src/os/unix/ngx_darwin_config.h	(revision 3907)
+++ src/os/unix/ngx_darwin_config.h	(working copy)
@@ -56,6 +56,11 @@
 #include <ngx_auto_config.h>
 
 
+#if (NGX_HAVE_POSIX_SEM)
+#include <semaphore.h>
+#endif
+
+
 #if (NGX_HAVE_POLL)
 #include <poll.h>
 #endif
Index: src/core/ngx_shmtx.c
===================================================================
--- src/core/ngx_shmtx.c	(revision 3907)
+++ src/core/ngx_shmtx.c	(working copy)
@@ -16,9 +16,160 @@
 {
     mtx->lock = addr;
 
+    if (mtx->spin == (ngx_uint_t) -1) {
+        return NGX_OK;
+    }
+
+    mtx->spin = 2048;
+
+#if (NGX_HAVE_POSIX_SEM)
+
+    if (sem_init(&mtx->sem, 1, 0) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
+                      "sem_init() failed");
+    } else {
+        mtx->semaphore = 1;
+    }
+
+#endif
+
     return NGX_OK;
 }
 
+
+void
+ngx_shmtx_destory(ngx_shmtx_t *mtx)
+{
+#if (NGX_HAVE_POSIX_SEM)
+
+    if (mtx->semaphore) {
+        if (sem_destroy(&mtx->sem) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
+                          "sem_destroy() failed");
+        }
+    }
+
+#endif
+}
+
+
+ngx_uint_t
+ngx_shmtx_trylock(ngx_shmtx_t *mtx)
+{
+    ngx_atomic_uint_t  val;
+
+    val = *mtx->lock;
+
+    return ((val & 0x80000000) == 0
+            && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000));
+}
+
+
+void
+ngx_shmtx_lock(ngx_shmtx_t *mtx)
+{
+    ngx_uint_t         i, n;
+    ngx_atomic_uint_t  val;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx lock");
+
+    for ( ;; ) {
+
+        val = *mtx->lock;
+
+        if ((val & 0x80000000) == 0
+            && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000))
+        {
+            return;
+        }
+
+        if (ngx_ncpu > 1) {
+
+            for (n = 1; n < mtx->spin; n <<= 1) {
+
+                for (i = 0; i < n; i++) {
+                    ngx_cpu_pause();
+                }
+
+                val = *mtx->lock;
+
+                if ((val & 0x80000000) == 0
+                    && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000))
+                {
+                    return;
+                }
+            }
+        }
+
+#if (NGX_HAVE_POSIX_SEM)
+
+        if (mtx->semaphore) {
+            val = *mtx->lock;
+
+            if ((val & 0x80000000)
+                && ngx_atomic_cmp_set(mtx->lock, val, val + 1))
+            {
+                ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+                               "shmtx wait %XA", val);
+
+                if (sem_wait(&mtx->sem) == -1) {
+                    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
+                                  "sem_wait() failed while waiting on shmtx");
+                }
+
+                ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+                               "shmtx awoke");
+
+                continue;
+            }
+        }
+
+#endif
+
+        ngx_sched_yield();
+    }
+}
+
+
+void
+ngx_shmtx_unlock(ngx_shmtx_t *mtx)
+{
+    ngx_atomic_uint_t  val, old, wait;
+
+    if (mtx->spin != (ngx_uint_t) -1) {
+        ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx unlock");
+    }
+
+    for ( ;; ) {
+
+        old = *mtx->lock;
+        wait = old & 0x7fffffff;
+        val = wait ? wait - 1 : 0;
+
+        if (ngx_atomic_cmp_set(mtx->lock, old, val)) {
+            break;
+        }
+    }
+
+#if (NGX_HAVE_POSIX_SEM)
+    {
+
+    if (wait == 0 || !mtx->semaphore) {
+        return;
+    }
+
+    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+                   "shmtx wake %XA", old);
+
+    if (sem_post(&mtx->sem) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
+                      "sem_post() failed while wake shmtx");
+    }
+    }
+#endif
+}
+
+
 #else
 
 
@@ -65,4 +216,62 @@
 }
 
 
+ngx_uint_t
+ngx_shmtx_trylock(ngx_shmtx_t *mtx)
+{
+    ngx_err_t  err;
+
+    err = ngx_trylock_fd(mtx->fd);
+
+    if (err == 0) {
+        return 1;
+    }
+
+    if (err == NGX_EAGAIN) {
+        return 0;
+    }
+
+#if __osf__ /* Tru64 UNIX */
+
+    if (err == NGX_EACCESS) {
+        return 0;
+    }
+
 #endif
+
+    ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name);
+
+    return 0;
+}
+
+
+void
+ngx_shmtx_lock(ngx_shmtx_t *mtx)
+{
+    ngx_err_t  err;
+
+    err = ngx_lock_fd(mtx->fd);
+
+    if (err == 0) {
+        return;
+    }
+
+    ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name);
+}
+
+
+void
+ngx_shmtx_unlock(ngx_shmtx_t *mtx)
+{
+    ngx_err_t  err;
+
+    err = ngx_unlock_fd(mtx->fd);
+
+    if (err == 0) {
+        return;
+    }
+
+    ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name);
+}
+
+#endif
Index: src/core/ngx_shmtx.h
===================================================================
--- src/core/ngx_shmtx.h	(revision 3907)
+++ src/core/ngx_shmtx.h	(working copy)
@@ -15,95 +15,23 @@
 typedef struct {
 #if (NGX_HAVE_ATOMIC_OPS)
     ngx_atomic_t  *lock;
+#if (NGX_HAVE_POSIX_SEM)
+    ngx_uint_t     semaphore;
+    sem_t          sem;
+#endif
 #else
     ngx_fd_t       fd;
     u_char        *name;
 #endif
+    ngx_uint_t     spin;
 } ngx_shmtx_t;
 
 
 ngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name);
-
-
-#if (NGX_HAVE_ATOMIC_OPS)
-
-static ngx_inline ngx_uint_t
-ngx_shmtx_trylock(ngx_shmtx_t *mtx)
-{
-    return (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid));
-}
-
-#define ngx_shmtx_lock(mtx)   ngx_spinlock((mtx)->lock, ngx_pid, 1024)
-
-#define ngx_shmtx_unlock(mtx) (void) ngx_atomic_cmp_set((mtx)->lock, ngx_pid, 0)
-
-#define ngx_shmtx_destory(mtx)
-
-
-#else
-
-static ngx_inline ngx_uint_t
-ngx_shmtx_trylock(ngx_shmtx_t *mtx)
-{
-    ngx_err_t  err;
-
-    err = ngx_trylock_fd(mtx->fd);
-
-    if (err == 0) {
-        return 1;
-    }
-
-    if (err == NGX_EAGAIN) {
-        return 0;
-    }
-
-#if __osf__ /* Tru64 UNIX */
-
-    if (err == NGX_EACCESS) {
-        return 0;
-    }
-
-#endif
-
-    ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name);
-
-    return 0;
-}
-
-
-static ngx_inline void
-ngx_shmtx_lock(ngx_shmtx_t *mtx)
-{
-    ngx_err_t  err;
-
-    err = ngx_lock_fd(mtx->fd);
-
-    if (err == 0) {
-        return;
-    }
-
-    ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name);
-}
-
-
-static ngx_inline void
-ngx_shmtx_unlock(ngx_shmtx_t *mtx)
-{
-    ngx_err_t  err;
-
-    err = ngx_unlock_fd(mtx->fd);
-
-    if (err == 0) {
-        return;
-    }
-
-    ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name);
-}
-
-
 void ngx_shmtx_destory(ngx_shmtx_t *mtx);
+ngx_uint_t ngx_shmtx_trylock(ngx_shmtx_t *mtx);
+void ngx_shmtx_lock(ngx_shmtx_t *mtx);
+void ngx_shmtx_unlock(ngx_shmtx_t *mtx);
 
-#endif
 
-
 #endif /* _NGX_SHMTX_H_INCLUDED_ */
Index: auto/os/freebsd
===================================================================
--- auto/os/freebsd	(revision 3907)
+++ auto/os/freebsd	(working copy)
@@ -49,7 +49,16 @@
     have=NGX_HAVE_AIO_SENDFILE . auto/have
 fi
 
+# POSIX semaphores
+# http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/127545
 
+if [ $osreldate -ge 701106 ]; then
+    echo " + POSIX semaphores should work"
+else
+    have=NGX_HAVE_POSIX_SEM . auto/nohave
+fi
+
+
 # kqueue
 
 if [ \( $osreldate -lt 500000 -a $osreldate -ge 410000 \) \
Index: auto/unix
===================================================================
--- auto/unix	(revision 3907)
+++ auto/unix	(working copy)
@@ -234,6 +234,18 @@
 . auto/feature
 
 
+ngx_feature="POSIX semaphores"
+ngx_feature_name="NGX_HAVE_POSIX_SEM"
+ngx_feature_run=yes
+ngx_feature_incs="#include <semaphore.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="sem_t  sem;
+                  if (sem_init(&sem, 1, 0) == -1) return 1;
+                  sem_destroy(&sem);"
+. auto/feature
+
+
 ngx_feature="struct msghdr.msg_control"
 ngx_feature_name="NGX_HAVE_MSGHDR_MSG_CONTROL"
 ngx_feature_run=no


More information about the nginx mailing list