Changes On Branch bug-f583715154-v2
Not logged in

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

Changes In Branch bug-f583715154-v2 Excluding Merge-Ins

This is equivalent to a diff from 2616eb1a29 to c082062e57

2020-04-02
14:12
merge bug-f583715154-v2: added tests covering [f583715154] and few other RC similar situations aroun... check-in: b8112b0642 user: sebres tags: core-8-6-branch
2020-04-01
13:28
improve stability of socket*-13.2.* tests (e. g. windows compatibility, etc) Closed-Leaf check-in: c082062e57 user: sebres tags: bug-f583715154-v2
12:06
added tests covering [f583715154] and few other RC similar situations around socket thread transfer ... check-in: 9b7bd94769 user: sebres tags: bug-f583715154-v2
2020-03-31
20:04
fixes [f583715154] - tclUnixSock.c: introduced ThreadActionProc considering a transfer of async-conn... check-in: 532506ef71 user: sebres tags: bug-f583715154-v2
15:59
[b8e82d293b] Restrict the options of [binary encode uuencode] to values that will not prevent abilit... check-in: 94b6f3c028 user: dgp tags: core-8-6-branch
10:46
Merge 8.6 check-in: 5f099cb477 user: jan.nijtmans tags: core-8-branch
10:30
Other suggested solution for [b8e82d293b] check-in: cba0dea6a2 user: jan.nijtmans tags: bug-b8e82d293b-2
10:17
Remove MODULE_SCOPE function TclpSetInterfaces(), which is just empty. Increase size of MSG structu... check-in: 2616eb1a29 user: jan.nijtmans tags: core-8-6-branch
2020-03-30
15:48
Optimize [binary decode hex] to process bytearray without string generation when it can. check-in: 84f81d94cf user: dgp tags: core-8-6-branch

Changes to generic/tclIO.c.
3185
3186
3187
3188
3189
3190
3191



3192
3193
3194
3195
3196
3197
3198
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201







+
+
+







    statePtr->nextCSPtr = NULL;

    /*
     * TIP #218, Channel Thread Actions
     */

    ChanThreadAction((Channel *) chan, TCL_CHANNEL_THREAD_REMOVE);

    /* Channel is not managed by any thread */
    statePtr->managingThread = NULL;
}

void
Tcl_CutChannel(
    Tcl_Channel chan)		/* The channel being added. Must not be
				 * referenced in any interpreter. */
{
3229
3230
3231
3232
3233
3234
3235



3236
3237
3238
3239
3240
3241
3242
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248







+
+
+







     * TIP #218, Channel Thread Actions
     * For all transformations and the base channel.
     */

    for (; chanPtr != NULL ; chanPtr = chanPtr->upChanPtr) {
	ChanThreadAction(chanPtr, TCL_CHANNEL_THREAD_REMOVE);
    }

    /* Channel is not managed by any thread */
    statePtr->managingThread = NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_SpliceChannel --
 * SpliceChannel --
8376
8377
8378
8379
8380
8381
8382







8383
8384
8385
8386
8387
8388
8389
8382
8383
8384
8385
8386
8387
8388
8389
8390
8391
8392
8393
8394
8395
8396
8397
8398
8399
8400
8401
8402







+
+
+
+
+
+
+







     * Now call the channel handlers as usual.
     *
     * Preserve the channel struct in case the script closes it.
     */

    TclChannelPreserve((Tcl_Channel)channel);
    Tcl_Preserve(statePtr);

    /*
     * Avoid processing if the channel owner has been changed.
     */
    if (statePtr->managingThread != Tcl_GetCurrentThread()) {
	goto done;
    }

    /*
     * If we are flushing in the background, be sure to call FlushChannel for
     * writable events. Note that we have to discard the writable event so we
     * don't call any write handlers before the flush is complete.
     */

8411
8412
8413
8414
8415
8416
8417







8418
8419
8420
8421
8422
8423
8424
8425
8426
8427
8428
8429
8430
8431
8432
8433
8434

8435
8436
8437
8438
8439
8440
8441
8424
8425
8426
8427
8428
8429
8430
8431
8432
8433
8434
8435
8436
8437
8438
8439
8440
8441
8442
8443
8444
8445
8446
8447
8448
8449
8450
8451
8452
8453
8454
8455
8456
8457
8458
8459
8460
8461
8462







+
+
+
+
+
+
+

















+







	if ((chPtr->mask & mask) != 0) {
	    nh.nextHandlerPtr = chPtr->nextPtr;
	    chPtr->proc(chPtr->clientData, chPtr->mask & mask);
	    chPtr = nh.nextHandlerPtr;
	} else {
	    chPtr = chPtr->nextPtr;
	}

	/*
	 * Stop if the channel owner has been changed in-between.
	 */
	if (chanPtr->state->managingThread != Tcl_GetCurrentThread()) {
	    goto done;
	}
    }

    /*
     * Update the notifier interest, since it may have changed after invoking
     * event handlers. Skip that if the channel was deleted in the call to the
     * channel handler.
     */

    if (chanPtr->typePtr != NULL) {
	/*
	 * TODO: This call may not be needed.  If a handler induced a
	 * change in interest, that handler should have made its own
	 * UpdateInterest() call, one would think.
	 */
	UpdateInterest(chanPtr);
    }

