Tcl Source Code

Check-in [e4212ca03b]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to [email protected]
or submit via the online form by Sep 9.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Implement the whole TIP 456 specification.

Also introduces the `-reuseaddr' and `-reuseport' options for the `socket' command.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | tip-456
Files: files | file ages | folders
SHA1: e4212ca03b4d608b0f82c8a5a0c18213f9765bdc
User & Date: limeboy 2016-11-24 12:47:21
Context
2016-11-24
15:00
Adjust the tests and add a handful of new ones. check-in: ac3cfef074 user: limeboy tags: tip-456
13:18
Fix compile error if SO_REUSEPORT not supported. Put command options in alphabetical order check-in: c2331d8741 user: jan.nijtmans tags: tip-456 fork
12:47
Implement the whole TIP 456 specification.

Also introduces the `-reuseaddr' and `-reuseport' option... check-in: e4212ca03b user: limeboy tags: tip-456

2016-11-22
11:24
Fix indenting check-in: c77d70c21f user: jan.nijtmans tags: tip-456
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tcl.decls.

2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
}

# ----- BASELINE -- FOR -- 8.6.0 ----- #

# TIP #456
declare 631 {
    Tcl_Channel Tcl_OpenTcpServerEx(Tcl_Interp *interp, int port,
	    const char *host, int flags, Tcl_TcpAcceptProc *acceptProc,
	    ClientData callbackData)
}

# ----- BASELINE -- FOR -- 8.7.0 ----- #









|







2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
}

# ----- BASELINE -- FOR -- 8.6.0 ----- #

# TIP #456
declare 631 {
    Tcl_Channel Tcl_OpenTcpServerEx(Tcl_Interp *interp, int port,
	    const char *host, unsigned int flags, Tcl_TcpAcceptProc *acceptProc,
	    ClientData callbackData)
}

# ----- BASELINE -- FOR -- 8.7.0 ----- #



Changes to generic/tcl.h.

2365
2366
2367
2368
2369
2370
2371







2372
2373
2374
2375
2376
2377
2378
 *----------------------------------------------------------------------------
 * Definitions needed for the Tcl_LoadFile function. [TIP #416]
 */

#define TCL_LOAD_GLOBAL 1
#define TCL_LOAD_LAZY 2








/*
 *----------------------------------------------------------------------------
 * Single public declaration for NRE.
 */

typedef int (Tcl_NRPostProc) (ClientData data[], Tcl_Interp *interp,
				int result);






>
>
>
>
>
>
>







2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
 *----------------------------------------------------------------------------
 * Definitions needed for the Tcl_LoadFile function. [TIP #416]
 */

#define TCL_LOAD_GLOBAL 1
#define TCL_LOAD_LAZY 2

/*
 *----------------------------------------------------------------------------
 * Definitions needed for the Tcl_OpenTcpServerEx function. [TIP #456]
 */
#define TCL_TCPSERVER_REUSEADDR (1<<0)
#define TCL_TCPSERVER_REUSEPORT (1<<1)

/*
 *----------------------------------------------------------------------------
 * Single public declaration for NRE.
 */

typedef int (Tcl_NRPostProc) (ClientData data[], Tcl_Interp *interp,
				int result);

Changes to generic/tclDecls.h.

1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
....
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
				Tcl_LoadHandle handlePtr);
/* 630 */
EXTERN void		Tcl_ZlibStreamSetCompressionDictionary(
				Tcl_ZlibStream zhandle,
				Tcl_Obj *compressionDictionaryObj);
/* 631 */
EXTERN Tcl_Channel	Tcl_OpenTcpServerEx(Tcl_Interp *interp, int port,
				const char *host, int flags,
				Tcl_TcpAcceptProc *acceptProc,
				ClientData callbackData);

typedef struct {
    const struct TclPlatStubs *tclPlatStubs;
    const struct TclIntStubs *tclIntStubs;
    const struct TclIntPlatStubs *tclIntPlatStubs;
................................................................................
    int (*tcl_CloseEx) (Tcl_Interp *interp, Tcl_Channel chan, int flags); /* 624 */
    int (*tcl_NRExprObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_Obj *resultPtr); /* 625 */
    int (*tcl_NRSubstObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 626 */
    int (*tcl_LoadFile) (Tcl_Interp *interp, Tcl_Obj *pathPtr, const char *const symv[], int flags, void *procPtrs, Tcl_LoadHandle *handlePtr); /* 627 */
    void * (*tcl_FindSymbol) (Tcl_Interp *interp, Tcl_LoadHandle handle, const char *symbol); /* 628 */
    int (*tcl_FSUnloadFile) (Tcl_Interp *interp, Tcl_LoadHandle handlePtr); /* 629 */
    void (*tcl_ZlibStreamSetCompressionDictionary) (Tcl_ZlibStream zhandle, Tcl_Obj *compressionDictionaryObj); /* 630 */
    Tcl_Channel (*tcl_OpenTcpServerEx) (Tcl_Interp *interp, int port, const char *host, int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); /* 631 */
} TclStubs;

extern const TclStubs *tclStubsPtr;

#ifdef __cplusplus
}
#endif






|







 







|







1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
....
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
				Tcl_LoadHandle handlePtr);
