[#444] io_write()/fwrite() and EINTR on Solaris — Jos Backus <jos@...>

I am encountering a problem similar to the one mentioned here,

19 messages 2002/09/06
[#453] Re: io_write()/fwrite() and EINTR on Solaris — nobu.nokada@... 2002/09/08

Hi,

[#454] Re: io_write()/fwrite() and EINTR on Solaris — matz@... (Yukihiro Matsumoto) 2002/09/09

Hi

[#469] Re: io_write()/fwrite() and EINTR on Solaris — Jos Backus <jos@...> 2002/09/09

On Mon, Sep 09, 2002 at 03:55:13PM +0900, Yukihiro Matsumoto wrote:

[#479] Re: io_write()/fwrite() and EINTR on Solaris — Jos Backus <jos@...> 2002/09/10

On Tue, Sep 10, 2002 at 01:04:10AM +0900, Jos Backus wrote:

[#492] Re: io_write()/fwrite() and EINTR on Solaris — Jos Backus <jos@...> 2002/09/21

On Wed, Sep 11, 2002 at 02:23:33AM +0900, Jos Backus wrote:

Re: io_write()/fwrite() and EINTR on Solaris

From: nobu.nokada@...
Date: 2002-09-08 23:33:56 UTC
List: ruby-core #453
Hi,

At Fri, 6 Sep 2002 12:29:51 +0900,
Jos Backus wrote:
> I am encountering a problem similar to the one mentioned here,
> 
> http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&th=16d3eb9718c73a88&rnum=1
> http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&th=ec398d145e0e36c1&rnum=2
> 
> except that mine happens with the fwrite() call in io_write(). A script using
> two threads occasionally sees an exception being raised when calling IO#puts.
> The code in io.c looks like this:
> 
>     n = fwrite(RSTRING(str)->ptr, 1, RSTRING(str)->len, f);
>     if (n != RSTRING(str)->len && ferror(f)) {
>         rb_sys_fail(fptr->path);
>     }
> 
> How do I fix this? I don't suppose it's possible to wrap the fwrite() call
> with TRAP_BEG/ TRAP_END, and if ferror(f) and errno == EINTR or ERESTART, just
> ignore the result and skip raising the exception? What do I return from
> io_write() in this case?

Once I made a patch for non-blocking IO, which contained
retrying fwrite().  And now added ERESTART condition.

Does this help you?


Index: io.c
===================================================================
RCS file: /cvs/ruby/src/ruby/io.c,v
retrieving revision 1.156
diff -u -2 -p -r1.156 io.c
--- io.c	6 Sep 2002 01:58:32 -0000	1.156
+++ io.c	8 Sep 2002 23:30:18 -0000
@@ -262,4 +262,24 @@ io_fflush(f, fptr)
 }
 
+void
+rb_io_wait_readable(f)
+    int f;
+{
+    fd_set rfds;
+    FD_ZERO(&rfds);
+    FD_SET(f, &rfds);
+    rb_thread_select(f + 1, &rfds, NULL, NULL, NULL);
+}
+
+void
+rb_io_wait_writable(f)
+    int f;
+{
+    fd_set wfds;
+    FD_ZERO(&wfds);
+    FD_SET(f, &wfds);
+    rb_thread_select(f + 1, NULL, &wfds, NULL, NULL);
+}
+
 /* writing functions */
 static VALUE
@@ -269,5 +289,6 @@ io_write(io, str)
     OpenFile *fptr;
     FILE *f;
-    long n;
+    long n, r;
+    register char *ptr;
 
     rb_secure(4);
@@ -285,21 +306,38 @@ io_write(io, str)
     f = GetWriteFile(fptr);
 
+    ptr = RSTRING(str)->ptr;
+    n = RSTRING(str)->len;
+    do {
 #ifdef __human68k__
-    {
-	register char *ptr = RSTRING(str)->ptr;
-	n = RSTRING(str)->len;
-	while (--n >= 0)
-	    if (fputc(*ptr++, f) == EOF)
-		break;
-	n = ptr - RSTRING(str)->ptr;
-    }
-    if (n != RSTRING(str)->len && ferror(f))
-	rb_sys_fail(fptr->path);
+	if (fputc(*ptr++, f) == EOF) {
+	    if (ferror(f)) rb_sys_fail(fptr->path);
+	    break;
+	}
+	--n;
 #else
-    n = fwrite(RSTRING(str)->ptr, 1, RSTRING(str)->len, f);
-    if (n != RSTRING(str)->len && ferror(f)) {
-	rb_sys_fail(fptr->path);
-    }
+	r = fwrite(ptr, 1, n, f);
+	ptr += r;
+	n -= r;
+	if (ferror(f)) {
+	    switch (errno) {
+	      case EINTR:
+#if defined(ERESTART)
+	      case ERESTART:
+#endif
+		clearerr(f);
+		continue;
+	      case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+	      case EWOULDBLOCK:
 #endif
+		clearerr(f);
+		rb_io_wait_writable(fileno(f));
+		continue;
+	    }
+	    rb_sys_fail(fptr->path);
+	}
+#endif
+    } while (n > 0);
+    n = ptr - RSTRING(str)->ptr;
     if (fptr->mode & FMODE_SYNC) {
 	io_fflush(f, fptr);
@@ -545,4 +583,28 @@ rb_io_to_io(io)
 
 /* reading functions */
+static void
+io_read_retryable(f, path)
+    FILE *f;
+    const char *path;
+{
+    switch (errno) {
+      case EINTR:
+#if defined(ERESTART)
+      case ERESTART:
+#endif
+	clearerr(f);
+	break;
+      case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+      case EWOULDBLOCK:
+#endif
+	clearerr(f);
+	rb_io_wait_readable(fileno(f));
+	break;
+      default:
+	rb_sys_fail(path);
+	break;
+    }
+}
 
 long
@@ -586,4 +648,5 @@ rb_io_fread(ptr, len, f)
 		switch (errno) {
 		  case EINTR:
+		    clearerr(f);
 		    continue;
 		  case EAGAIN:
@@ -591,4 +654,5 @@ rb_io_fread(ptr, len, f)
 		  case EWOULDBLOCK:
 #endif
+		    clearerr(f);
 		    return len - n;
 		}
@@ -655,4 +719,5 @@ read_all(fptr, siz)
 	if (pos > 0 && n == 0 && bytes == 0) {
 	    if (feof(fptr->f)) return Qnil;
+	    if (!ferror(fptr->f)) return rb_str_new(0, 0);
 	    rb_sys_fail(fptr->path);
 	}
@@ -766,6 +831,6 @@ appendline(fptr, delim, strp)
 	if (c == EOF) {
 	    if (ferror(f)) {
-		if (errno == EINTR) continue;
-		rb_sys_fail(fptr->path);
+		io_read_retryable(f, fptr->path);
+		continue;
 	    }
 	    return c;
@@ -1079,6 +1144,6 @@ rb_io_each_byte(io)
 	if (c == EOF) {
 	    if (ferror(f)) {
-		if (errno == EINTR) continue;
-		rb_sys_fail(fptr->path);
+		io_read_retryable(f, fptr->path);
+		continue;
 	    }
 	    break;
@@ -1110,6 +1175,6 @@ rb_io_getc(io)
     if (c == EOF) {
 	if (ferror(f)) {
-	    if (errno == EINTR) goto retry;
-	    rb_sys_fail(fptr->path);
+	    io_read_retryable(f, fptr->path);
+	    goto retry;
 	}
 	return Qnil;


-- 
Nobu Nakada

In This Thread