done:
    Tcl_Release(statePtr);
    TclChannelRelease(channel);

    tsdPtr->nestedHandlerPtr = nh.nestedHandlerPtr;
}

/*
8904
8905
8906
8907
8908
8909
8910





8911
8912
8913
8914
8915
8916
8917
8925
8926
8927
8928
8929
8930
8931
8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943







+
+
+
+
+







    int result;			/* Result of call to eval script. */

    esPtr = (EventScriptRecord *)clientData;
    chanPtr = esPtr->chanPtr;
    mask = esPtr->mask;
    interp = esPtr->interp;

    /*
     * Be sure event executed in managed channel (covering bugs similar [f583715154]).
     */
    assert(chanPtr->state->managingThread == Tcl_GetCurrentThread());

    /*
     * We must preserve the interpreter so we can report errors on it later.
     * Note that we do not need to preserve the channel because that is done
     * by Tcl_NotifyChannel before calling channel handlers.
     */

    Tcl_Preserve(interp);
Changes to tests/socket.test.
58
59
60
61
62
63
64

65
66
67
68
69
70
71
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72







+







# server will be performed; otherwise, it will attempt to start the remote
# server (via exec) on platforms that support this, on the local host,
# listening at port 2048. If all fails, a message is printed and the tests
# using the remote server are not performed.

package require tcltest 2
namespace import -force ::tcltest::*
::tcltest::loadTestedCommands

if {[expr {[info exists ::env(TRAVIS_OSX_IMAGE)] && [string match xcode* $::env(TRAVIS_OSX_IMAGE)]}]} {
    return
}

# Some tests require the Thread package or exec command
testConstraint thread [expr {0 == [catch {package require Thread 2.7-}]}]
1812
1813
1814
1815
1816
1817
1818



































































































1819
1820
1821
1822
1823
1824
1825
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        puts $s "hello"
        gets $s result
    }
    close $s
    thread::release $serverthread
    append result " " [llength [thread::names]]
} -result {hello 1} -constraints [list socket supported_$af thread]