/* 630 */
EXTERN void		Tcl_ZlibStreamSetCompressionDictionary(
				Tcl_ZlibStream zhandle,
				Tcl_Obj *compressionDictionaryObj);
/* 631 */
EXTERN Tcl_Channel	Tcl_OpenTcpServerEx(Tcl_Interp *interp, int port,
				const char *host, unsigned int flags,
				Tcl_TcpAcceptProc *acceptProc,
				ClientData callbackData);

typedef struct {
    const struct TclPlatStubs *tclPlatStubs;
    const struct TclIntStubs *tclIntStubs;
    const struct TclIntPlatStubs *tclIntPlatStubs;
................................................................................
    int (*tcl_CloseEx) (Tcl_Interp *interp, Tcl_Channel chan, int flags); /* 624 */
    int (*tcl_NRExprObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_Obj *resultPtr); /* 625 */
    int (*tcl_NRSubstObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 626 */
    int (*tcl_LoadFile) (Tcl_Interp *interp, Tcl_Obj *pathPtr, const char *const symv[], int flags, void *procPtrs, Tcl_LoadHandle *handlePtr); /* 627 */
    void * (*tcl_FindSymbol) (Tcl_Interp *interp, Tcl_LoadHandle handle, const char *symbol); /* 628 */
    int (*tcl_FSUnloadFile) (Tcl_Interp *interp, Tcl_LoadHandle handlePtr); /* 629 */
    void (*tcl_ZlibStreamSetCompressionDictionary) (Tcl_ZlibStream zhandle, Tcl_Obj *compressionDictionaryObj); /* 630 */
    Tcl_Channel (*tcl_OpenTcpServerEx) (Tcl_Interp *interp, int port, const char *host, unsigned int flags, Tcl_TcpAcceptProc *acceptProc, ClientData callbackData); /* 631 */
} TclStubs;

extern const TclStubs *tclStubsPtr;

#ifdef __cplusplus
}
#endif

Changes to generic/tclIOCmd.c.

1481
1482
1483
1484
1485
1486
1487
1488

1489
1490
1491

1492
1493

1494
1495
1496
1497
1498
1499
1500
....
1553
1554
1555
1556
1557
1558
1559



1560
1561
1562
1563
1564
1565
1566
1567
1568
....
1579
1580
1581
1582
1583
1584
1585
1586







1587
1588
1589
1590
1591
1592
1593
Tcl_SocketObjCmd(
    ClientData notUsed,		/* Not used. */
    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", "-reuseport", NULL

    };
    enum socketOptions {
	SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEPORT

    };
    int optionIndex, a, server = 0, port, myport = 0, async = 0, flags = 0;

    const char *host, *myaddr = NULL;
    Tcl_Obj *script = NULL;
    Tcl_Channel chan;

    if (TclpHasSockets(interp) != TCL_OK) {
	return TCL_ERROR;
    }
