Tcl Source Code

Artifact [9e361252be]
Login

Artifact 9e361252be5611c2a418afa8c7e31502fd5ffc58:

Attachment "refchan-read-eagain-85.patch" to ticket [2827000fff] added by andreas_kupries 2009-08-07 05:25:24.
Index: doc/refchan.n
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/refchan.n,v
retrieving revision 1.11
diff -w -u -r1.11 refchan.n
--- doc/refchan.n	26 Mar 2008 09:59:22 -0000	1.11
+++ doc/refchan.n	6 Aug 2009 21:25:35 -0000
@@ -116,7 +116,36 @@
 performed the read (usually \fBgets\fR or \fBread\fR). However,
 returning fewer bytes than requested is acceptable.
 .PP
-If the subcommand throws an error, the command which caused its
+Note that returning nothing (0 bytes) is a signal to the higher layers
+that \fBEOF\fR has been reached on the channel. To signal that the
+channel is out of data right now, but has not yet reached \fBEOF\fR,
+it is necessary to throw the error "EAGAIN", i.e. to either
+.PP
+.CS
+return -code error EAGAIN
+.CE
+or
+.CS
+error EAGAIN
+.CE
+.PP
+For extensibility any error whose value is a negative integer number
+will cause the higher layers to set the C-level variable "\fBerrno\fR"
+to the absolute value of this number, signaling a system error. This
+means that both
+.PP
+.CS
+return -code error -11
+.CE
+and
+.CS
+error -11
+.CE
+.PP
+are equivalent to the examples above, using the more readable string "EAGAIN".
+No other error value has such a mapping to a symbolic string.
+.PP
+If the subcommand throws any other error, the command which caused its
 invocation (usually \fBgets\fR, or \fBread\fR) will appear to have
 thrown this error. Any exception beyond \fIerror\fR, (e.g.
 \fIbreak\fR, etc.) is treated as and converted to an error.
Index: generic/tclIORChan.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIORChan.c,v
retrieving revision 1.28.2.6
diff -w -u -r1.28.2.6 tclIORChan.c
--- generic/tclIORChan.c	22 Jan 2009 00:05:14 -0000	1.28.2.6
+++ generic/tclIORChan.c	6 Aug 2009 21:25:36 -0000
@@ -447,6 +447,7 @@
 static ReflectedChannelMap *	GetReflectedChannelMap(Tcl_Interp *interp);
 static void		DeleteReflectedChannelMap(ClientData clientData,
 			    Tcl_Interp *interp);