proc transf_test {{testmode transfer} {maxIter 1000} {maxTime 10000}} {
  try {
    set ::count 0
    set ::testmode $testmode
    set port 0
    set srvsock {}
    # if binding on port 0 is not possible (system related, blocked on ISPs etc):
    if {[catch {close [socket -async $::localhost $port]}]} {
      # simplest server on random port (immediatelly closing a connect):
      set port [randport]
      set srvsock [socket -server {apply {{ch args} {close $ch}}} -myaddr $::localhost $port]
      # socket on windows has some issues yet (e. g. bug [b6d0d8cc2c]), so we simply decrease iteration count (to 1/4):
      if {$::tcl_platform(platform) eq "windows" && $maxIter > 50} {
      	set ::count [expr {$maxIter / 4 * 3 - 1}]; # bypass 3/4 iterations
      }
    }
    tcltest::DebugPuts 1 "== test \[$::localhost\]:$port $testmode =="
    set ::master [thread::id]
    # helper thread creating async connection and initiating transfer (detach) to master:
    set ::helper [thread::create]
    thread::send -async $::helper [list \
      lassign [list $::master $::localhost $port $testmode] \
                     ::master ::localhost ::port ::testmode
    ]
    thread::send -async $::helper {
      set ::helper [thread::id]
      proc iteration {args} {
        set fd [socket -async $::localhost $::port]
        if {"helper-writable" in $::testmode} {;# to test both sides during connect
          fileevent $fd writable [list apply {{fd} {
            if {[thread::id] ne $::helper} {
              thread::send -async $::master {set ::count "ERROR: invalid thread, $::helper is expecting"}
              close $fd
              return
            }
          }} $fd]
        };#
        thread::detach $fd
        thread::send -async $::master [list transf_master $fd {*}$args]
      }
      iteration first
    }
    # master proc commiting transfer attempt (attach) and checking acquire was successful:
    proc transf_master {fd args} {
      tcltest::DebugPuts 1 "** trma / $::count ** $args **"
      thread::attach $fd
      if {"master-close" in $::testmode} {;# to test close during connect
        set ::count $::count
        close $fd
        return
      };#
      fileevent $fd writable [list apply {{fd} {
        if {[thread::id] ne $::master} {
          thread::send -async $::master {set ::count "ERROR: invalid thread, $::master is expecting"}
          close $fd
          return
        }
        set ::count $::count
        close $fd
      }} $fd]
    }
    # repeat maxIter times (up to maxTime ms as timeout):
    set tout [after $maxTime {set ::count "TIMEOUT"}]
    while 1 {
      vwait ::count
      if {![string is integer $::count]} {
        # if timeout just skip (test was successful until now):
      	if {$::count eq "TIMEOUT"} {::tcltest::Skip "timing issue"}
        break
      }
      if {[incr ::count] >= $maxIter} break
      tcltest::DebugPuts 1 "** iter / $::count **"
      thread::send -async $::helper [list iteration nr $::count]
    }
    update
    set ::count
  } finally {
    catch {after cancel $tout}
    if {$srvsock ne {}} {close $srvsock}
    if {[info exists ::helper]} {thread::release -wait $::helper}
    tcltest::DebugPuts 1 "== stop / $::count =="
    unset -nocomplain ::count ::testmode ::master ::helper
  }
}
test socket_$af-13.2.tr1 {Testing socket transfer between threads during async connect} -body {
    transf_test {transfer} 1000
} -result 1000 -constraints [list socket supported_$af thread]
test socket_$af-13.2.tr2 {Testing socket transfer between threads during async connect} -body {
    transf_test {transfer helper-writable} 100
} -result 100 -constraints [list socket supported_$af thread]
test socket_$af-13.2.cl1 {Testing socket transfer between threads during async connect} -body {
    transf_test {master-close} 100
} -result 100 -constraints [list socket supported_$af thread]
test socket_$af-13.2.cl2 {Testing socket transfer between threads during async connect} -body {
    transf_test {master-close helper-writable} 100
} -result 100 -constraints [list socket supported_$af thread]
catch {rename transf_master {}}
rename transf_test {}

# ----------------------------------------------------------------------

removeFile script1
removeFile script2

# cleanup
Changes to unix/tclUnixSock.c.
114
115
116
117
118
119
120

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

137
138
139
140
141
142
143
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145







+
















+








#define SOCKET_BUFSIZE	4096

/*
 * Static routines for this file:
 */

