aboutsummaryrefslogtreecommitdiff
path: root/lib/libpthread/pthreads/fd.c
diff options
context:
space:
mode:
authorsvn2git <svn2git@FreeBSD.org>1994-05-01 08:00:00 +0000
committersvn2git <svn2git@FreeBSD.org>1994-05-01 08:00:00 +0000
commita16f65c7d117419bd266c28a1901ef129a337569 (patch)
tree2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /lib/libpthread/pthreads/fd.c
parent8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff)
Diffstat (limited to 'lib/libpthread/pthreads/fd.c')
-rw-r--r--lib/libpthread/pthreads/fd.c558
1 files changed, 558 insertions, 0 deletions
diff --git a/lib/libpthread/pthreads/fd.c b/lib/libpthread/pthreads/fd.c
new file mode 100644
index 000000000000..20f1b0105859
--- /dev/null
+++ b/lib/libpthread/pthreads/fd.c
@@ -0,0 +1,558 @@
+/* ==== fd.c ============================================================
+ * Copyright (c) 1993, 1994 by Chris Provenzano, proven@mit.edu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Chris Provenzano.
+ * 4. The name of Chris Provenzano may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CHRIS PROVENZANO ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CHRIS PROVENZANO BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Description : All the syscalls dealing with fds.
+ *
+ * 1.00 93/08/14 proven
+ * -Started coding this file.
+ *
+ * 1.01 93/11/13 proven
+ * -The functions readv() and writev() added.
+ */
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread/posix.h>
+
+/*
+ * These first functions really should not be called by the user.
+ *
+ * I really should dynamically figure out what the table size is.
+ */
+int dtablesize = 64;
+static struct fd_table_entry fd_entry[64];
+
+/* ==========================================================================
+ * fd_init()
+ */
+void fd_init(void)
+{
+ int i;
+
+ for (i = 0; i < dtablesize; i++) {
+ fd_table[i] = &fd_entry[i];
+
+ fd_table[i]->ops = NULL;
+ fd_table[i]->type = FD_NT;
+ fd_table[i]->fd.i = NOTOK;
+ fd_table[i]->flags = 0;
+ fd_table[i]->count = 0;
+
+ pthread_queue_init(&(fd_table[i]->r_queue));
+ pthread_queue_init(&(fd_table[i]->w_queue));
+
+ fd_table[i]->r_owner = NULL;
+ fd_table[i]->w_owner = NULL;
+ fd_table[i]->lock = SEMAPHORE_CLEAR;
+ fd_table[i]->next = NULL;
+ fd_table[i]->lockcount = 0;
+ }
+
+ /* Currently only initialize first 3 fds. */
+ fd_kern_init(0);
+ fd_kern_init(1);
+ fd_kern_init(2);
+ printf ("Warning: threaded process may have changed open file descriptors\n");
+}
+
+/* ==========================================================================
+ * fd_allocate()
+ */
+int fd_allocate()
+{
+ semaphore *lock;
+ int i;
+
+ for (i = 0; i < dtablesize; i++) {
+ lock = &(fd_table[i]->lock);
+ while (SEMAPHORE_TEST_AND_SET(lock)) {
+ continue;
+ }
+ if (fd_table[i]->count || fd_table[i]->r_owner
+ || fd_table[i]->w_owner) {
+ SEMAPHORE_RESET(lock);
+ continue;
+ }
+ if (fd_table[i]->type == FD_NT) {
+ /* Test to see if the kernel version is in use */
+ /* If so continue; */
+ }
+ fd_table[i]->count++;
+ SEMAPHORE_RESET(lock);
+ return(i);
+ }
+ pthread_run->error = ENFILE;
+ return(NOTOK);
+}
+
+/* ==========================================================================
+ * fd_free()
+ *
+ * Assumes fd is locked and owner by pthread_run
+ * Don't clear the queues, fd_unlock will do that.
+ */
+int fd_free(int fd)
+{
+ struct fd_table_entry *fd_valid;
+ int ret;
+
+ if (ret = --fd_table[fd]->count) {
+ /* Separate pthread queue into two distinct queues. */
+ fd_valid = fd_table[fd];
+ fd_table[fd] = fd_table[fd]->next;
+ fd_valid->next = fd_table[fd]->next;
+ }
+
+ fd_table[fd]->type = FD_NIU;
+ fd_table[fd]->fd.i = NOTOK;
+ fd_table[fd]->next = NULL;
+ fd_table[fd]->flags = 0;
+ fd_table[fd]->count = 0;
+ return(ret);
+}
+
+/* ==========================================================================
+ * fd_basic_unlock()
+ *
+ * The real work of unlock without the locking of fd_table[fd].lock.
+ */
+void fd_basic_unlock(int fd, int lock_type)
+{
+ struct pthread *pthread;
+ semaphore *plock;
+
+ if (fd_table[fd]->r_owner == pthread_run) {
+ if (pthread = pthread_queue_get(&fd_table[fd]->r_queue)) {
+
+ plock = &(pthread->lock);
+ while (SEMAPHORE_TEST_AND_SET(plock)) {
+ pthread_yield();
+ }
+ pthread_queue_deq(&fd_table[fd]->r_queue);
+ fd_table[fd]->r_owner = pthread;
+ pthread->state = PS_RUNNING;
+ SEMAPHORE_RESET(plock);
+ } else {
+ fd_table[fd]->r_owner = NULL;
+ }
+ }
+
+ if (fd_table[fd]->w_owner == pthread_run) {
+ if (pthread = pthread_queue_get(&fd_table[fd]->w_queue)) {
+ plock = &(pthread->lock);
+ while (SEMAPHORE_TEST_AND_SET(plock)) {
+ pthread_yield();
+ }
+ pthread_queue_deq(&fd_table[fd]->r_queue);
+ fd_table[fd]->w_owner = pthread;
+ pthread->state = PS_RUNNING;
+ SEMAPHORE_RESET(plock);
+ } else {
+ fd_table[fd]->w_owner = NULL;
+ }
+ }
+}
+
+/* ==========================================================================
+ * fd_unlock()
+ * If there is a lock count then the function fileunlock will do
+ * the unlocking, just return.
+ */
+void fd_unlock(int fd, int lock_type)
+{
+ semaphore *lock;
+
+ if (!(fd_table[fd]->lockcount)) {
+ lock = &(fd_table[fd]->lock);
+ while (SEMAPHORE_TEST_AND_SET(lock)) {
+ pthread_yield();
+ }
+ fd_basic_unlock(fd, lock_type);
+ SEMAPHORE_RESET(lock);
+ }
+}
+
+/* ==========================================================================
+ * fd_basic_lock()
+ *
+ * The real work of lock without the locking of fd_table[fd].lock.
+ * Be sure to leave the lock the same way you found it. i.e. locked.
+ */
+int fd_basic_lock(unsigned int fd, int lock_type, semaphore * lock)
+{
+ semaphore *plock;
+
+ /* If not in use return EBADF error */
+ if (fd_table[fd]->type == FD_NIU) {
+ return(NOTOK);
+ }
+
+ /* If not tested, test it and see if it is valid */
+ if (fd_table[fd]->type == FD_NT) {
+ /* If not ok return EBADF error */
+ if (fd_kern_init(fd) != OK) {
+ return(NOTOK);
+ }
+ }
+ if ((fd_table[fd]->type == FD_HALF_DUPLEX) ||
+ (lock_type & FD_READ)) {
+ if (fd_table[fd]->r_owner) {
+ if (fd_table[fd]->r_owner != pthread_run) {
+ plock = &(pthread_run->lock);
+ while (SEMAPHORE_TEST_AND_SET(plock)) {
+ pthread_yield();
+ }
+ pthread_queue_enq(&fd_table[fd]->r_queue, pthread_run);
+ SEMAPHORE_RESET(lock);
+
+ /* Reschedule will unlock pthread_run */
+ reschedule(PS_FDLR_WAIT);
+
+ while(SEMAPHORE_TEST_AND_SET(lock)) {
+ pthread_yield();
+ }
+ } else {
+ if (!fd_table[fd]->lockcount) {
+ PANIC();
+ }
+ }
+ }
+ fd_table[fd]->r_owner = pthread_run;
+ }
+ if ((fd_table[fd]->type != FD_HALF_DUPLEX) &&
+ (lock_type & FD_WRITE)) {
+ if (fd_table[fd]->w_owner) {
+ if (fd_table[fd]->w_owner != pthread_run) {
+ plock = &(pthread_run->lock);
+ while (SEMAPHORE_TEST_AND_SET(plock)) {
+ pthread_yield();
+ }
+ pthread_queue_enq(&fd_table[fd]->w_queue, pthread_run);
+ SEMAPHORE_RESET(lock);
+
+ /* Reschedule will unlock pthread_run */
+ reschedule(PS_FDLW_WAIT);
+
+ while(SEMAPHORE_TEST_AND_SET(lock)) {
+ pthread_yield();
+ }
+ }
+ }
+ fd_table[fd]->w_owner = pthread_run;
+ }
+ if (!fd_table[fd]->count) {
+ fd_basic_unlock(fd, lock_type);
+ return(NOTOK);
+ }
+ return(OK);
+}
+
+/* ==========================================================================
+ * fd_lock()
+ */
+int fd_lock(unsigned int fd, int lock_type)
+{
+ semaphore *lock;
+ int error;
+
+ if (fd < dtablesize) {
+ lock = &(fd_table[fd]->lock);
+ while (SEMAPHORE_TEST_AND_SET(lock)) {
+ pthread_yield();
+ }
+ error = fd_basic_lock(fd, lock_type, lock);
+ SEMAPHORE_RESET(lock);
+ return(error);
+ }
+ return(NOTOK);
+}
+
+
+/* ==========================================================================
+ * ======================================================================= */
+
+/* ==========================================================================
+ * read()
+ */
+ssize_t read(int fd, void *buf, size_t nbytes)
+{
+ int ret;
+
+ if ((ret = fd_lock(fd, FD_READ)) == OK) {
+ ret = fd_table[fd]->ops->read(fd_table[fd]->fd,
+ fd_table[fd]->flags, buf, nbytes);
+ fd_unlock(fd, FD_READ);
+ }
+ return(ret);
+}
+
+/* ==========================================================================
+ * readv()
+ */
+int readv(int fd, const struct iovec *iov, int iovcnt)
+{
+ int ret;
+
+ if ((ret = fd_lock(fd, FD_READ)) == OK) {
+ ret = fd_table[fd]->ops->readv(fd_table[fd]->fd,
+ fd_table[fd]->flags, iov, iovcnt);
+ fd_unlock(fd, FD_READ);
+ }
+ return(ret);
+}
+
+/* ==========================================================================
+ * write()
+ */
+ssize_t write(int fd, const void *buf, size_t nbytes)
+{
+ int ret;
+
+ if ((ret = fd_lock(fd, FD_WRITE)) == OK) {
+ ret = fd_table[fd]->ops->write(fd_table[fd]->fd,
+ fd_table[fd]->flags, buf, nbytes);
+ fd_unlock(fd, FD_WRITE);
+ }
+ return(ret);
+}
+
+/* ==========================================================================
+ * writev()
+ */
+int writev(int fd, const struct iovec *iov, int iovcnt)
+{
+ int ret;
+
+ if ((ret = fd_lock(fd, FD_WRITE)) == OK) {
+ ret = fd_table[fd]->ops->writev(fd_table[fd]->fd,
+ fd_table[fd]->flags, iov, iovcnt);
+ fd_unlock(fd, FD_WRITE);
+ }
+ return(ret);
+}
+
+/* ==========================================================================
+ * lseek()
+ */
+off_t lseek(int fd, off_t offset, int whence)
+{
+ int ret;
+
+ if ((ret = fd_lock(fd, FD_RDWR)) == OK) {
+ ret = fd_table[fd]->ops->seek(fd_table[fd]->fd,
+ fd_table[fd]->flags, offset, whence);
+ fd_unlock(fd, FD_RDWR);
+ }
+ return(ret);
+}
+
+/* ==========================================================================
+ * close()
+ *
+ * The whole close procedure is a bit odd and needs a bit of a rethink.
+ * For now close() locks the fd, calls fd_free() which checks to see if
+ * there are any other fd values poinging to the same real fd. If so
+ * It breaks the wait queue into two sections those that are waiting on fd
+ * and those waiting on other fd's. Those that are waiting on fd are connected
+ * to the fd_table[fd] queue, and the count is set to zero, (BUT THE LOCK IS NOT
+ * RELEASED). close() then calls fd_unlock which give the fd to the next queued
+ * element which determins that the fd is closed and then calls fd_unlock etc...
+ */
+int close(int fd)
+{
+ union fd_data realfd;
+ int ret, flags;
+
+ if ((ret = fd_lock(fd, FD_RDWR)) == OK) {
+ flags = fd_table[fd]->flags;
+ realfd = fd_table[fd]->fd;
+ if (fd_free(fd) == OK) {
+ ret = fd_table[fd]->ops->close(realfd, flags);
+ }
+ fd_unlock(fd, FD_RDWR);
+ }
+ return(ret);
+}
+
+/* ==========================================================================
+ * fd_basic_dup()
+ *
+ * Might need to do more than just what's below.
+ */
+static inline void fd_basic_dup(int fd, int newfd)
+{
+ fd_table[newfd]->next = fd_table[fd]->next;
+ fd_table[fd]->next = fd_table[newfd];
+ fd_table[fd]->count++;
+}
+
+/* ==========================================================================
+ * dup2()
+ *
+ * Always lock the lower number fd first to avoid deadlocks.
+ * newfd must be locked by hand so it can be closed if it is open,
+ * or it won't be opened while dup is in progress.
+ */
+int dup2(fd, newfd)
+{
+ union fd_data realfd;
+ semaphore *lock;
+ int ret, flags;
+
+ if (newfd < dtablesize) {
+ if (fd < newfd) {
+ if ((ret = fd_lock(fd, FD_RDWR)) == OK) {
+ /* Need to lock the newfd by hand */
+ lock = &(fd_table[newfd]->lock);
+ while(SEMAPHORE_TEST_AND_SET(lock)) {
+ pthread_yield();
+ }
+
+ /* Is it inuse */
+ if (fd_basic_lock(newfd, FD_RDWR, lock) == OK) {
+ /* free it and check close status */
+ flags = fd_table[fd]->flags;
+ realfd = fd_table[fd]->fd;
+ if (fd_free(fd) == OK) {
+ ret = fd_table[fd]->ops->close(realfd, flags);
+ } else {
+ /* Lots of work to do */
+ }
+ }
+ fd_basic_dup(fd, newfd);
+ }
+ fd_unlock(fd, FD_RDWR);
+ } else {
+ /* Need to lock the newfd by hand */
+ lock = &(fd_table[newfd]->lock);
+ while(SEMAPHORE_TEST_AND_SET(lock)) {
+ pthread_yield();
+ }
+ if ((ret = fd_lock(fd, FD_RDWR)) == OK) {
+ }
+ /* Is it inuse */
+ if ((ret = fd_basic_lock(newfd, FD_RDWR, lock)) == OK) {
+ /* free it and check close status */
+ flags = fd_table[fd]->flags;
+ realfd = fd_table[fd]->fd;
+ if (fd_free(fd) == OK) {
+ ret = fd_table[fd]->ops->close(realfd, flags);
+ } else {
+ /* Lots of work to do */
+ }
+
+ fd_basic_dup(fd, newfd);
+ fd_unlock(fd, FD_RDWR);
+ }
+ SEMAPHORE_RESET(lock);
+ }
+ } else {
+ ret = NOTOK;
+ }
+ return(ret);
+
+}
+
+/* ==========================================================================
+ * dup()
+ */
+int dup(int fd)
+{
+ int ret;
+
+ if ((ret = fd_lock(fd, FD_RDWR)) == OK) {
+ ret = fd_allocate();
+ fd_basic_dup(fd, ret);
+ fd_unlock(fd, FD_RDWR);
+ }
+ return(ret);
+}
+
+/* ==========================================================================
+ * fcntl()
+ */
+int fcntl(int fd, int cmd, ...)
+{
+ int ret, realfd, flags;
+ struct flock *flock;
+ semaphore *plock;
+ va_list ap;
+
+ flags = 0;
+ if ((ret = fd_lock(fd, FD_RDWR)) == OK) {
+ va_start(ap, cmd);
+ switch(cmd) {
+ case F_DUPFD:
+ ret = fd_allocate();
+ fd_basic_dup(va_arg(ap, int), ret);
+ break;
+ case F_SETFD:
+ flags = va_arg(ap, int);
+ case F_GETFD:
+ ret = fd_table[fd]->ops->fcntl(fd_table[fd]->fd,
+ fd_table[fd]->flags, cmd, flags | __FD_NONBLOCK);
+ break;
+ case F_GETFL:
+ ret = fd_table[fd]->flags;
+ break;
+ case F_SETFL:
+ flags = va_arg(ap, int);
+ if ((ret = fd_table[fd]->ops->fcntl(fd_table[fd]->fd,
+ fd_table[fd]->flags, cmd, flags | __FD_NONBLOCK)) == OK) {
+ fd_table[fd]->flags = flags;
+ }
+ break;
+/* case F_SETLKW: */
+ /*
+ * Do the same as SETLK but if it fails with EACCES or EAGAIN
+ * block the thread and try again later, not implemented yet
+ */
+/* case F_SETLK: */
+/* case F_GETLK:
+ flock = va_arg(ap, struct flock*);
+ ret = fd_table[fd]->ops->fcntl(fd_table[fd]->fd,
+ fd_table[fd]->flags, cmd, flock);
+ break; */
+ default:
+ /* Might want to make va_arg use a union */
+ ret = fd_table[fd]->ops->fcntl(fd_table[fd]->fd,
+ fd_table[fd]->flags, cmd, va_arg(ap, void*));
+ break;
+ }
+ va_end(ap);
+ fd_unlock(fd, FD_RDWR);
+ }
+ return(ret);
+}