summaryrefslogtreecommitdiff
path: root/lib/libpthread/pthreads/fd.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libpthread/pthreads/fd.c')
-rw-r--r--lib/libpthread/pthreads/fd.c953
1 files changed, 736 insertions, 217 deletions
diff --git a/lib/libpthread/pthreads/fd.c b/lib/libpthread/pthreads/fd.c
index 2302f1d2068..e603c0da0f9 100644
--- a/lib/libpthread/pthreads/fd.c
+++ b/lib/libpthread/pthreads/fd.c
@@ -39,13 +39,24 @@
*/
#ifndef lint
-static const char rcsid[] = "$Id: fd.c,v 1.1 1995/10/18 08:43:04 deraadt Exp $ $provenid: fd.c,v 1.16 1994/02/07 02:18:39 proven Exp $";
+static const char rcsid[] = "$Id: fd.c,v 1.2 1998/07/21 19:48:00 peter Exp $";
#endif
#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <sys/uio.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h> /* For ioctl */
+#endif
+#if __STDC__
#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
#include <fcntl.h>
#include <errno.h>
#include <pthread/posix.h>
@@ -55,8 +66,77 @@ static const char rcsid[] = "$Id: fd.c,v 1.1 1995/10/18 08:43:04 deraadt Exp $ $
*
* I really should dynamically figure out what the table size is.
*/
-int dtablesize = 64;
-static struct fd_table_entry fd_entry[64];
+static pthread_mutex_t fd_table_mutex = PTHREAD_MUTEX_INITIALIZER;
+static const int dtablecount = 4096/sizeof(struct fd_table_entry);
+int dtablesize;
+
+static int fd_get_pthread_fd_from_kernel_fd( int );
+
+/* ==========================================================================
+ * Allocate dtablecount entries at once and populate the fd_table.
+ *
+ * fd_init_entry()
+ */
+int fd_init_entry(int entry)
+{
+ struct fd_table_entry *fd_entry;
+ int i, round;
+
+ if (fd_table[entry] == NULL) {
+ round = entry - entry % dtablecount;
+
+ if ((fd_entry = (struct fd_table_entry *)malloc(
+ sizeof(struct fd_table_entry) * dtablecount)) == NULL) {
+ return(NOTOK);
+ }
+
+ for (i = 0; i < dtablecount && round+i < dtablesize; i++) {
+ fd_table[round + i] = &fd_entry[i];
+
+ fd_table[round + i]->ops = NULL;
+ fd_table[round + i]->type = FD_NT;
+ fd_table[round + i]->fd.i = NOTOK;
+ fd_table[round + i]->flags = 0;
+ fd_table[round + i]->count = 0;
+
+ pthread_mutex_init(&(fd_table[round + i]->mutex), NULL);
+ pthread_queue_init(&(fd_table[round + i]->r_queue));
+ pthread_queue_init(&(fd_table[round + i]->w_queue));
+ fd_table[round + i]->r_owner = NULL;
+ fd_table[round + i]->w_owner = NULL;
+ fd_table[round + i]->r_lockcount= 0;
+ fd_table[round + i]->w_lockcount= 0;
+
+ fd_table[round + i]->next = NULL;
+ }
+ }
+ return(OK);
+}
+
+/* ==========================================================================
+ * fd_check_entry()
+ */
+int fd_check_entry(unsigned int entry)
+{
+ int ret = OK;
+
+ pthread_mutex_lock(&fd_table_mutex);
+
+ if (entry < dtablesize) {
+ if (fd_table[entry] == NULL) {
+ if (fd_init_entry(entry)) {
+ SET_ERRNO(EBADF);
+ ret = -EBADF;
+ }
+ }
+ } else {
+ SET_ERRNO(EBADF);
+ ret = -EBADF;
+ }
+
+ pthread_mutex_unlock(&fd_table_mutex);
+ return(ret);
+}
/* ==========================================================================
* fd_init()
@@ -65,30 +145,32 @@ 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;
+ if ((dtablesize = machdep_sys_getdtablesize()) < 0) {
+ /* Can't figure out the table size. */
+ PANIC();
}
- /* 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");
+ /* select() can only handle FD_SETSIZE descriptors, so our inner loop will
+ * break if dtablesize is higher than that. This should be removed if and
+ * when the inner loop is rewritten to use poll(). */
+ if (dtablesize > FD_SETSIZE) {
+ dtablesize = FD_SETSIZE;
+ }
+
+ if (fd_table = (struct fd_table_entry **)malloc(
+ sizeof(struct fd_table_entry) * dtablesize)) {
+ memset(fd_table, 0, sizeof(struct fd_table_entry) * dtablesize);
+ if (fd_check_entry(0) == OK) {
+ return;
+ }
+ }
+
+ /*
+ * There isn't enough memory to allocate a fd table at init time.
+ * This is a problem.
+ */
+ PANIC();
+
}
/* ==========================================================================
@@ -96,116 +178,143 @@ void fd_init(void)
*/
int fd_allocate()
{
- semaphore *lock;
+ pthread_mutex_t * mutex;
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; */
+ if (fd_check_entry(i) == OK) {
+ mutex = &(fd_table[i]->mutex);
+ if (pthread_mutex_trylock(mutex)) {
+ continue;
+ }
+ if (fd_table[i]->count || fd_table[i]->r_owner
+ || fd_table[i]->w_owner) {
+ pthread_mutex_unlock(mutex);
+ continue;
+ }
+ if (fd_table[i]->type == FD_NT) {
+ /* Test to see if the kernel version is in use */
+ if ((machdep_sys_fcntl(i, F_GETFL, NULL)) >= OK) {
+ /* If so continue; */
+ pthread_mutex_unlock(mutex);
+ continue;
+ }
+ }
+ fd_table[i]->count++;
+ pthread_mutex_unlock(mutex);
+ return(i);
}
- fd_table[i]->count++;
- SEMAPHORE_RESET(lock);
- return(i);
}
- pthread_run->error = ENFILE;
+ SET_ERRNO(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)
+/*----------------------------------------------------------------------
+ * Function: fd_get_pthread_fd_from_kernel_fd
+ * Purpose: get the fd_table index of a kernel fd
+ * Args: fd = kernel fd to convert
+ * Returns: fd_table index, -1 if not found
+ * Notes:
+ *----------------------------------------------------------------------*/
+static int
+fd_get_pthread_fd_from_kernel_fd( int kfd )
{
- struct fd_table_entry *fd_valid;
- int ret;
+ int j;
- 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;
+ /* This is *SICK*, but unless there is a faster way to
+ * turn a kernel fd into an fd_table index, this has to do.
+ */
+ for( j=0; j < dtablesize; j++ ) {
+ if( fd_table[j] &&
+ fd_table[j]->type != FD_NT &&
+ fd_table[j]->type != FD_NIU &&
+ fd_table[j]->fd.i == kfd ) {
+ return j;
+ }
}
- 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);
+ /* Not listed byfd, Check for kernel fd == pthread fd */
+ if( fd_table[kfd] == NULL || fd_table[kfd]->type == FD_NT ) {
+ /* Assume that the kernel fd is the same */
+ return kfd;
+ }
+
+ return NOTOK; /* Not found */
}
/* ==========================================================================
- * fd_basic_unlock()
- *
+ * fd_basic_basic_unlock()
+ *
* The real work of unlock without the locking of fd_table[fd].lock.
*/
-void fd_basic_unlock(int fd, int lock_type)
+void fd_basic_basic_unlock(struct fd_table_entry * entry, 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 (entry->r_owner == pthread_run) {
+ if ((entry->type == FD_HALF_DUPLEX) ||
+ (entry->type == FD_TEST_HALF_DUPLEX) ||
+ (lock_type == FD_READ) || (lock_type == FD_RDWR)) {
+ if (entry->r_lockcount == 0) {
+ if (pthread = pthread_queue_deq(&entry->r_queue)) {
+ pthread_sched_prevent();
+ entry->r_owner = pthread;
+ if ((SET_PF_DONE_EVENT(pthread)) == OK) {
+ pthread_sched_other_resume(pthread);
+ } else {
+ pthread_sched_resume();
+ }
+ } else {
+ entry->r_owner = NULL;
+ }
+ } else {
+ entry->r_lockcount--;
+ }
+ }
}
- 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;
- }
+ if (entry->w_owner == pthread_run) {
+ if ((entry->type != FD_HALF_DUPLEX) &&
+ (entry->type != FD_TEST_HALF_DUPLEX) &&
+ ((lock_type == FD_WRITE) || (lock_type == FD_RDWR))) {
+ if (entry->w_lockcount == 0) {
+ if (pthread = pthread_queue_deq(&entry->w_queue)) {
+ pthread_sched_prevent();
+ entry->w_owner = pthread;
+ if ((SET_PF_DONE_EVENT(pthread)) == OK) {
+ pthread_sched_other_resume(pthread);
+ } else {
+ pthread_sched_resume();
+ }
+ } else {
+ entry->w_owner = NULL;
+ }
+ } else {
+ entry->w_lockcount--;
+ }
+ }
}
}
/* ==========================================================================
+ * fd_basic_unlock()
+ */
+void fd_basic_unlock(int fd, int lock_type)
+{
+ fd_basic_basic_unlock(fd_table[fd], lock_type);
+}
+
+/* ==========================================================================
* 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;
+ pthread_mutex_t *mutex;
- 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);
- }
+ mutex = &(fd_table[fd]->mutex);
+ pthread_mutex_lock(mutex);
+ fd_basic_basic_unlock(fd_table[fd], lock_type);
+ pthread_mutex_unlock(mutex);
}
/* ==========================================================================
@@ -214,64 +323,126 @@ void fd_unlock(int fd, int lock_type)
* 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)
+int fd_basic_lock(unsigned int fd, int lock_type, pthread_mutex_t * mutex,
+ struct timespec * timeout)
{
semaphore *plock;
- /* If not in use return EBADF error */
- if (fd_table[fd]->type == FD_NIU) {
+ switch (fd_table[fd]->type) {
+ case FD_NIU:
+ /* If not in use return EBADF error */
+ SET_ERRNO(EBADF);
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) {
+ break;
+ case FD_NT:
+ /*
+ * If not tested, test it and see if it is valid
+ * If not ok return EBADF error
+ */
+ fd_kern_init(fd);
+ if (fd_table[fd]->type == FD_NIU) {
+ SET_ERRNO(EBADF);
return(NOTOK);
}
+ break;
+ case FD_TEST_HALF_DUPLEX:
+ case FD_TEST_FULL_DUPLEX:
+ /* If a parent process reset the fd to its proper state */
+ if (!fork_lock) {
+ /* It had better be a kernel fd */
+ fd_kern_reset(fd);
+ }
+ break;
+ default:
+ break;
}
+
if ((fd_table[fd]->type == FD_HALF_DUPLEX) ||
- (lock_type & FD_READ)) {
+ (fd_table[fd]->type == FD_TEST_HALF_DUPLEX) ||
+ (lock_type == FD_READ) || (lock_type == FD_RDWR)) {
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_sched_prevent();
pthread_queue_enq(&fd_table[fd]->r_queue, pthread_run);
- SEMAPHORE_RESET(lock);
-
- /* Reschedule will unlock pthread_run */
- reschedule(PS_FDLR_WAIT);
+ SET_PF_WAIT_EVENT(pthread_run);
+ pthread_mutex_unlock(mutex);
- while(SEMAPHORE_TEST_AND_SET(lock)) {
- pthread_yield();
+ if (timeout) {
+ /* get current time */
+ struct timespec current_time;
+ machdep_gettimeofday(&current_time);
+ sleep_schedule(&current_time, timeout);
+
+ /* Reschedule will unlock pthread_run */
+ pthread_run->data.fd.fd = fd;
+ pthread_run->data.fd.branch = __LINE__;
+ pthread_resched_resume(PS_FDLR_WAIT);
+ pthread_mutex_lock(mutex);
+
+ /* If we're the owner then we have to cancel the sleep */
+ if (fd_table[fd]->r_owner != pthread_run) {
+ CLEAR_PF_DONE_EVENT(pthread_run);
+ SET_ERRNO(ETIMEDOUT);
+ return(NOTOK);
+ }
+ sleep_cancel(pthread_run);
+ } else {
+ /* Reschedule will unlock pthread_run */
+ pthread_run->data.fd.fd = fd;
+ pthread_run->data.fd.branch = __LINE__;
+ pthread_resched_resume(PS_FDLR_WAIT);
+ pthread_mutex_lock(mutex);
}
+ CLEAR_PF_DONE_EVENT(pthread_run);
} else {
- if (!fd_table[fd]->lockcount) {
- PANIC();
- }
+ fd_table[fd]->r_lockcount++;
}
}
fd_table[fd]->r_owner = pthread_run;
}
if ((fd_table[fd]->type != FD_HALF_DUPLEX) &&
- (lock_type & FD_WRITE)) {
+ (fd_table[fd]->type != FD_TEST_HALF_DUPLEX) &&
+ ((lock_type == FD_WRITE) || (lock_type == FD_RDWR))) {
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_sched_prevent();
pthread_queue_enq(&fd_table[fd]->w_queue, pthread_run);
- SEMAPHORE_RESET(lock);
+ SET_PF_WAIT_EVENT(pthread_run);
+ pthread_mutex_unlock(mutex);
+
+ if (timeout) {
+ /* get current time */
+ struct timespec current_time;
+ machdep_gettimeofday(&current_time);
+ sleep_schedule(&current_time, timeout);
- /* Reschedule will unlock pthread_run */
- reschedule(PS_FDLW_WAIT);
+ /* Reschedule will unlock pthread_run */
+ pthread_run->data.fd.fd = fd;
+ pthread_run->data.fd.branch = __LINE__;
+ pthread_resched_resume(PS_FDLR_WAIT);
+ pthread_mutex_lock(mutex);
- while(SEMAPHORE_TEST_AND_SET(lock)) {
- pthread_yield();
+ /* If we're the owner then we have to cancel the sleep */
+ if (fd_table[fd]->w_owner != pthread_run) {
+ if (lock_type == FD_RDWR) {
+ /* Unlock current thread */
+ fd_basic_unlock(fd, FD_READ);
+ }
+ CLEAR_PF_DONE_EVENT(pthread_run);
+ SET_ERRNO(ETIMEDOUT);
+ return(NOTOK);
+ }
+ sleep_cancel(pthread_run);
+ } else {
+ /* Reschedule will unlock pthread_run */
+ pthread_run->data.fd.fd = fd;
+ pthread_run->data.fd.branch = __LINE__;
+ pthread_resched_resume(PS_FDLR_WAIT);
+ pthread_mutex_lock(mutex);
}
+ CLEAR_PF_DONE_EVENT(pthread_run);
+ } else {
+ fd_table[fd]->w_lockcount++;
}
}
fd_table[fd]->w_owner = pthread_run;
@@ -283,24 +454,144 @@ int fd_basic_lock(unsigned int fd, int lock_type, semaphore * lock)
return(OK);
}
+/*----------------------------------------------------------------------
+ * Function: fd_unlock_for_cancel
+ * Purpose: Unlock all fd locks held prior to being cancelled
+ * Args: void
+ * Returns:
+ * OK or NOTOK
+ * Notes:
+ * Assumes the kernel is locked on entry
+ *----------------------------------------------------------------------*/
+int
+fd_unlock_for_cancel( void )
+{
+ int i, fd;
+ struct pthread_select_data *data;
+ int rdlk, wrlk, lktype;
+ int found;
+
+ /* What we do depends on the previous state of the thread */
+ switch( pthread_run->old_state ) {
+ case PS_RUNNING:
+ case PS_JOIN:
+ case PS_SLEEP_WAIT:
+ case PS_WAIT_WAIT:
+ case PS_SIGWAIT:
+ case PS_FDLR_WAIT:
+ case PS_FDLW_WAIT:
+ case PS_DEAD:
+ case PS_UNALLOCED:
+ break; /* Nothing to do */
+
+ case PS_COND_WAIT:
+ CLEAR_PF_GROUP( pthread_run, PF_EVENT_GROUP );
+ /* Must reaquire the mutex according to the standard */
+ if( pthread_run->data.mutex == NULL ) {
+ PANIC();
+ }
+ pthread_mutex_lock( pthread_run->data.mutex );
+ break;
+
+ case PS_FDR_WAIT:
+ CLEAR_PF_GROUP( pthread_run, PF_EVENT_GROUP);
+ /* Free the lock on the fd being used */
+ fd = fd_get_pthread_fd_from_kernel_fd( pthread_run->data.fd.fd );
+ if( fd == NOTOK ) {
+ PANIC(); /* Can't find fd */
+ }
+ fd_unlock( fd, FD_READ );
+ break;
+
+ case PS_FDW_WAIT: /* Waiting on i/o */
+ CLEAR_PF_GROUP( pthread_run, PF_EVENT_GROUP);
+ /* Free the lock on the fd being used */
+ fd = fd_get_pthread_fd_from_kernel_fd( pthread_run->data.fd.fd );
+ if( fd == NOTOK ) {
+ PANIC(); /* Can't find fd */
+ }
+ fd_unlock( fd, FD_WRITE );
+ break;
+
+ case PS_SELECT_WAIT:
+ data = pthread_run->data.select_data;
+
+ CLEAR_PF_GROUP( pthread_run, PF_EVENT_GROUP);
+
+ for( i = 0; i < data->nfds; i++) {
+ rdlk =(FD_ISSET(i,&data->readfds)
+ || FD_ISSET(i,&data->exceptfds));
+ wrlk = FD_ISSET(i, &data->writefds);
+ lktype = rdlk ? (wrlk ? FD_RDWR : FD_READ) : FD_WRITE;
+
+ if( ! (rdlk || wrlk) )
+ continue; /* No locks, no unlock */
+
+ if( (fd = fd_get_pthread_fd_from_kernel_fd( i )) == NOTOK ) {
+ PANIC(); /* Can't find fd */
+ }
+
+ fd_unlock( fd, lktype );
+ }
+ break;
+
+ case PS_MUTEX_WAIT:
+ PANIC(); /* Should never cancel a mutex wait */
+
+ default:
+ PANIC(); /* Unknown thread status */
+ }
+}
+
/* ==========================================================================
* fd_lock()
*/
-int fd_lock(unsigned int fd, int lock_type)
+#define pthread_mutex_lock_timedwait(a, b) pthread_mutex_lock(a)
+
+int fd_lock(unsigned int fd, int lock_type, struct timespec * timeout)
{
- semaphore *lock;
+ struct timespec current_time;
+ pthread_mutex_t *mutex;
int error;
- if (fd < dtablesize) {
- lock = &(fd_table[fd]->lock);
- while (SEMAPHORE_TEST_AND_SET(lock)) {
- pthread_yield();
+ if ((error = fd_check_entry(fd)) == OK) {
+ mutex = &(fd_table[fd]->mutex);
+ if (pthread_mutex_lock_timedwait(mutex, timeout)) {
+ SET_ERRNO(ETIMEDOUT);
+ return(-ETIMEDOUT);
}
- error = fd_basic_lock(fd, lock_type, lock);
- SEMAPHORE_RESET(lock);
- return(error);
+ error = fd_basic_lock(fd, lock_type, mutex, timeout);
+ pthread_mutex_unlock(mutex);
}
- return(NOTOK);
+ return(error);
+}
+
+/* ==========================================================================
+ * fd_free()
+ *
+ * Assumes fd is locked and owner by pthread_run
+ * Don't clear the queues, fd_unlock will do that.
+ */
+struct fd_table_entry * fd_free(int fd)
+{
+ struct fd_table_entry *fd_valid;
+
+ fd_valid = NULL;
+ fd_table[fd]->r_lockcount = 0;
+ fd_table[fd]->w_lockcount = 0;
+ if (--fd_table[fd]->count) {
+ fd_valid = fd_table[fd];
+ fd_table[fd] = fd_table[fd]->next;
+ fd_valid->next = fd_table[fd]->next;
+ /* Don't touch queues of fd_valid */
+ }
+
+ 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(fd_valid);
}
@@ -308,73 +599,111 @@ int fd_lock(unsigned int fd, int lock_type)
* ======================================================================= */
/* ==========================================================================
- * read()
+ * read_timedwait()
*/
-ssize_t read(int fd, void *buf, size_t nbytes)
+ssize_t read_timedwait(int fd, void *buf, size_t nbytes,
+ struct timespec * timeout)
{
int ret;
- if ((ret = fd_lock(fd, FD_READ)) == OK) {
+ if ((ret = fd_lock(fd, FD_READ, NULL)) == OK) {
ret = fd_table[fd]->ops->read(fd_table[fd]->fd,
- fd_table[fd]->flags, buf, nbytes);
+ fd_table[fd]->flags, buf, nbytes, timeout);
fd_unlock(fd, FD_READ);
}
return(ret);
}
/* ==========================================================================
- * readv()
+ * read()
*/
-int readv(int fd, const struct iovec *iov, int iovcnt)
+ssize_t read(int fd, void *buf, size_t nbytes)
+{
+ return(read_timedwait(fd, buf, nbytes, NULL));
+}
+
+/* ==========================================================================
+ * readv_timedwait()
+ */
+int readv_timedwait(int fd, const struct iovec *iov, int iovcnt,
+ struct timespec * timeout)
{
int ret;
- if ((ret = fd_lock(fd, FD_READ)) == OK) {
+ if ((ret = fd_lock(fd, FD_READ, NULL)) == OK) {
ret = fd_table[fd]->ops->readv(fd_table[fd]->fd,
- fd_table[fd]->flags, iov, iovcnt);
+ fd_table[fd]->flags, iov, iovcnt, timeout);
fd_unlock(fd, FD_READ);
}
return(ret);
}
/* ==========================================================================
+ * readv()
+ */
+ssize_t readv(int fd, const struct iovec *iov, int iovcnt)
+{
+ return(readv_timedwait(fd, iov, iovcnt, NULL));
+}
+
+/* ==========================================================================
* write()
*/
-ssize_t write(int fd, const void *buf, size_t nbytes)
+ssize_t write_timedwait(int fd, const void *buf, size_t nbytes,
+ struct timespec * timeout)
{
- int ret;
+ 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);
+ if ((ret = fd_lock(fd, FD_WRITE, NULL)) == OK)
+ {
+ ret = fd_table[fd]->ops->write(fd_table[fd]->fd,
+ fd_table[fd]->flags, buf, nbytes,
+ timeout);
+ fd_unlock(fd, FD_WRITE);
+ }
+ return(ret);
}
/* ==========================================================================
- * writev()
+ * write()
+ */
+ssize_t write(int fd, const void * buf, size_t nbytes)
+{
+ return(write_timedwait(fd, buf, nbytes, NULL));
+}
+
+/* ==========================================================================
+ * writev_timedwait()
*/
-int writev(int fd, const struct iovec *iov, int iovcnt)
+int writev_timedwait(int fd, const struct iovec *iov, int iovcnt,
+ struct timespec * timeout)
{
int ret;
- if ((ret = fd_lock(fd, FD_WRITE)) == OK) {
+ if ((ret = fd_lock(fd, FD_WRITE, NULL)) == OK) {
ret = fd_table[fd]->ops->writev(fd_table[fd]->fd,
- fd_table[fd]->flags, iov, iovcnt);
+ fd_table[fd]->flags, iov, iovcnt, timeout);
fd_unlock(fd, FD_WRITE);
}
return(ret);
}
/* ==========================================================================
+ * writev()
+ */
+ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
+{
+ return(writev_timedwait(fd, iov, iovcnt, NULL));
+}
+
+/* ==========================================================================
* lseek()
*/
off_t lseek(int fd, off_t offset, int whence)
{
int ret;
- if ((ret = fd_lock(fd, FD_RDWR)) == OK) {
+ if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) {
ret = fd_table[fd]->ops->seek(fd_table[fd]->fd,
fd_table[fd]->flags, offset, whence);
fd_unlock(fd, FD_RDWR);
@@ -393,66 +722,178 @@ off_t lseek(int fd, off_t offset, int whence)
* 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...
+ *
+ * XXX close() is even uglier now. You may assume that the kernel fd is the
+ * same as fd if fd_table[fd] == NULL or if fd_table[fd]->type == FD_NT.
+ * This is true because before any fd_table[fd] is allocated the corresponding
+ * kernel fd must be checks to see if it's valid.
*/
int close(int fd)
{
- union fd_data realfd;
- int ret, flags;
+ struct fd_table_entry * entry;
+ pthread_mutex_t *mutex;
+ 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);
+ if(fd < 0 || fd >= dtablesize)
+ {
+ SET_ERRNO(EBADF);
+ return -1;
+ }
+ /* Need to lock the newfd by hand */
+ pthread_mutex_lock(&fd_table_mutex);
+ if (fd_table[fd]) {
+ pthread_mutex_unlock(&fd_table_mutex);
+ mutex = &(fd_table[fd]->mutex);
+ pthread_mutex_lock(mutex);
+
+ /*
+ * XXX Gross hack ... because of fork(), any fd closed by the
+ * parent should not change the fd of the child, unless it owns it.
+ */
+ switch(fd_table[fd]->type) {
+ case FD_NIU:
+ pthread_mutex_unlock(mutex);
+ ret = -EBADF;
+ break;
+ case FD_NT:
+ /*
+ * If it's not tested then the only valid possibility is it's
+ * kernel fd.
+ */
+ ret = machdep_sys_close(fd);
+ fd_table[fd]->type = FD_NIU;
+ pthread_mutex_unlock(mutex);
+ break;
+ case FD_TEST_FULL_DUPLEX:
+ case FD_TEST_HALF_DUPLEX:
+ realfd = fd_table[fd]->fd;
+ flags = fd_table[fd]->flags;
+ if ((entry = fd_free(fd)) == NULL) {
+ ret = fd_table[fd]->ops->close(realfd, flags);
+ } else {
+ /* There can't be any others waiting for fd. */
+ pthread_mutex_unlock(&entry->mutex);
+ /* Note: entry->mutex = mutex */
+ mutex = &(fd_table[fd]->mutex);
+ }
+ pthread_mutex_unlock(mutex);
+ break;
+ default:
+ ret = fd_basic_lock(fd, FD_RDWR, mutex, NULL);
+ if (ret == OK) {
+ realfd = fd_table[fd]->fd;
+ flags = fd_table[fd]->flags;
+ pthread_mutex_unlock(mutex);
+ if ((entry = fd_free(fd)) == NULL) {
+ ret = fd_table[fd]->ops->close(realfd, flags);
+ } else {
+ fd_basic_basic_unlock(entry, FD_RDWR);
+ pthread_mutex_unlock(&entry->mutex);
+ /* Note: entry->mutex = mutex */
}
- return(ret);
+ fd_unlock(fd, FD_RDWR);
+ } else {
+ pthread_mutex_unlock(mutex);
+ }
+ break;
+ }
+ } else {
+ /* Don't bother creating a table entry */
+ pthread_mutex_unlock(&fd_table_mutex);
+ ret = machdep_sys_close(fd);
+ }
+ if( ret < 0) {
+ SET_ERRNO(-ret);
+ ret = -1;
+ }
+ return(ret);
}
/* ==========================================================================
* fd_basic_dup()
*
- * Might need to do more than just what's below.
+ *
+ * This is a MAJOR guess!! I don't know if the mutext unlock is valid
+ * in the BIG picture. But it seems to be needed to avoid deadlocking
+ * with ourselves when we try to close the duped file descriptor.
*/
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[newfd] = fd_table[fd];
fd_table[fd]->count++;
+ pthread_mutex_unlock(&fd_table[newfd]->next->mutex);
+
}
/* ==========================================================================
* 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.
+ * Note: Always lock the lower number fd first to avoid deadlocks.
+ * Note: Leave the newfd locked. It will be unlocked at close() time.
+ * Note: 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)
{
+ struct fd_table_entry * entry;
+ pthread_mutex_t *mutex;
union fd_data realfd;
- semaphore *lock;
int ret, flags;
+ if ((ret = fd_check_entry(newfd)) != OK)
+ return ret;
+
if (newfd < dtablesize) {
if (fd < newfd) {
- if ((ret = fd_lock(fd, FD_RDWR)) == OK) {
+ if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) {
/* Need to lock the newfd by hand */
- lock = &(fd_table[newfd]->lock);
- while(SEMAPHORE_TEST_AND_SET(lock)) {
- pthread_yield();
- }
+ mutex = &(fd_table[newfd]->mutex);
+ pthread_mutex_lock(mutex);
/* Is it inuse */
- if (fd_basic_lock(newfd, FD_RDWR, lock) == OK) {
+ if (fd_basic_lock(newfd, FD_RDWR, mutex, NULL) == OK) {
+ realfd = fd_table[newfd]->fd;
+ flags = fd_table[newfd]->flags;
/* 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);
+ if ((entry = fd_free(newfd)) == NULL) {
+ entry = fd_table[newfd];
+ entry->ops->close(realfd, flags);
+ if (entry->r_queue.q_next) {
+ if (fd_table[fd]->next) {
+ fd_table[fd]->r_queue.q_last->next =
+ entry->r_queue.q_next;
+ } else {
+ fd_table[fd]->r_queue.q_next =
+ entry->r_queue.q_next;
+ }
+ fd_table[fd]->r_queue.q_last =
+ entry->r_queue.q_last;
+ }
+ if (entry->w_queue.q_next) {
+ if (fd_table[fd]->next) {
+ fd_table[fd]->w_queue.q_last->next =
+ entry->w_queue.q_next;
+ } else {
+ fd_table[fd]->w_queue.q_next =
+ entry->w_queue.q_next;
+ }
+ fd_table[fd]->w_queue.q_last =
+ entry->w_queue.q_last;
+ }
+ entry->r_queue.q_next = NULL;
+ entry->w_queue.q_next = NULL;
+ entry->r_queue.q_last = NULL;
+ entry->w_queue.q_last = NULL;
+ entry->r_owner = NULL;
+ entry->w_owner = NULL;
+ ret = OK;
} else {
- /* Lots of work to do */
+ fd_basic_basic_unlock(entry, FD_RDWR);
+ pthread_mutex_unlock(&entry->mutex);
+ /* Note: entry->mutex = mutex */
}
}
fd_basic_dup(fd, newfd);
@@ -460,27 +901,56 @@ int dup2(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 */
- }
+ mutex = &(fd_table[newfd]->mutex);
+ pthread_mutex_lock(mutex);
- fd_basic_dup(fd, newfd);
+ if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) {
+ /* Is newfd inuse */
+ if ((ret = fd_basic_lock(newfd, FD_RDWR, mutex, NULL)) == OK) {
+ realfd = fd_table[newfd]->fd;
+ flags = fd_table[newfd]->flags;
+ /* free it and check close status */
+ if ((entry = fd_free(newfd)) == NULL) {
+ entry = fd_table[newfd];
+ entry->ops->close(realfd, flags);
+ if (entry->r_queue.q_next) {
+ if (fd_table[fd]->next) {
+ fd_table[fd]->r_queue.q_last->next =
+ entry->r_queue.q_next;
+ } else {
+ fd_table[fd]->r_queue.q_next =
+ entry->r_queue.q_next;
+ }
+ fd_table[fd]->r_queue.q_last =
+ entry->r_queue.q_last;
+ }
+ if (entry->w_queue.q_next) {
+ if (fd_table[fd]->next) {
+ fd_table[fd]->w_queue.q_last->next =
+ entry->w_queue.q_next;
+ } else {
+ fd_table[fd]->w_queue.q_next =
+ entry->w_queue.q_next;
+ }
+ fd_table[fd]->w_queue.q_last =
+ entry->w_queue.q_last;
+ }
+ entry->r_queue.q_next = NULL;
+ entry->w_queue.q_next = NULL;
+ entry->r_queue.q_last = NULL;
+ entry->w_queue.q_last = NULL;
+ entry->r_owner = NULL;
+ entry->w_owner = NULL;
+ ret = OK;
+ } else {
+ fd_basic_basic_unlock(entry, FD_RDWR);
+ pthread_mutex_unlock(&entry->mutex);
+ /* Note: entry->mutex = mutex */
+ }
+ fd_basic_dup(fd, newfd);
+ }
fd_unlock(fd, FD_RDWR);
}
- SEMAPHORE_RESET(lock);
}
} else {
ret = NOTOK;
@@ -496,7 +966,7 @@ int dup(int fd)
{
int ret;
- if ((ret = fd_lock(fd, FD_RDWR)) == OK) {
+ if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) {
ret = fd_allocate();
fd_basic_dup(fd, ret);
fd_unlock(fd, FD_RDWR);
@@ -515,7 +985,7 @@ int fcntl(int fd, int cmd, ...)
va_list ap;
flags = 0;
- if ((ret = fd_lock(fd, FD_RDWR)) == OK) {
+ if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) {
va_start(ap, cmd);
switch(cmd) {
case F_DUPFD:
@@ -523,10 +993,8 @@ int fcntl(int fd, int cmd, ...)
fd_basic_dup(va_arg(ap, int), ret);
break;
case F_SETFD:
- flags = va_arg(ap, int);
+ break;
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;
@@ -560,3 +1028,54 @@ int fcntl(int fd, int cmd, ...)
}
return(ret);
}
+
+/* ==========================================================================
+ * getdtablesize()
+ */
+int getdtablesize()
+{
+ return dtablesize;
+}
+
+/* ==========================================================================
+ * ioctl()
+ *
+ * Really want to do a real implementation of this that parses the args ala
+ * fcntl(), above, but it will have to be a totally platform-specific,
+ * nightmare-on-elm-st-style sort of thing. Might even deserve its own file
+ * ala select()... --SNL
+ */
+#ifndef ioctl_request_type
+#define ioctl_request_type unsigned long /* Dummy patch by Monty */
+#endif
+
+int
+ioctl(int fd, ioctl_request_type request, ...)
+{
+ int ret;
+ pthread_va_list ap;
+ caddr_t arg;
+
+ va_start( ap, request ); /* Get the arg */
+ arg = va_arg(ap,caddr_t);
+ va_end( ap );
+
+ if (fd < 0 || fd >= dtablesize)
+ ret = NOTOK;
+ else if (fd_table[fd]->fd.i == NOTOK)
+ ret = machdep_sys_ioctl(fd, request, arg);
+ else if ((ret = fd_lock(fd, FD_RDWR, NULL)) == OK) {
+ ret = machdep_sys_ioctl(fd_table[fd]->fd.i, request, arg);
+ if( ret == 0 && request == FIONBIO ) {
+ /* Properly set NONBLOCK flag */
+ int v = *(int *)arg;
+ if( v )
+ fd_table[fd]->flags |= __FD_NONBLOCK;
+ else
+ fd_table[fd]->flags &= ~__FD_NONBLOCK;
+ }
+ fd_unlock(fd, FD_RDWR);
+ }
+ return ret;
+}
+