diff options
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | generic/tclIO.c | 95 | ||||
-rw-r--r-- | generic/tclIO.h | 18 |
3 files changed, 120 insertions, 7 deletions
@@ -1,3 +1,17 @@ +2004-05-19 Andreas Kupries <andreask@activestate.com> + + * tclIO.c: Fixed [SF Tcl Bug 943274]. This is the same problem as + * tclIO.h: [SF Tcl Bug 462317], see ChangeLog entry + 2001-09-26. The fix done at that time is incomplete. It + is possible to get around it if the actual read + operation is defered and not executed in the event + handler itself. Instead of tracking if we are in an + read caused by a synthesized fileevent we now track if + the OS has delivered a true event = actual data and + bypass the driver if a read finds that there is no + actual data waiting. The flag is cleared by a short or + full read. + 2004-05-17 Vince Darley <vincentdarley@users.sourceforge.net> * generic/tclPathObj.c: fix to (Bug 956063) in 'file dirname'. diff --git a/generic/tclIO.c b/generic/tclIO.c index 93bd0de..1a40828 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.73 2004/04/23 23:38:42 andreas_kupries Exp $ + * RCS: @(#) $Id: tclIO.c,v 1.74 2004/05/19 19:41:09 andreas_kupries Exp $ */ #include "tclInt.h" @@ -4237,8 +4237,18 @@ Tcl_ReadRaw(chan, bufPtr, bytesToRead) statePtr->flags &= (~(CHANNEL_BLOCKED)); } - if ((statePtr->flags & CHANNEL_TIMER_FEV) && - (statePtr->flags & CHANNEL_NONBLOCKING)) { + /* [SF Tcl Bug 943274]. Better emulation of non-blocking + * channels for channels without BlockModeProc, by keeping + * track of true fileevents generated by the OS == Data + * waiting and reading if and only if we are sure to have + * data. + */ + + if ((statePtr->flags & CHANNEL_NONBLOCKING) && + (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) && + !(statePtr->flags & CHANNEL_HAS_MORE_DATA)) { + + /* We bypass the driver, it would block, as no data is available */ nread = -1; result = EWOULDBLOCK; } else { @@ -4266,6 +4276,12 @@ Tcl_ReadRaw(chan, bufPtr, bytesToRead) if (nread < (bytesToRead - copied)) { statePtr->flags |= CHANNEL_BLOCKED; } + + if (nread <= (bytesToRead - copied)) { + /* [SF Tcl Bug 943274] We have read the available + * data, clear flag */ + statePtr->flags &= ~CHANNEL_HAS_MORE_DATA; + } } else if (nread == 0) { statePtr->flags |= CHANNEL_EOF; statePtr->inputEncodingFlags |= TCL_ENCODING_END; @@ -4390,7 +4406,7 @@ DoReadChars(chanPtr, objPtr, toRead, appendFlag) int offset, factor, copied, copiedNow, result; Tcl_Encoding encoding; #define UTF_EXPANSION_FACTOR 1024 - + /* * This operation should occur at the top of a channel stack. */ @@ -5307,8 +5323,17 @@ GetInput(chanPtr) return 0; } - if ((statePtr->flags & CHANNEL_TIMER_FEV) && - (statePtr->flags & CHANNEL_NONBLOCKING)) { + /* [SF Tcl Bug 943274]. Better emulation of non-blocking channels + * for channels without BlockModeProc, by keeping track of true + * fileevents generated by the OS == Data waiting and reading if + * and only if we are sure to have data. + */ + + if ((statePtr->flags & CHANNEL_NONBLOCKING) && + (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) && + !(statePtr->flags & CHANNEL_HAS_MORE_DATA)) { + + /* Bypass the driver, it would block, as no data is available */ nread = -1; result = EWOULDBLOCK; } else { @@ -5329,6 +5354,13 @@ GetInput(chanPtr) if (nread < toRead) { statePtr->flags |= CHANNEL_BLOCKED; } + + if (nread <= toRead) { + /* [SF Tcl Bug 943274] We have read the available data, + * clear flag */ + statePtr->flags &= ~CHANNEL_HAS_MORE_DATA; + } + } else if (nread == 0) { statePtr->flags |= CHANNEL_EOF; statePtr->inputEncodingFlags |= TCL_ENCODING_END; @@ -6729,6 +6761,20 @@ Tcl_NotifyChannel(channel, mask) Channel* upChanPtr; Tcl_ChannelType* upTypePtr; + /* [SF Tcl Bug 943274] + * For a non-blocking channel without blockmodeproc we keep track + * of actual input coming from the OS so that we can do a credible + * imitation of non-blocking behaviour. + */ + + if ((mask & TCL_READABLE) && + (statePtr->flags & CHANNEL_NONBLOCKING) && + (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) && + !(statePtr->flags & CHANNEL_TIMER_FEV)) { + + statePtr->flags |= CHANNEL_HAS_MORE_DATA; + } + /* * In contrast to the other API functions this procedure walks towards * the top of a stack and not down from it. @@ -9185,3 +9231,40 @@ Tcl_ChannelWideSeekProc(chanTypePtr) return NULL; } } + +#if 0 +/* For future debugging work, a simple function to print the flags of + * a channel in semi-readable form. + */ + +static int +DumpFlags (str, flags) + char* str; + int flags; +{ + char buf [20]; + int i = 0; + + if (flags & TCL_READABLE) {buf[i] = 'r';} else {buf [i]='_';}; i++; + if (flags & TCL_WRITABLE) {buf[i] = 'w';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_NONBLOCKING) {buf[i] = 'n';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_LINEBUFFERED) {buf[i] = 'l';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_UNBUFFERED) {buf[i] = 'u';} else {buf [i]='_';}; i++; + if (flags & BUFFER_READY) {buf[i] = 'R';} else {buf [i]='_';}; i++; + if (flags & BG_FLUSH_SCHEDULED) {buf[i] = 'F';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_CLOSED) {buf[i] = 'c';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_EOF) {buf[i] = 'E';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_STICKY_EOF) {buf[i] = 'S';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_BLOCKED) {buf[i] = 'B';} else {buf [i]='_';}; i++; + if (flags & INPUT_SAW_CR) {buf[i] = '/';} else {buf [i]='_';}; i++; + if (flags & INPUT_NEED_NL) {buf[i] = '*';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_DEAD) {buf[i] = 'D';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_RAW_MODE) {buf[i] = 'R';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_TIMER_FEV) {buf[i] = 'T';} else {buf [i]='_';}; i++; + if (flags & CHANNEL_HAS_MORE_DATA) {buf[i] = 'H';} else {buf [i]='_';}; i++; + buf [i] ='\0'; + + fprintf (stderr,"%s: %s\n", str, buf); fflush(stderr); + return 0; +} +#endif diff --git a/generic/tclIO.h b/generic/tclIO.h index 9a9c826..a4eefd3 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.5 2002/01/15 17:55:30 dgp Exp $ + * RCS: @(#) $Id: tclIO.h,v 1.6 2004/05/19 19:41:10 andreas_kupries Exp $ */ /* @@ -307,6 +307,22 @@ typedef struct ChannelState { * routines will simulate a * short read (0 characters * read) */ +#define CHANNEL_HAS_MORE_DATA (1<<18) /* Set by NotifyChannel for a + * channel if and only if the + * channel is configured + * non-blocking, the driver + * for said channel has no + * blockmodeproc, and data has + * arrived for reading at the + * OS level). A GetInput will + * pass reading from the + * driver if the channel is + * non-blocking, without + * blockmode proc and the flag + * has not been set. A read + * will be performed if the + * flag is set. This will + * reset the flag as well. */ /* * For each channel handler registered in a call to Tcl_CreateChannelHandler, |