summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclIO.c26
-rw-r--r--unix/tclUnixSock.c49
2 files changed, 74 insertions, 1 deletions
diff --git a/generic/tclIO.c b/generic/tclIO.c
index 67264a0..ab8d8ac 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -3189,6 +3189,9 @@ CutChannel(
*/
ChanThreadAction((Channel *) chan, TCL_CHANNEL_THREAD_REMOVE);
+
+ /* Channel is not managed by any thread */
+ statePtr->managingThread = NULL;
}
void
@@ -3233,6 +3236,9 @@ Tcl_CutChannel(
for (; chanPtr != NULL ; chanPtr = chanPtr->upChanPtr) {
ChanThreadAction(chanPtr, TCL_CHANNEL_THREAD_REMOVE);
}
+
+ /* Channel is not managed by any thread */
+ statePtr->managingThread = NULL;
}
/*
@@ -8382,6 +8388,13 @@ Tcl_NotifyChannel(
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.
@@ -8415,6 +8428,13 @@ Tcl_NotifyChannel(
} else {
chPtr = chPtr->nextPtr;
}
+
+ /*
+ * Stop if the channel owner has been changed in-between.
+ */
+ if (chanPtr->state->managingThread != Tcl_GetCurrentThread()) {
+ goto done;
+ }
}
/*
@@ -8432,6 +8452,7 @@ Tcl_NotifyChannel(
UpdateInterest(chanPtr);
}
+done:
Tcl_Release(statePtr);
TclChannelRelease(channel);
@@ -8909,6 +8930,11 @@ TclChannelEventScriptInvoker(
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.
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c
index 1901c8b..4d7a8fb 100644
--- a/unix/tclUnixSock.c
+++ b/unix/tclUnixSock.c
@@ -118,6 +118,7 @@ struct TcpState {
* 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);
@@ -134,6 +135,7 @@ 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);
@@ -159,7 +161,7 @@ static const Tcl_ChannelType tcpChannelType = {
NULL, /* flush proc. */
NULL, /* handler proc. */
NULL, /* wide seek proc. */
- NULL, /* thread action proc. */
+ TcpThreadActionProc, /* thread action proc. */
NULL /* truncate proc. */
};
@@ -958,6 +960,51 @@ TcpGetOptionProc(
/*
* ----------------------------------------------------------------------
*
+ * 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.