summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog24
-rw-r--r--generic/tclIO.c58
-rw-r--r--generic/tclIO.h13
3 files changed, 78 insertions, 17 deletions
diff --git a/ChangeLog b/ChangeLog
index 8e541a3..0a3c241 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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,