Tcl Source Code

Artifact [9923db5cb1]
Login

Artifact 9923db5cb1023023638dbbffa168343933f881bf:

Attachment "patch.002" to ticket [0b9d3ba2ba] added by cklein 2016-11-18 16:36:35. (unpublished)
Index: generic/tclIOCmd.c
==================================================================
--- generic/tclIOCmd.c
+++ generic/tclIOCmd.c
@@ -1483,16 +1483,16 @@
     Tcl_Interp *interp,		/* Current interpreter. */
     int objc,			/* Number of arguments. */
     Tcl_Obj *const objv[])	/* Argument objects. */
 {
     static const char *const socketOptions[] = {
-	"-async", "-myaddr", "-myport", "-server", NULL
+	"-async", "-myaddr", "-myport", "-server", "-reuseport", NULL
     };
     enum socketOptions {
-	SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER
+	SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEPORT
     };
-    int optionIndex, a, server = 0, port, myport = 0, async = 0;
+    int optionIndex, a, server = 0, port, myport = 0, async = 0, reuseport = 0;
     const char *host, *myaddr = NULL;
     Tcl_Obj *script = NULL;
     Tcl_Channel chan;
 
     if (TclpHasSockets(interp) != TCL_OK) {
@@ -1555,10 +1555,13 @@
 			"no argument given for -server option", -1));
 		return TCL_ERROR;
 	    }
 	    script = objv[a];
 	    break;
+    case SKT_REUSEPORT:
+        reuseport = 1;
+        break;
 	default:
 	    Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions");
 	}
     }
     if (server) {
@@ -1598,10 +1601,16 @@
 		ckalloc(sizeof(AcceptCallback));
 
 	Tcl_IncrRefCount(script);
 	acceptCallbackPtr->script = script;
 	acceptCallbackPtr->interp = interp;
+
+    /* Hint for Tcl_OpenTcpServer to set socket option REUSEPORT */
+    if(reuseport) {
+        port |= (1 << 16);
+    }
+
 	chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
 		acceptCallbackPtr);
 	if (chan == NULL) {
 	    Tcl_DecrRefCount(script);
 	    ckfree(acceptCallbackPtr);

Index: unix/tclUnixSock.c
==================================================================
--- unix/tclUnixSock.c
+++ unix/tclUnixSock.c
@@ -111,10 +111,15 @@
  * a socket.
  */
 
 #define SOCKET_BUFSIZE	4096
 
+#ifdef SO_REUSEPORT
+/* Bitmask to check if the setting of SO_REUSEPORT was requested by the caller. */
+#define USE_SOCK_REUSEPORT (1 << 16)
+#endif
+
 /*
  * Static routines for this file:
  */
 
 static int		TcpConnect(Tcl_Interp *interp,
@@ -1433,10 +1438,14 @@
     struct addrinfo *addrlist = NULL, *addrPtr;	/* socket address */
     TcpState *statePtr = NULL;
     char channelName[SOCK_CHAN_LENGTH];
     const char *errorMsg = NULL;
     TcpFdList *fds = NULL, *newfds;
+#ifdef SO_REUSEPORT
+    int reuseport = port & USE_SOCK_REUSEPORT;
+    CLEAR_BITS(port, USE_SOCK_REUSEPORT);
+#endif
 
     /*
      * Try to record and return the most meaningful error message, i.e. the
      * one from the first socket that went the farthest before it failed.
      */
@@ -1509,10 +1518,21 @@
 	 * specified port.
 	 */
 
 	(void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
 		(char *) &reuseaddr, sizeof(reuseaddr));
+
+#ifdef SO_REUSEPORT
+    /*
+     * Set up to allows multiple sockets on the same host to bind to the same port.
+     * The flag can be switched on by setting the lowest bit above the valid maximum port (0xffff).
+     */
+    if(reuseport) {
+        (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
+            (char *) &reuseport, sizeof(reuseport));
+    }
+#endif
 
         /*
          * Make sure we use the same port number when opening two server
          * sockets for IPv4 and IPv6 on a random port.
          *

Index: win/tclWinSock.c
==================================================================
--- win/tclWinSock.c
+++ win/tclWinSock.c
@@ -2109,12 +2109,12 @@
 		htons(chosenport);
 	}
 
 	/*
 	 * Bind to the specified port. Note that we must not call
-	 * setsockopt with SO_REUSEADDR because Microsoft allows addresses
-	 * to be reused even if they are still in use.
+	 * setsockopt with SO_REUSEADDR or SO_REUSEPORT because Microsoft
+	 * allows addresses and ports to be reused even if they are still in use.
 	 *
 	 * Bind should not be affected by the socket having already been
 	 * set into nonblocking mode. If there is trouble, this is one
 	 * place to look for bugs.
 	 */