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.
44 lines
1,019 B
C
44 lines
1,019 B
C
#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)
|
|
{
|
|
struct iovec iovs[2] = {
|
|
{ .iov_base = f->wbase, .iov_len = f->wpos-f->wbase },
|
|
{ .iov_base = (void *)buf, .iov_len = len }
|
|
};
|
|
struct iovec *iov = iovs;
|
|
size_t rem = iov[0].iov_len + iov[1].iov_len;
|
|
int iovcnt = 2;
|
|
ssize_t cnt;
|
|
for (;;) {
|
|
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;
|
|
return iovcnt == 2 ? 0 : len-iov[0].iov_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;
|
|
}
|
|
}
|