From f862c606dc4ecda51a521b2ec46c7c308500af38 Mon Sep 17 00:00:00 2001 From: andreas_kupries Date: Wed, 19 May 2004 19:41:09 +0000 Subject: * 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. --- ChangeLog | 14 +++++++++ generic/tclIO.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- generic/tclIO.h | 18 ++++++++++- 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5a3b426..e634d40 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2004-05-19 Andreas Kupries + + * 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 * 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, -- cgit v0.12