+static int              ErrnoReturn(ReflectedChannel *rcPtr, Tcl_Obj* resObj);
 
 /*
  * Global constant strings (messages). ==================
@@ -1218,8 +1219,13 @@
 	ForwardOpToOwnerThread(rcPtr, ForwardedInput, &p);
 
 	if (p.base.code != TCL_OK) {
+	    if (p.base.code < 0) {
+		/* No error message, this is an errno signal. */
+		*errorCodePtr = -p.base.code;
+	    } else {
 	    PassReceivedError(rcPtr->chan, &p);
 	    *errorCodePtr = EINVAL;
+	    }
 	    p.input.toRead = -1;
 	} else {
 	    *errorCodePtr = EOK;
@@ -1234,6 +1240,14 @@
 
     toReadObj = Tcl_NewIntObj(toRead);
     if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK) {
+	int code = ErrnoReturn (rcPtr, resObj);
+
+	if (code < 0) {
+	    Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
+	    *errorCodePtr = -code;
+	    return -1;
+	}
+
 	Tcl_SetChannelError(rcPtr->chan, resObj);
 	Tcl_DecrRefCount(resObj);	/* Remove reference held from invoke */
 	*errorCodePtr = EINVAL;
@@ -2266,6 +2280,53 @@
 /*
  *----------------------------------------------------------------------
  *
+ * ErrnoReturn --
+ *
+ *	Checks a method error result if it returned an 'errno'.
+ *
+ * Results:
+ *	The negative errno found in the error result, or 0.
+ *
+ * Side effects:
+ *	None.
+ *
+ * Users:
+ *	Currently only ReflectInput(), to enable the signaling of EAGAIN.
+ *	by non-blocking channels at buffer-empty, but not EOF.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ErrnoReturn(ReflectedChannel *rcPtr, Tcl_Obj* resObj)
+{
+    int code;
+    Tcl_InterpState sr;		/* State of handler interp */
+
+    if (!rcPtr->interp) {
+	return 0;
+    }
+
+    sr = Tcl_SaveInterpState(rcPtr->interp, 0 /* Dummy */);
+    UnmarshallErrorResult(rcPtr->interp, resObj);
+
+    resObj = Tcl_GetObjResult(rcPtr->interp);
+
+    if (((Tcl_GetIntFromObj(rcPtr->interp, resObj, &code) != TCL_OK) || (code >= 0))) {
+	if (strcmp ("EAGAIN",Tcl_GetString(resObj)) == 0) {
+	    code = -11;
+	} else {
+	    code = 0;
+	}
+    }
+
+    Tcl_RestoreInterpState(rcPtr->interp, sr);
+    return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * GetReflectedChannelMap --
  *
  *	Gets and potentially initializes the reflected channel map for an
@@ -2749,7 +2810,13 @@
 	Tcl_Obj *toReadObj = Tcl_NewIntObj(paramPtr->input.toRead);
 
 	if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK){
+	    int code = ErrnoReturn (rcPtr, resObj);
+
+	    if (code < 0) {
+		paramPtr->base.code = code;
+	    } else {
 	    ForwardSetObjError(paramPtr, resObj);
+	    }
 	    paramPtr->input.toRead = -1;
 	} else {
 	    /*
Index: tests/ioCmd.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/ioCmd.test,v
retrieving revision 1.36.2.5
diff -w -u -r1.36.2.5 ioCmd.test
--- tests/ioCmd.test	24 Apr 2008 18:50:42 -0000	1.36.2.5
+++ tests/ioCmd.test	6 Aug 2009 21:25:36 -0000
@@ -980,6 +980,38 @@
     rename foo {}
     set res
 } -result {{read rc* 4096} 1 *bad code* {-code 1 -level 0 -errorcode NONE -errorline 1 -errorinfo *bad code*subcommand "read"*}}
+test iocmd-23.9 {chan read, no data means eof} -match glob -setup {
+    set res {}
+    proc foo {args} {
+	oninit; onfinal; track
+	return ""
+    }
+    set c [chan create {r w} foo]
+} -body {
+    note [read $c 2]
+    note [eof $c]
+    set res
+} -cleanup {
+    close $c
+    rename foo {}
+    unset res
+} -result {{read rc* 4096} {} 1}
+test iocmd-23.10 {chan read, EAGAIN means no data, yet no eof either} -match glob -setup {
+    set res {}
+    proc foo {args} {
+	oninit; onfinal; track
+	error EAGAIN
+    }
+    set c [chan create {r w} foo]
+} -body {
+    note [read $c 2]
+    note [eof $c]
+    set res
+} -cleanup {
+    close $c
+    rename foo {}
+    unset res
+} -result {{read rc* 4096} {} 0}
 
 # --- === *** ###########################
 # method write
@@ -2211,6 +2243,46 @@
     set res
 } -result {{read rc* 4096} 1 *bad code* {-code 1 -level 0 -errorcode NONE -errorline 1 -errorinfo *bad code*subcommand "read"*}} \
     -constraints {testchannel testthread}
+test iocmd.tf-23.9 {chan read, no data means eof} -match glob -setup {
+    set res {}
+    proc foo {args} {
+	oninit; onfinal; track
+	return ""
+    }
+    set c [chan create {r w} foo]
+} -body {
+    notes [inthread $c {
+	note [read $c 2]
+	note [eof $c]
+	close $c
+	notes
+    } c]
+    set res
+} -cleanup {
+    rename foo {}
+    unset res
+} -result {{read rc* 4096} {} 1} \
+    -constraints {testchannel testthread}
+test iocmd.tf-23.10 {chan read, EAGAIN means no data, yet no eof either} -match glob -setup {
+    set res {}
+    proc foo {args} {
+	oninit; onfinal; track
+	error EAGAIN
+    }
+    set c [chan create {r w} foo]
+} -body {
+    notes [inthread $c {
+	note [read $c 2]
+	note [eof $c]
+	close $c
+	notes
+    } c]
+    set res
+} -cleanup {
+    rename foo {}
+    unset res
+} -result {{read rc* 4096} {} 0} \
+    -constraints {testchannel testthread}
 
 # --- === *** ###########################
 # method write