summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2012-02-02 00:11:29 -0500
committerRich Felker <dalias@aerifal.cx>2012-02-02 00:11:29 -0500
commit58165923890865a6ac042fafce13f440ee986fd9 (patch)
tree1a2e608393566ba3184e95f224cbd5a538e275a2
parent4948a24df21c1e80bedc1f302547c9cb26e4dbfe (diff)
downloadmusl-58165923890865a6ac042fafce13f440ee986fd9.tar.gz
make stdio open, read, and write operations cancellation points
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.
-rw-r--r--src/stdio/__stdio_read.c11
-rw-r--r--src/stdio/__stdio_write.c20
-rw-r--r--src/stdio/fopen.c2
3 files changed, 28 insertions, 5 deletions
diff --git a/src/stdio/__stdio_read.c b/src/stdio/__stdio_read.c
index 218bd88d..ee17a576 100644
--- a/src/stdio/__stdio_read.c
+++ b/src/stdio/__stdio_read.c
@@ -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;
diff --git a/src/stdio/__stdio_write.c b/src/stdio/__stdio_write.c
index 63d9c858..dd97cf68 100644
--- a/src/stdio/__stdio_write.c
+++ b/src/stdio/__stdio_write.c
@@ -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;
diff --git a/src/stdio/fopen.c b/src/stdio/fopen.c
index 469de6f0..084cc73c 100644
--- a/src/stdio/fopen.c
+++ b/src/stdio/fopen.c
@@ -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);