# HG changeset patch
# User Sepherosa Ziehau <sepherosa@gmail.com>
# Date 1375091148 -28800
#      Mon Jul 29 17:45:48 2013 +0800
# Node ID 818c09f4a7ce2b4bf00db55864fc6077875c37e3
# Parent  d3eab5e2df5f38ba0e47c490e5fcbee68ad9f27b
# Parent  dc8e8dc6b97c1d3299dea05dddca400da5270988
SO_REUSEPORT

diff -r d3eab5e2df5f -r 818c09f4a7ce src/core/nginx.c
--- a/src/core/nginx.c	Thu Jul 25 15:00:41 2013 +0400
+++ b/src/core/nginx.c	Mon Jul 29 17:45:48 2013 +0800
@@ -23,6 +23,8 @@
     void *conf);
 static char *ngx_set_worker_processes(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_set_so_reuseport(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 
 static ngx_conf_enum_t  ngx_debug_points[] = {
@@ -76,6 +78,13 @@
       0,
       NULL },
 
+    { ngx_string("so_reuseport"),
+      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+      ngx_set_so_reuseport,
+      0,
+      0,
+      NULL },
+
     { ngx_string("debug_points"),
       NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_enum_slot,
@@ -1361,3 +1370,27 @@
 
     return NGX_CONF_OK;
 }
+
+
+static char *
+ngx_set_so_reuseport(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#ifdef SO_REUSEPORT
+    ngx_str_t        *value;
+    ngx_core_conf_t  *ccf;
+
+    ccf = (ngx_core_conf_t *) conf;
+
+    value = (ngx_str_t *) cf->args->elts;
+
+    if (ngx_strcmp(value[1].data, "on") == 0) {
+        if (!ngx_test_config)
+            ccf->so_reuseport = 1;
+    } else if (ngx_strcmp(value[1].data, "off") == 0) {
+        ccf->so_reuseport = 0;
+    } else {
+        return "invalid value";
+    }
+#endif
+    return NGX_CONF_OK;
+}
diff -r d3eab5e2df5f -r 818c09f4a7ce src/core/ngx_connection.c
--- a/src/core/ngx_connection.c	Thu Jul 25 15:00:41 2013 +0400
+++ b/src/core/ngx_connection.c	Mon Jul 29 17:45:48 2013 +0800
@@ -269,18 +269,26 @@
 ngx_int_t
 ngx_open_listening_sockets(ngx_cycle_t *cycle)
 {
-    int               reuseaddr;
+    int               reuseaddr, reuseport;
     ngx_uint_t        i, tries, failed;
     ngx_err_t         err;
     ngx_log_t        *log;
     ngx_socket_t      s;
     ngx_listening_t  *ls;
+    ngx_core_conf_t  *ccf;
 
     reuseaddr = 1;
+    reuseport = 0;
 #if (NGX_SUPPRESS_WARN)
     failed = 0;
 #endif
 
+    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+    if (ccf->so_reuseport) {
+        reuseaddr = 0;
+        reuseport = 1;
+    }
+
     log = cycle->log;
 
     /* TODO: configurable try number */
@@ -318,21 +326,43 @@
                 return NGX_ERROR;
             }
 
-            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
-                           (const void *) &reuseaddr, sizeof(int))
-                == -1)
-            {
-                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
-                              "setsockopt(SO_REUSEADDR) %V failed",
-                              &ls[i].addr_text);
+            if (reuseaddr) {
+                if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                               (const void *) &reuseaddr, sizeof(int))
+                    == -1)
+                {
+                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                  "setsockopt(SO_REUSEADDR) %V failed",
+                                  &ls[i].addr_text);
 
-                if (ngx_close_socket(s) == -1) {
+                    if (ngx_close_socket(s) == -1) {
+                        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                      ngx_close_socket_n " %V failed",
+                                      &ls[i].addr_text);
+                    }
+
+                    return NGX_ERROR;
+                }
+            }
+            if (reuseport) {
+#ifdef SO_REUSEPORT
+                if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT,
+                               (const void *) &reuseport, sizeof(int))
+                    == -1)
+                {
                     ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
-                                  ngx_close_socket_n " %V failed",
+                                  "setsockopt(SO_REUSEPORT) %V failed",
                                   &ls[i].addr_text);
+
+                    if (ngx_close_socket(s) == -1) {
+                        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                      ngx_close_socket_n " %V failed",
+                                      &ls[i].addr_text);
+                    }
+
+                    return NGX_ERROR;
                 }
-
-                return NGX_ERROR;
+#endif
             }
 
 #if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
diff -r d3eab5e2df5f -r 818c09f4a7ce src/core/ngx_cycle.c
--- a/src/core/ngx_cycle.c	Thu Jul 25 15:00:41 2013 +0400
+++ b/src/core/ngx_cycle.c	Mon Jul 29 17:45:48 2013 +0800
@@ -565,12 +565,14 @@
         }
     }
 
-    if (ngx_open_listening_sockets(cycle) != NGX_OK) {
-        goto failed;
-    }
+    if (!ccf->so_reuseport) {
+        if (ngx_open_listening_sockets(cycle) != NGX_OK) {
+            goto failed;
+        }
 
-    if (!ngx_test_config) {
-        ngx_configure_listening_sockets(cycle);
+        if (!ngx_test_config) {
+            ngx_configure_listening_sockets(cycle);
+        }
     }
 
 
diff -r d3eab5e2df5f -r 818c09f4a7ce src/core/ngx_cycle.h
--- a/src/core/ngx_cycle.h	Thu Jul 25 15:00:41 2013 +0400
+++ b/src/core/ngx_cycle.h	Mon Jul 29 17:45:48 2013 +0800
@@ -103,6 +103,8 @@
      ngx_array_t              env;
      char                   **environment;
 
+     unsigned                 so_reuseport:1;
+
 #if (NGX_THREADS)
      ngx_int_t                worker_threads;
      size_t                   thread_stack_size;
diff -r d3eab5e2df5f -r 818c09f4a7ce src/event/ngx_event.c
--- a/src/event/ngx_event.c	Thu Jul 25 15:00:41 2013 +0400
+++ b/src/event/ngx_event.c	Mon Jul 29 17:45:48 2013 +0800
@@ -598,7 +598,8 @@
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
     ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
 
-    if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
+    if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex &&
+        !ccf->so_reuseport) {
         ngx_use_accept_mutex = 1;
         ngx_accept_mutex_held = 0;
         ngx_accept_mutex_delay = ecf->accept_mutex_delay;
diff -r d3eab5e2df5f -r 818c09f4a7ce src/os/unix/ngx_process_cycle.c
--- a/src/os/unix/ngx_process_cycle.c	Thu Jul 25 15:00:41 2013 +0400
+++ b/src/os/unix/ngx_process_cycle.c	Mon Jul 29 17:45:48 2013 +0800
@@ -853,13 +853,18 @@
     ngx_core_conf_t  *ccf;
     ngx_listening_t  *ls;
 
+    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+    if (ccf->so_reuseport) {
+        ngx_open_listening_sockets(cycle);
+        ngx_configure_listening_sockets(cycle);
+    }
+
     if (ngx_set_environment(cycle, NULL) == NULL) {
         /* fatal */
         exit(2);
     }
 
-    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
-
     if (worker >= 0 && ccf->priority != 0) {
         if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {
             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
