/* io-unix.c - non-blocking io primitives for unix * * Copyright (C) 2009 Timo Teräs * All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 or later as * published by the Free Software Foundation. * * See http://www.gnu.org/ for details. */ int tf_open(struct tf_fd *fd, const char *pathname, int flags) { int kfd, r; kfd = open(pathname, flags | O_CLOEXEC | O_NONBLOCK); if (unlikely(kfd < 0)) return -errno; r = tf_fd_init(fd, kfd, TF_FD_AUTOCLOSE | TF_FD_STREAM_ORIENTED); if (r < 0) { close(kfd); return r; } return 0; } int tf_open_fd(struct tf_fd *fd, int kfd) { int mode, flags = 0; mode = fcntl(kfd, F_GETFL, 0); if (!(mode & O_NONBLOCK)) { fcntl(fd->fd, F_SETFL, mode | O_NONBLOCK); flags |= TF_FD_RESTORE_BLOCKING; } return tf_fd_init(fd, kfd, TF_FD_STREAM_ORIENTED | flags); } int tf_close(struct tf_fd *fd) { int r; if (fd->flags & TF_FD_RESTORE_BLOCKING) { fcntl(fd->fd, F_SETFL, fcntl(fd->fd, F_GETFL, 0) & ~O_NONBLOCK); } if (fd->flags & TF_FD_AUTOCLOSE) { r = close(fd->fd); if (unlikely(r == -1)) return -errno; } return 0; } int tf_read(struct tf_fd *fd, void *buf, size_t count, tf_mtime_diff_t timeout) { ssize_t n; int r, mode; mode = tf_schedule_timeout(timeout); tf_fd_wait(fd, EPOLLIN); do { n = read(fd->fd, buf, count); if (n == count) { r = 0; break; } if (n < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { r = errno; break; } } else if (n == 0) { r = EIO; break; } else { buf += n; count -= n; if (!(fd->flags & TF_FD_STREAM_ORIENTED)) continue; } r = tf_schedule(mode); if (r != TF_WAKEUP_FD) break; } while (1); tf_fd_release(fd); return -r; } int tf_write(struct tf_fd *fd, const void *buf, size_t count, tf_mtime_diff_t timeout) { ssize_t n; int r, mode; mode = tf_schedule_timeout(timeout); tf_fd_wait(fd, EPOLLOUT); do { n = write(fd->fd, buf, count); if (n == count) { r = 0; break; } if (n < 0) { if (errno == EINTR) continue; if (errno != EAGAIN) { r = errno; break; } } else { buf += n; count -= n; if (!(fd->flags & TF_FD_STREAM_ORIENTED)) continue; } r = tf_schedule(mode); if (r != TF_WAKEUP_FD) break; } while (1); tf_fd_release(fd); return -r; }