/* ==== fd_kern.c ============================================================
* Copyright (c) 1993 by Chris Provenzano, proven@mit.edu
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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
* 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
* Description : Deals with the valid kernel fds.
* -Started coding this file.
* -The functions readv() and writev() added.
#include <pthread/posix.h>
/* ==========================================================================
* Called only from context_switch(). The kernel must be locked
* and interrupts must be turned of.
* This function uses a linked list of waiting pthreads, NOT a queue.
static struct pthread
*fd_wait_read
, *fd_wait_write
;
static semaphore fd_wait_lock
= SEMAPHORE_CLEAR
;
static fd_set fd_set_read
, fd_set_write
;
static struct timeval zerotime
= { 0, 0 };
struct pthread
**pthread
;
/* If someone has the lock then they are in RUNNING state, just return */
if (SEMAPHORE_TEST_AND_SET(lock
)) {
if (fd_wait_read
|| fd_wait_write
) {
for (pthread
= &fd_wait_read
; *pthread
; pthread
= &((*pthread
)->next
)) {
FD_SET((*pthread
)->fd
, &fd_set_read
);
for (pthread
= &fd_wait_write
; *pthread
; pthread
= &((*pthread
)->next
)) {
FD_SET((*pthread
)->fd
, &fd_set_write
);
while ((count
= select(dtablesize
, &fd_set_read
, &fd_set_write
,
NULL
, &zerotime
)) < OK
) {
for (pthread
= &fd_wait_read
; count
&& *pthread
; ) {
if (FD_ISSET((*pthread
)->fd
, &fd_set_read
)) {
(*pthread
)->state
= PS_RUNNING
;
*pthread
= (*pthread
)->next
;
pthread
= &((*pthread
)->next
);
for (pthread
= &fd_wait_write
; count
&& *pthread
; ) {
if (FD_ISSET((*pthread
)->fd
, &fd_set_write
)) {
plock
= &(*pthread
)->lock
;
if (!(SEMAPHORE_TEST_AND_SET(plock
))) {
/* Thread locked, skip it. */
(*pthread
)->state
= PS_RUNNING
;
*pthread
= (*pthread
)->next
;
pthread
= &((*pthread
)->next
);
/* ==========================================================================
* Special Note: All operations return the errno as a negative of the errno
* ======================================================================= */
/* ==========================================================================
ssize_t
__fd_kern_read(int fd
, int flags
, void *buf
, size_t nbytes
)
while ((ret
= machdep_sys_read(fd
, buf
, nbytes
)) < OK
) {
if (ret
== -EWOULDBLOCK
) {
while (SEMAPHORE_TEST_AND_SET(lock
)) {
plock
= &(pthread_run
->lock
);
while (SEMAPHORE_TEST_AND_SET(plock
)) {
/* queue pthread for a FDR_WAIT */
pthread_run
->next
= fd_wait_read
;
fd_wait_read
= pthread_run
;
pthread_run
->error
= -ret
;
/* ==========================================================================
int __fd_kern_readv(int fd
, int flags
, struct iovec
*iov
, int iovcnt
)
while ((ret
= machdep_sys_readv(fd
, iov
, iovcnt
)) < OK
) {
if (ret
== -EWOULDBLOCK
) {
while (SEMAPHORE_TEST_AND_SET(lock
)) {
plock
= &(pthread_run
->lock
);
while (SEMAPHORE_TEST_AND_SET(plock
)) {
/* queue pthread for a FDR_WAIT */
pthread_run
->next
= fd_wait_read
;
fd_wait_read
= pthread_run
;
pthread_run
->error
= -ret
;
/* ==========================================================================
ssize_t
__fd_kern_write(int fd
, int flags
, const void *buf
, size_t nbytes
)
while ((ret
= machdep_sys_write(fd
, buf
, nbytes
)) < OK
) {
if (pthread_run
->error
== -EWOULDBLOCK
) {
while (SEMAPHORE_TEST_AND_SET(lock
)) {
plock
= &(pthread_run
->lock
);
while (SEMAPHORE_TEST_AND_SET(plock
)) {
/* queue pthread for a FDW_WAIT */
pthread_run
->next
= fd_wait_write
;
fd_wait_write
= pthread_run
;
pthread_run
->error
= ret
;
/* ==========================================================================
int __fd_kern_writev(int fd
, int flags
, struct iovec
*iov
, int iovcnt
)
while ((ret
= machdep_sys_writev(fd
, iov
, iovcnt
)) < OK
) {
if (pthread_run
->error
== -EWOULDBLOCK
) {
while (SEMAPHORE_TEST_AND_SET(lock
)) {
plock
= &(pthread_run
->lock
);
while (SEMAPHORE_TEST_AND_SET(plock
)) {
/* queue pthread for a FDW_WAIT */
pthread_run
->next
= fd_wait_write
;
fd_wait_write
= pthread_run
;
pthread_run
->error
= ret
;
/* ==========================================================================
* For blocking version we really should set an interrupt
int __fd_kern_fcntl(int fd
, int flags
, int cmd
, int arg
)
machdep_sys_fcntl(fd
, cmd
, arg
);
/* ==========================================================================
int __fd_kern_close(int fd
, int flags
)
* File descriptor operations
extern machdep_sys_close();
/* Normal file operations */
static struct fd_ops __fd_kern_ops
= {
__fd_kern_write
, __fd_kern_read
, __fd_kern_close
, __fd_kern_fcntl
,
__fd_kern_readv
, __fd_kern_writev
/* NFS file opperations */
/* FIFO file opperations */
/* ==========================================================================
* Because open could potentially block opening a file from a remote
* system, we want to make sure the call will timeout. We then try and open
* the file, and stat the file to determine what operations we should
* A reqular file on the local system needs no special treatment.
int open(const char *path
, int flags
, ...)
/* If pthread scheduling == FIFO set a virtual timer */
if (!((fd
= fd_allocate()) < OK
)) {
fd_table
[fd
]->flags
= flags
;
if (!((fd_kern
= machdep_sys_open(path
, flags
, mode
)) < OK
)) {
/* fstat the file to determine what type it is */
if (fstat(fd_kern
, &stat_buf
)) {
printf("error %d stating new fd %d\n", errno
, fd
);
if (S_ISREG(stat_buf
.st_mode
)) {
fd_table
[fd
]->ops
= &(__fd_kern_ops
);
fd_table
[fd
]->type
= FD_HALF_DUPLEX
;
fd_table
[fd
]->ops
= &(__fd_kern_ops
);
fd_table
[fd
]->type
= FD_FULL_DUPLEX
;
fd_table
[fd
]->fd
.i
= fd_kern
;
pthread_run
->error
= - fd_kern
;
/* ==========================================================================
* Assume the entry is locked before routine is invoked
* This may change. The problem is setting the fd to nonblocking changes
* the parents fd too, which may not be the desired result.
static fd_kern_init_called
= 0;
void fd_kern_init(int fd
)
if ((fd_table
[fd
]->flags
= machdep_sys_fcntl(fd
, F_GETFL
, NULL
)) >= OK
) {
machdep_sys_fcntl(fd
, F_SETFL
, fd_table
[fd
]->flags
| __FD_NONBLOCK
);
fd_table
[fd
]->ops
= &(__fd_kern_ops
);
fd_table
[fd
]->type
= FD_HALF_DUPLEX
;
/* Only give one warning */
if (!(fd_kern_init_called
++)) {
printf("Warning: threaded process may have changed open file ");
printf("descriptors of parent\n");
/* ==========================================================================
* Here are the berkeley socket functions. These are not POSIX.
* ======================================================================= */
/* ==========================================================================
int socket(int af
, int type
, int protocol
)
if (!((fd
= fd_allocate()) < OK
)) {
if (!((fd_kern
= machdep_sys_socket(af
, type
, protocol
)) < OK
)) {
machdep_sys_fcntl(fd_kern
, F_SETFL
, __FD_NONBLOCK
);
/* Should fstat the file to determine what type it is */
fd_table
[fd
]->ops
= & __fd_kern_ops
;
fd_table
[fd
]->type
= FD_FULL_DUPLEX
;
fd_table
[fd
]->fd
.i
= fd_kern
;
pthread_run
->error
= - fd_kern
;
/* ==========================================================================
int bind(int fd
, const struct sockaddr
*name
, int namelen
)
/* Not much to do in bind */
if ((ret
= fd_lock(fd
, FD_RDWR
)) == OK
) {
if ((ret
= machdep_sys_bind(fd_table
[fd
]->fd
, name
, namelen
)) < OK
) {
pthread_run
->error
= - ret
;
/* ==========================================================================
int connect(int fd
, const struct sockaddr
*name
, int namelen
)
if ((ret
= fd_lock(fd
, FD_RDWR
)) == OK
) {
if ((ret
= machdep_sys_connect(fd_table
[fd
]->fd
, name
, namelen
)) < OK
) {
if ((ret
== -EWOULDBLOCK
) || (ret
== -EINPROGRESS
) ||
while (SEMAPHORE_TEST_AND_SET(lock
)) {
plock
= &(pthread_run
->lock
);
while (SEMAPHORE_TEST_AND_SET(plock
)) {
/* queue pthread for a FDW_WAIT */
pthread_run
->next
= fd_wait_write
;
fd_wait_write
= pthread_run
;
/* OK now lets see if it really worked */
if (((ret
= machdep_sys_getpeername(fd_table
[fd
]->fd
,
&tmpname
, &tmpnamelen
)) < OK
) && (ret
== -ENOTCONN
)) {
/* Get the error, this function should not fail */
machdep_sys_getsockopt(fd_table
[fd
]->fd
, SOL_SOCKET
,
SO_ERROR
, &pthread_run
->error
, &tmpnamelen
);
pthread_run
->error
= -ret
;
/* ==========================================================================
int accept(int fd
, struct sockaddr
*name
, int *namelen
)
if ((ret
= fd_lock(fd
, FD_RDWR
)) == OK
) {
while ((ret
= machdep_sys_accept(fd_table
[fd
]->fd
, name
, namelen
)) < OK
) {
if (pthread_run
->error
== -EWOULDBLOCK
) {
while (SEMAPHORE_TEST_AND_SET(lock
)) {
plock
= &(pthread_run
->lock
);
while (SEMAPHORE_TEST_AND_SET(plock
)) {
/* queue pthread for a FDR_WAIT */
pthread_run
->next
= fd_wait_read
;
fd_wait_read
= pthread_run
;
/* ==========================================================================
int listen(int fd
, int backlog
)
if ((ret
= fd_lock(fd
, FD_RDWR
)) == OK
) {
ret
= machdep_sys_listen(fd_table
[fd
]->fd
, backlog
);