................................................................................
	    if (a >= objc) {
		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"no argument given for -server option", -1));
		return TCL_ERROR;
	    }
	    script = objv[a];
	    break;



	case SKT_REUSEPORT:
	    flags |= 1;
	    break;
	default:
	    Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions");
	}
    }
    if (server) {
	host = myaddr;		/* NULL implies INADDR_ANY */
................................................................................

    wrongNumArgs:
	iPtr = (Interp *) interp;
	Tcl_WrongNumArgs(interp, 1, objv,
		"?-myaddr addr? ?-myport myport? ?-async? host port");
	iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS;
	Tcl_WrongNumArgs(interp, 1, objv,
		"-server command ?-myaddr addr? port");







	return TCL_ERROR;
    }

    if (a == objc-1) {
	if (TclSockGetPort(interp, TclGetString(objv[a]), "tcp",
		&port) != TCL_OK) {
	    return TCL_ERROR;






|
>


|
>

|
>







 







>
>
>

|







 







|
>
>
>
>
>
>
>







1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
....
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
....
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
Tcl_SocketObjCmd(
    ClientData notUsed,		/* Not used. */
    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", "-reuseaddr", "-reuseport",
	NULL
    };
    enum socketOptions {
	SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER, SKT_REUSEADDR,
	SKT_REUSEPORT
    };
    int optionIndex, a, server = 0, port, myport = 0, async = 0;
    unsigned int flags = 0;
    const char *host, *myaddr = NULL;
    Tcl_Obj *script = NULL;
    Tcl_Channel chan;

    if (TclpHasSockets(interp) != TCL_OK) {
	return TCL_ERROR;
    }
................................................................................
	    if (a >= objc) {
		Tcl_SetObjResult(interp, Tcl_NewStringObj(
			"no argument given for -server option", -1));
		return TCL_ERROR;
	    }
	    script = objv[a];
	    break;
	case SKT_REUSEADDR:
	    flags |= TCL_TCPSERVER_REUSEADDR;
	    break;
	case SKT_REUSEPORT:
	    flags |= TCL_TCPSERVER_REUSEPORT;
	    break;
	default:
	    Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions");
	}
    }
    if (server) {
	host = myaddr;		/* NULL implies INADDR_ANY */
................................................................................

    wrongNumArgs:
	iPtr = (Interp *) interp;
	Tcl_WrongNumArgs(interp, 1, objv,
		"?-myaddr addr? ?-myport myport? ?-async? host port");
	iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS;
	Tcl_WrongNumArgs(interp, 1, objv,
		"-server command ?-reuseaddr? ?-reuseport? ?-myaddr addr? port");
	return TCL_ERROR;
    }

    if (!server && flags != 0) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"options -reuseaddr and -reuseport are only valid for servers",
		-1));
	return TCL_ERROR;
    }

    if (a == objc-1) {
	if (TclSockGetPort(interp, TclGetString(objv[a]), "tcp",
		&port) != TCL_OK) {
	    return TCL_ERROR;

Changes to generic/tclIOSock.c.

279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
	if (v4head != NULL) {
	    v4ptr->ai_next = *addrlist;
	    *addrlist = v4head;
	}
    }
    return 1;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_OpenTcpServer --
 *
 *	Opens a TCP server socket and creates a channel around it.
 *
................................................................................
 *
 *----------------------------------------------------------------------
 */
Tcl_Channel Tcl_OpenTcpServer(Tcl_Interp *interp, int port,
	    const char *host, Tcl_TcpAcceptProc *acceptProc,
	    ClientData callbackData)
{
    return Tcl_OpenTcpServerEx(interp, port, host, 0, acceptProc, callbackData);
}

 
/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */






|







 







|
|
|








279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
	if (v4head != NULL) {
	    v4ptr->ai_next = *addrlist;
	    *addrlist = v4head;
	}
    }
    return 1;
}
 
/*
 *----------------------------------------------------------------------
 *
 * Tcl_OpenTcpServer --
 *
 *	Opens a TCP server socket and creates a channel around it.
 *
................................................................................
 *
 *----------------------------------------------------------------------
 */
Tcl_Channel Tcl_OpenTcpServer(Tcl_Interp *interp, int port,
	    const char *host, Tcl_TcpAcceptProc *acceptProc,
	    ClientData callbackData)
{
    return Tcl_OpenTcpServerEx(interp, port, host, TCL_TCPSERVER_REUSEADDR,
			       acceptProc, callbackData);
}
 
/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */

Changes to unix/tclUnixSock.c.

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
....
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
....
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517


1518
1519

1520


1521
1522
1523
1524


1525
1526
1527




1528
1529

1530
1531
1532
1533
1534
1535
1536
1537
1538
/*
 * The following defines how much buffer space the kernel should maintain for
 * 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
#endif

/*
 * Static routines for this file:
 */

static int		TcpConnect(Tcl_Interp *interp,
                                           TcpState *state);
static void		TcpAccept(ClientData data, int mask);
................................................................................
 */

