diff options
Diffstat (limited to 'lib/libpthread/pthreads/fd.c')
-rw-r--r-- | lib/libpthread/pthreads/fd.c | 953 |
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(¤t_time); + sleep_schedule(¤t_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(¤t_time); + sleep_schedule(¤t_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; +} + |