static void		TcpAsyncCallback(ClientData clientData, int mask);
static int		TcpConnect(Tcl_Interp *interp, TcpState *state);
static void		TcpAccept(ClientData data, int mask);
static int		TcpBlockModeProc(ClientData data, int mode);
static int		TcpCloseProc(ClientData instanceData,
			    Tcl_Interp *interp);
static int		TcpClose2Proc(ClientData instanceData,
			    Tcl_Interp *interp, int flags);
static int		TcpGetHandleProc(ClientData instanceData,
			    int direction, ClientData *handlePtr);
static int		TcpGetOptionProc(ClientData instanceData,
			    Tcl_Interp *interp, const char *optionName,
			    Tcl_DString *dsPtr);
static int		TcpInputProc(ClientData instanceData, char *buf,
			    int toRead, int *errorCode);
static int		TcpOutputProc(ClientData instanceData,
			    const char *buf, int toWrite, int *errorCode);
static void		TcpThreadActionProc(ClientData instanceData, int action);
static void		TcpWatchProc(ClientData instanceData, int mask);
static int		WaitForConnect(TcpState *statePtr, int *errorCodePtr);
static void		WrapNotify(ClientData clientData, int mask);

/*
 * This structure describes the channel type structure for TCP socket
 * based IO:
155
156
157
158
159
160
161
162

163
164
165
166
167
168
169
157
158
159
160
161
162
163

164
165
166
167
168
169
170
171







-
+







    TcpWatchProc,		/* Initialize notifier. */
    TcpGetHandleProc,		/* Get OS handles out of channel. */
    TcpClose2Proc,		/* Close2 proc. */
    TcpBlockModeProc,		/* Set blocking or non-blocking mode.*/
    NULL,			/* flush proc. */
    NULL,			/* handler proc. */
    NULL,			/* wide seek proc. */
    NULL,			/* thread action proc. */
    TcpThreadActionProc,	/* thread action proc. */
    NULL			/* truncate proc. */
};

/*
 * The following variable holds the network name of this host.
 */

950
951
952
953
954
955
956













































957
958
959
960
961
962
963
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







    if (len > 0) {
	return Tcl_BadChannelOption(interp, optionName,
                "connecting peername sockname");
    }

    return TCL_OK;
}

/*
 * ----------------------------------------------------------------------
 *
 * TcpThreadActionProc --
 *
 *	Handles detach/attach for asynchronously connecting socket.
 *
 *	Reassigning the file handler associated with thread-related channel
 *	notification, responsible for callbacks (signaling that asynchronous
 *	connection attempt has succeeded or failed).
 *
 * Results:
 *	None.
 *
 * ----------------------------------------------------------------------
 */

static void
TcpThreadActionProc(
    ClientData instanceData,
    int action)
{
    TcpState *statePtr = (TcpState *)instanceData;

    if (GOT_BITS(statePtr->flags, TCP_ASYNC_CONNECT)) {
	/*
	 * Async-connecting socket must get reassigned handler if it have been
	 * transferred to another thread. Remove the handler if the socket is 
	 * not managed by this thread anymore and create new handler (TSD related)
	 * so the callback will run in the correct thread, bug [f583715154].
	 */
	switch (action) {
	  case TCL_CHANNEL_THREAD_REMOVE:
	    CLEAR_BITS(statePtr->flags, TCP_ASYNC_PENDING);
	    Tcl_DeleteFileHandler(statePtr->fds.fd);
	  break;
	  case TCL_CHANNEL_THREAD_INSERT:
	    Tcl_CreateFileHandler(statePtr->fds.fd,
		TCL_WRITABLE | TCL_EXCEPTION, TcpAsyncCallback, statePtr);
	    SET_BITS(statePtr->flags, TCP_ASYNC_PENDING);
	  break;
	}
    }
}

/*
 * ----------------------------------------------------------------------
 *
 * TcpWatchProc --
 *
 *	Initialize the notifier to watch the fd from this channel.