make stdio open, read, and write operations cancellation points
authorRich Felker <dalias@aerifal.cx>
Thu, 2 Feb 2012 05:11:29 +0000 (00:11 -0500)
committerRich Felker <dalias@aerifal.cx>
Thu, 2 Feb 2012 05:11:29 +0000 (00:11 -0500)
it should be noted that only the actual underlying buffer flush and
fill operations are cancellable, not reads from or writes to the
buffer. this behavior is compatible with POSIX, which makes all
cancellation points in stdio optional, and it achieves the goal of
allowing cancellation of a thread that's "stuck" on IO (due to a
non-responsive socket/pipe peer, slow/stuck hardware, etc.) without
imposing any measurable performance cost.

src/stdio/__stdio_read.c
src/stdio/__stdio_write.c
src/stdio/fopen.c

index 218bd88d18df42d908dc121600af2cf242cb9abd..ee17a576c5bb70c66c1def2d9d8f5537d7896059 100644 (file)
@@ -1,4 +1,11 @@
 #include "stdio_impl.h"
+#include <pthread.h>
+
+static void cleanup(void *p)
+{
+       FILE *f = p;
+       if (!f->lockcount) __unlockfile(f);
+}
 
 size_t __stdio_read(FILE *f, unsigned char *buf, size_t len)
 {
@@ -8,7 +15,9 @@ size_t __stdio_read(FILE *f, unsigned char *buf, size_t len)
        };
        ssize_t cnt;
 
-       cnt = syscall(SYS_readv, f->fd, iov, 2);
+       pthread_cleanup_push(cleanup, f);
+       cnt = syscall_cp(SYS_readv, f->fd, iov, 2);
+       pthread_cleanup_pop(0);
        if (cnt <= 0) {
                f->flags |= F_EOF ^ ((F_ERR^F_EOF) & cnt);
                f->rpos = f->rend = 0;
index 63d9c858db371486dcffb75279dbca559c2ca5ce..dd97cf68ac15f08a3c71fc6553418f39185539d4 100644 (file)
@@ -1,4 +1,11 @@
 #include "stdio_impl.h"
+#include <pthread.h>
+
+static void cleanup(void *p)
+{
+       FILE *f = p;
+       if (!f->lockcount) __unlockfile(f);
+}
 
 size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len)
 {
@@ -10,10 +17,14 @@ size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len)
        size_t rem = iov[0].iov_len + iov[1].iov_len;
        int iovcnt = 2;
        ssize_t cnt;
-       f->wpos = f->wbase;
        for (;;) {
-               cnt = syscall(SYS_writev, f->fd, iov, iovcnt);
-               if (cnt == rem) return len;
+               pthread_cleanup_push(cleanup, f);
+               cnt = syscall_cp(SYS_writev, f->fd, iov, iovcnt);
+               pthread_cleanup_pop(0);
+               if (cnt == rem) {
+                       f->wpos = f->wbase = f->buf;
+                       return len;
+               }
                if (cnt < 0) {
                        f->wpos = f->wbase = f->wend = 0;
                        f->flags |= F_ERR;
@@ -21,8 +32,11 @@ size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len)
                }
                rem -= cnt;
                if (cnt > iov[0].iov_len) {
+                       f->wpos = f->wbase = f->buf;
                        cnt -= iov[0].iov_len;
                        iov++; iovcnt--;
+               } else if (iovcnt == 2) {
+                       f->wbase += cnt;
                }
                iov[0].iov_base = (char *)iov[0].iov_base + cnt;
                iov[0].iov_len -= cnt;
index 469de6f0b62a6bf9d494859f186797a8103aa74e..084cc73ce5ab7014821c3774f120c2fbd07d60c3 100644 (file)
@@ -21,7 +21,7 @@ FILE *fopen(const char *filename, const char *mode)
        if (*mode == 'w') flags |= O_TRUNC;
        if (*mode == 'a') flags |= O_APPEND;
 
-       fd = syscall(SYS_open, filename, flags|O_LARGEFILE, 0666);
+       fd = syscall_cp(SYS_open, filename, flags|O_LARGEFILE, 0666);
        if (fd < 0) return 0;
 
        f = __fdopen(fd, mode);