Tcl_Channel
Tcl_OpenTcpServerEx(
    Tcl_Interp *interp,		/* For error reporting - may be NULL. */
    int port,			/* Port number to open. */
    const char *myHost,		/* Name of local host. */
    int flags,			/* Flags. */
    Tcl_TcpAcceptProc *acceptProc,
				/* Callback for accepting connections from new
				 * clients. */
    ClientData acceptProcData)	/* Data for the callback. */
{
    int status = 0, sock = -1, reuseaddr = 1, chosenport;
    struct addrinfo *addrlist = NULL, *addrPtr;	/* socket address */
    TcpState *statePtr = NULL;
    char channelName[SOCK_CHAN_LENGTH];
    const char *errorMsg = NULL;
    TcpFdList *fds = NULL, *newfds;

    /*
................................................................................
	/*
	 * Set kernel space buffering
	 */

	TclSockMinimumBuffers(INT2PTR(sock), SOCKET_BUFSIZE);

	/*
	 * Set up to reuse server addresses automatically and bind to the
	 * 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 (flags & USE_SOCK_REUSEPORT) {
	int reuseport = 1;




	(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.
         *
         * As sockaddr_in6 uses the same offset and size for the port member
         * as sockaddr_in, we can handle both through the IPv4 API.






<
<
<
<
<







 







|





|







 







|
<


>
>
|
<
>
|
>
>
|
|
<
<
>
>
|
<
<
>
>
>
>
|
<
>

<







109
110
111
112
113
114
115





116
117
118
119
120
121
122
....
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
....
1502
1503
1504
1505
1506
1507
1508
1509

1510
1511
1512
1513
1514

1515
1516
1517
1518
1519
1520


1521
1522
1523


1524
1525
1526
1527
1528

1529
1530

1531
1532
1533
1534
1535
1536
1537
/*
 * The following defines how much buffer space the kernel should maintain for
 * a socket.
 */

#define SOCKET_BUFSIZE	4096






/*
 * Static routines for this file:
 */

static int		TcpConnect(Tcl_Interp *interp,
                                           TcpState *state);
static void		TcpAccept(ClientData data, int mask);
................................................................................
 */

Tcl_Channel
Tcl_OpenTcpServerEx(
    Tcl_Interp *interp,		/* For error reporting - may be NULL. */
    int port,			/* Port number to open. */
    const char *myHost,		/* Name of local host. */
    unsigned int flags,		/* Flags. */
    Tcl_TcpAcceptProc *acceptProc,
				/* Callback for accepting connections from new
				 * clients. */
    ClientData acceptProcData)	/* Data for the callback. */
{
    int status = 0, sock = -1, optvalue, chosenport;
    struct addrinfo *addrlist = NULL, *addrPtr;	/* socket address */
    TcpState *statePtr = NULL;
    char channelName[SOCK_CHAN_LENGTH];
    const char *errorMsg = NULL;
    TcpFdList *fds = NULL, *newfds;

    /*
................................................................................
	/*
	 * Set kernel space buffering
	 */

	TclSockMinimumBuffers(INT2PTR(sock), SOCKET_BUFSIZE);

	/*
	 * Set up to reuse server addresses and/or ports if requested.

	 */

	if (flags & TCL_TCPSERVER_REUSEADDR) {
	    optvalue = 1;
	    (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,

			      (char *) &optvalue, sizeof(optvalue));
	}

	if (flags & TCL_TCPSERVER_REUSEPORT) {
#ifndef SO_REUSEPORT
	    /*


	     * If the platform doesn't support the SO_REUSEPORT flag we can't do
	     * much beside erroring out.
	     */


	    errorMsg = "SO_REUSEPORT isn't supported by this platform";
	    goto error;
#endif
	    optvalue = 1;
	    (void) setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,

			      (char *) &optvalue, sizeof(optvalue));
	}


        /*
         * Make sure we use the same port number when opening two server
         * sockets for IPv4 and IPv6 on a random port.
         *
         * As sockaddr_in6 uses the same offset and size for the port member
         * as sockaddr_in, we can handle both through the IPv4 API.

Changes to win/tclWinSock.c.

2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
 */

Tcl_Channel
Tcl_OpenTcpServerEx(
    Tcl_Interp *interp,		/* For error reporting - may be NULL. */
    int port,			/* Port number to open. */
    const char *myHost,		/* Name of local host. */
    int flags,			/* Flags (not used) */
    Tcl_TcpAcceptProc *acceptProc,
				/* Callback for accepting connections from new
				 * clients. */
    ClientData acceptProcData)	/* Data for the callback. */
{
    SOCKET sock = INVALID_SOCKET;
    unsigned short chosenport = 0;






|







2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
 */

Tcl_Channel
Tcl_OpenTcpServerEx(
    Tcl_Interp *interp,		/* For error reporting - may be NULL. */
    int port,			/* Port number to open. */
    const char *myHost,		/* Name of local host. */
    unsigned int flags,		/* Flags (not used) */
    Tcl_TcpAcceptProc *acceptProc,
				/* Callback for accepting connections from new
				 * clients. */
    ClientData acceptProcData)	/* Data for the callback. */
{
    SOCKET sock = INVALID_SOCKET;
    unsigned short chosenport = 0;