summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandreas_kupries <andreas_kupries@noemail.net>2001-09-27 02:26:42 (GMT)
committerandreas_kupries <andreas_kupries@noemail.net>2001-09-27 02:26:42 (GMT)
commiteff70831c3e75c362c8339b1caa9e180bd9cce6c (patch)
tree5064f661b6561d88f55d23c22e13fd908a5fa448
parented712b3293ef31d4ba7b4064169f2ce54f8e18f8 (diff)
downloadtcl-eff70831c3e75c362c8339b1caa9e180bd9cce6c.zip
tcl-eff70831c3e75c362c8339b1caa9e180bd9cce6c.tar.gz
tcl-eff70831c3e75c362c8339b1caa9e180bd9cce6c.tar.bz2
* 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. FossilOrigin-Name: defcffd4a14645d49f97f7a90dd9ed4a867d8621
-rw-r--r--ChangeLog24
-rw-r--r--generic/tclIO.c56
-rw-r--r--generic/tclIO.h13
3 files changed, 77 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 3a73d98..86dba5d 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-24 Andreas Kupries <andreas_kupries@users.sourceforge.net>
* The change below fixes [Bug #464380]. The bug was reported by
diff --git a/generic/tclIO.c b/generic/tclIO.c
index 2d35e86..dafc6af 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.20.2.8 2001/07/18 17:17:19 andreas_kupries Exp $
+ * RCS: @(#) $Id: tclIO.c,v 1.20.2.9 2001/09/27 02:26:42 andreas_kupries Exp $
*/
#include "tclInt.h"
@@ -3774,17 +3774,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
@@ -4771,8 +4777,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;
@@ -4797,7 +4809,7 @@ GetInput(chanPtr)
}
Tcl_SetErrno(result);
return result;
- }
+ }
return 0;
}
@@ -6287,8 +6299,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 29d7b15..5172c2b 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.1.4.1 2000/07/27 01:39:17 hobbs Exp $
+ * RCS: @(#) $Id: tclIO.h,v 1.1.4.2 2001/09/27 02:26:43 andreas_kupries Exp $
*/
/*
@@ -294,6 +294,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,