diff options
author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2014-07-18 07:32:43 (GMT) |
---|---|---|
committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2014-07-18 07:32:43 (GMT) |
commit | b1ce77efec5ae88c40c52c8c2dfef2ec0120876f (patch) | |
tree | d79b1b35d05881a69d677c847399c574cc159ce4 /unix/tclUnixSock.c | |
parent | 57fd7d58a12e28ba76f2bafdf441d53fabf47cb0 (diff) | |
parent | 0cb480df70afc69c2a1637894dddd3f0b4e6d351 (diff) | |
download | tcl-b1ce77efec5ae88c40c52c8c2dfef2ec0120876f.zip tcl-b1ce77efec5ae88c40c52c8c2dfef2ec0120876f.tar.gz tcl-b1ce77efec5ae88c40c52c8c2dfef2ec0120876f.tar.bz2 |
merge trunk
Diffstat (limited to 'unix/tclUnixSock.c')
-rw-r--r-- | unix/tclUnixSock.c | 58 |
1 files changed, 56 insertions, 2 deletions
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c index fdd4287..0f700cf 100644 --- a/unix/tclUnixSock.c +++ b/unix/tclUnixSock.c @@ -57,6 +57,8 @@ struct TcpState { TcpFdList fds; /* The file descriptors of the sockets. */ int flags; /* ORed combination of the bitfields defined * below. */ + int interest; /* Event types of interest */ + /* * Only needed for server sockets */ @@ -145,6 +147,7 @@ static int TcpOutputProc(ClientData instanceData, const char *buf, int toWrite, int *errorCode); 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 @@ -930,6 +933,35 @@ TcpGetOptionProc( */ static void +WrapNotify( + ClientData clientData, + int mask) +{ + TcpState *statePtr = (TcpState *) clientData; + int newmask = mask & statePtr->interest; + + if (newmask == 0) { + /* + * There was no overlap between the states the channel is + * interested in notifications for, and the states that are + * reported present on the file descriptor by select(). The + * only way that can happen is when the channel is interested + * in a writable condition, and only a readable state is reported + * present (see TcpWatchProc() below). In that case, signal back + * to the caller the writable state, which is really an error + * condition. As an extra check on that assumption, check for + * a non-zero value of errno before reporting an artificial + * writable state. + */ + if (errno == 0) { + return; + } + newmask = TCL_WRITABLE; + } + Tcl_NotifyChannel(statePtr->channel, newmask); +} + +static void TcpWatchProc( ClientData instanceData, /* The socket state. */ int mask) /* Events of interest; an OR-ed combination of @@ -952,8 +984,30 @@ TcpWatchProc( * need to cache this request until the connection has succeeded. */ statePtr->filehandlers = mask; } else if (mask) { - Tcl_CreateFileHandler(statePtr->fds.fd, mask, - (Tcl_FileProc *) Tcl_NotifyChannel, statePtr->channel); + + /* + * Whether it is a bug or feature or otherwise, it is a fact + * of life that on at least some Linux kernels select() fails + * to report that a socket file descriptor is writable when + * the other end of the socket is closed. This is in contrast + * to the guarantees Tcl makes that its channels become + * writable and fire writable events on an error conditon. + * This has caused a leak of file descriptors in a state of + * background flushing. See Tcl ticket 1758a0b603. + * + * As a workaround, when our caller indicates an interest in + * writable notifications, we must tell the notifier built + * around select() that we are interested in the readable state + * of the file descriptor as well, as that is the only reliable + * means to get notified of error conditions. Then it is the + * task of WrapNotify() above to untangle the meaning of these + * channel states and report the chan events as best it can. + * We save a copy of the mask passed in to assist with that. + */ + + statePtr->interest = mask; + Tcl_CreateFileHandler(statePtr->fds.fd, mask|TCL_READABLE, + (Tcl_FileProc *) WrapNotify, statePtr); } else { Tcl_DeleteFileHandler(statePtr->fds.fd); } |