diff options
-rw-r--r-- | ChangeLog | 24 | ||||
-rw-r--r-- | generic/tclIO.c | 58 | ||||
-rw-r--r-- | generic/tclIO.h | 13 |
3 files changed, 78 insertions, 17 deletions
@@ -1,3 +1,27 @@ +2001-09-26 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * The changes below fix [Bug #462317] where Expect tried to read + more than was in the buffers and then blocked in the OS call as + its pty channel driver provides no blockmodeproc through which + the OS could be notified of blocking-behaviour. Because of this + the general I/O core has to take more care than usual to + preserve the semantics of non-blocking channels. + + * generic/tclIO.c (Tcl_ReadRaw): Do not read from the driver if + the channel is non-blocking and the fileevent causing the read + was generated by a timer. We do not know if there is data + available from the OS. Instead of going to the OS for more and + potentially blocking we simply signal EWOULDBLOCK to the higher + levels to cause the system to wait for true fileevents. + (GetInput): Same as before. + (ChannelTimerProc): Added set and clear of CHANNEL_TIMER_FEV. + + * generic/tclIO.h (CHANNEL_TIMER_FEV): New flag for channels. Is + set if a fileevent was generated by a timer, the channel is not + blocking and the driver did not provide a blockmodeproc. In that + case the I/O core has to be especially careful about going to + the driver for more data. + 2001-09-26 Don Porter <dgp@users.sourceforge.net> * doc/SplitPath.3 (Tcl_GetPathType): diff --git a/generic/tclIO.c b/generic/tclIO.c index e7be46f..7f5cc30 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIO.c,v 1.35 2001/09/06 09:35:39 dkf Exp $ + * RCS: @(#) $Id: tclIO.c,v 1.36 2001/09/27 02:12:19 andreas_kupries Exp $ */ #include "tclInt.h" @@ -4149,17 +4149,23 @@ Tcl_ReadRaw(chan, bufPtr, bytesToRead) statePtr->flags &= (~(CHANNEL_BLOCKED)); } - /* - * Now go to the driver to get as much as is possible to - * fill the remaining request. Do all the error handling - * by ourselves. The code was stolen from 'GetInput' and - * slightly adapted (different return value here). - * - * The case of 'bytesToRead == 0' at this point cannot happen. - */ - - nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData, + if ((statePtr->flags & CHANNEL_TIMER_FEV) && + (statePtr->flags & CHANNEL_NONBLOCKING)) { + nread = -1; + result = EWOULDBLOCK; + } else { + /* + * Now go to the driver to get as much as is possible to + * fill the remaining request. Do all the error handling + * by ourselves. The code was stolen from 'GetInput' and + * slightly adapted (different return value here). + * + * The case of 'bytesToRead == 0' at this point cannot happen. + */ + + nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData, bufPtr + copied, bytesToRead - copied, &result); + } if (nread > 0) { /* * If we get a short read, signal up that we may be @@ -5185,7 +5191,7 @@ GetInput(chanPtr) } statePtr->inQueueTail = bufPtr; } - + /* * If EOF is set, we should avoid calling the driver because on some * platforms it is impossible to read from a device after EOF. @@ -5195,8 +5201,14 @@ GetInput(chanPtr) return 0; } - nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData, - bufPtr->buf + bufPtr->nextAdded, toRead, &result); + if ((statePtr->flags & CHANNEL_TIMER_FEV) && + (statePtr->flags & CHANNEL_NONBLOCKING)) { + nread = -1; + result = EWOULDBLOCK; + } else { + nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData, + bufPtr->buf + bufPtr->nextAdded, toRead, &result); + } if (nread > 0) { bufPtr->nextAdded += nread; @@ -5221,7 +5233,7 @@ GetInput(chanPtr) } Tcl_SetErrno(result); return result; - } + } return 0; } @@ -6711,8 +6723,22 @@ ChannelTimerProc(clientData) statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc, (ClientData) chanPtr); + + /* Set the TIMER flag to notify the higher levels that the + * driver might have no data for us. We do this only if we are + * in non-blocking mode and the driver has no BlockModeProc + * because only then we really don't know if the driver will + * block or not. A similar test is done in "PeekAhead". + */ + + if ((statePtr->flags & CHANNEL_NONBLOCKING) && + (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL)) { + statePtr->flags |= CHANNEL_TIMER_FEV; + } + Tcl_NotifyChannel((Tcl_Channel)chanPtr, TCL_READABLE); - + + statePtr->flags &= ~CHANNEL_TIMER_FEV; } else { statePtr->timer = NULL; UpdateInterest(chanPtr); diff --git a/generic/tclIO.h b/generic/tclIO.h index caac702..2465fba 100644 --- a/generic/tclIO.h +++ b/generic/tclIO.h @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIO.h,v 1.3 2001/03/30 23:06:40 andreas_kupries Exp $ + * RCS: @(#) $Id: tclIO.h,v 1.4 2001/09/27 02:12:19 andreas_kupries Exp $ */ /* @@ -296,6 +296,17 @@ typedef struct ChannelState { * the state of the channel changes. */ #define CHANNEL_RAW_MODE (1<<16) /* When set, notes that the Raw API is * being used. */ +#define CHANNEL_TIMER_FEV (1<<17) /* When set the event we are + * notified by is a fileevent + * generated by a timer. We + * don't know if the driver + * has more data and should + * not try to read from it. If + * the system needs more than + * is in the buffers out read + * routines will simulate a + * short read (0 characters + * read) */ /* * For each channel handler registered in a call to Tcl_CreateChannelHandler, |