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