From 2e227506e5065155e1b53f983d1c3712cf38d770 Mon Sep 17 00:00:00 2001 From: Olaf Barthel Date: Wed, 5 Apr 2006 07:53:30 +0000 Subject: [PATCH] - Added a wait_select() function which works very much like the bsdsocket.library/WaitSelect() function. git-svn-id: file:///Users/olsen/Code/migration-svn-zu-git/logical-line-staging/clib2/trunk@15091 87f5fb63-7c3d-0410-a384-fd976d0f7a62 --- library/GNUmakefile.68k | 4 +- library/GNUmakefile.os4 | 4 +- library/changes | 3 + library/include/sys/select.h | 18 +- library/smakefile | 6 +- library/socket_headers.h | 3 +- library/socket_select.c | 1046 +---------------------------- library/socket_select_signal.c | 1141 ++++++++++++++++++++++++++++++++ library/socket_wait_select.c | 56 ++ 9 files changed, 1232 insertions(+), 1049 deletions(-) create mode 100644 library/socket_select_signal.c create mode 100644 library/socket_wait_select.c diff --git a/library/GNUmakefile.68k b/library/GNUmakefile.68k index d111e2c..3bb81be 100644 --- a/library/GNUmakefile.68k +++ b/library/GNUmakefile.68k @@ -1,5 +1,5 @@ # -# $Id: GNUmakefile.68k,v 1.80 2006-01-29 09:16:59 obarthel Exp $ +# $Id: GNUmakefile.68k,v 1.81 2006-04-05 07:53:23 obarthel Exp $ # # :ts=8 # @@ -772,7 +772,9 @@ NET_LIB = \ socket_recv.o \ socket_recvfrom.o \ socket_recvmsg.o \ + socket_select_signal.o \ socket_select.o \ + socket_wait_select.o \ socket_send.o \ socket_sendmsg.o \ socket_sendto.o \ diff --git a/library/GNUmakefile.os4 b/library/GNUmakefile.os4 index 0f1ac99..a789fc4 100644 --- a/library/GNUmakefile.os4 +++ b/library/GNUmakefile.os4 @@ -1,5 +1,5 @@ # -# $Id: GNUmakefile.os4,v 1.94 2006-04-05 06:43:56 obarthel Exp $ +# $Id: GNUmakefile.os4,v 1.95 2006-04-05 07:53:24 obarthel Exp $ # # :ts=8 # @@ -784,7 +784,9 @@ NET_LIB = \ socket_recv.o \ socket_recvfrom.o \ socket_recvmsg.o \ + socket_select_signal.o \ socket_select.o \ + socket_wait_select.o \ socket_send.o \ socket_sendmsg.o \ socket_sendto.o \ diff --git a/library/changes b/library/changes index a1c2775..a75acd4 100644 --- a/library/changes +++ b/library/changes @@ -1,3 +1,6 @@ +- Added a wait_select() function which works very much like the + bsdsocket.library/WaitSelect() function. + - Added include to due to latest "TimeVal" change in the OS4 SDK. Otherwise "struct timeval" will not be defined at the time the of the netincludes will be included by . diff --git a/library/include/sys/select.h b/library/include/sys/select.h index fb7fce9..0aa7248 100644 --- a/library/include/sys/select.h +++ b/library/include/sys/select.h @@ -1,5 +1,5 @@ /* - * $Id: select.h,v 1.6 2006-01-08 12:06:14 obarthel Exp $ + * $Id: select.h,v 1.7 2006-04-05 07:53:30 obarthel Exp $ * * :ts=4 * @@ -108,6 +108,22 @@ typedef struct fd_set extern int select(int nfds, fd_set *readfds,fd_set *writefds, fd_set *errorfds,struct timeval *timeout); +/* This is a special select() function which takes an extra Amiga signal + bit mask pointer parameter. This function works like select(), but it will + also return if any of the signals indicated by the 'signal_mask' parameter + are set. When this function returns, the variable pointed to by the + 'signal_mask' parameter will have all the bits set which were set at the + time the function was called and for which signals arrived while the + function was still running. When this function returns, any signals + received while it was running for which bits were set in the 'signal_mask' + parameter value will be cleared with the exception of SIGBREAKF_CTRL_C. + In brief, wait_select() works exactly like the bsdsocket.library/WaitSelect() + function. */ + +#ifndef __NO_WAIT_SELECT +extern int wait_select(int num_fds,fd_set *read_fds,fd_set *write_fds,fd_set *except_fds,struct timeval *timeout,ULONG * signal_mask); +#endif /* __NO_WAIT_SELECT */ + #endif /* __NO_NET_API */ /****************************************************************************/ diff --git a/library/smakefile b/library/smakefile index 61b3599..a278985 100644 --- a/library/smakefile +++ b/library/smakefile @@ -1,5 +1,5 @@ # -# $Id: smakefile,v 1.59 2006-01-02 13:23:33 obarthel Exp $ +# $Id: smakefile,v 1.60 2006-04-05 07:53:24 obarthel Exp $ # # :ts=8 # @@ -358,7 +358,9 @@ SOCKET_OBJ = \ socket_recv.o \ socket_recvfrom.o \ socket_recvmsg.o \ + socket_select_signal.o \ socket_select.o \ + socket_wait_select.o \ socket_send.o \ socket_sendmsg.o \ socket_sendto.o \ @@ -801,7 +803,7 @@ dirent_opendir.o : dirent_opendir.c stdlib_memory.h fcntl_open.o : fcntl_open.c stdlib_memory.h -socket_select.o : socket_select.c stdlib_memory.h +socket_select_signal.o : socket_select_signal.c stdlib_memory.h stdio_fclose.o : stdio_fclose.c stdlib_memory.h diff --git a/library/socket_headers.h b/library/socket_headers.h index 3c01203..fcc58fb 100644 --- a/library/socket_headers.h +++ b/library/socket_headers.h @@ -1,5 +1,5 @@ /* - * $Id: socket_headers.h,v 1.13 2006-01-08 12:04:24 obarthel Exp $ + * $Id: socket_headers.h,v 1.14 2006-04-05 07:53:24 obarthel Exp $ * * :ts=4 * @@ -127,6 +127,7 @@ extern int __socket_hook_entry(struct fd * fd,struct file_action_message * fam); extern int __get_h_errno(void); extern void __set_h_errno(int new_h_errno); extern BOOL __obtain_daemon_message(VOID); +extern int __select(int num_fds,fd_set *read_fds,fd_set *write_fds,fd_set *except_fds,struct timeval *timeout,ULONG * signal_mask_ptr); /****************************************************************************/ diff --git a/library/socket_select.c b/library/socket_select.c index 062f936..aeb82dc 100644 --- a/library/socket_select.c +++ b/library/socket_select.c @@ -1,5 +1,5 @@ /* - * $Id: socket_select.c,v 1.15 2006-01-08 12:04:24 obarthel Exp $ + * $Id: socket_select.c,v 1.16 2006-04-05 07:53:24 obarthel Exp $ * * :ts=4 * @@ -41,1053 +41,13 @@ /****************************************************************************/ -#ifndef _STDLIB_MEMORY_H -#include "stdlib_memory.h" -#endif /* _STDLIB_MEMORY_H */ - -/****************************************************************************/ - -STATIC VOID -copy_fd_set(fd_set * to,fd_set * from,int num_fds) -{ - ENTER(); - - SHOWPOINTER(to); - SHOWPOINTER(from); - SHOWVALUE(num_fds); - - if(to != NULL && from != NULL && num_fds > 0) - { - size_t num_bytes; - - num_bytes = sizeof(unsigned long) * ((num_fds + 31) / 32); - - SHOWVALUE(num_bytes); - - memmove(to,from,num_bytes); - } - - LEAVE(); -} - -/****************************************************************************/ - -STATIC VOID -zero_fd_set(fd_set * set,int num_fds) -{ - ENTER(); - - SHOWPOINTER(set); - SHOWVALUE(num_fds); - - if(set != NULL && num_fds > 0) - { - size_t num_bytes; - - num_bytes = sizeof(unsigned long) * ((num_fds + 31) / 32); - - SHOWVALUE(num_bytes); - - memset(set,0,num_bytes); - } - - LEAVE(); -} - -/****************************************************************************/ - -STATIC fd_set * -allocate_fd_set(int num_fds,fd_set * duplicate_this_set) -{ - fd_set * result = NULL; - size_t num_bytes; - fd_set * set; - - ENTER(); - - assert( num_fds > 0 ); - - SHOWVALUE(num_fds); - - num_bytes = sizeof(unsigned long) * ((num_fds + 31) / 32); - - SHOWVALUE(num_bytes); - - set = (fd_set *)malloc(num_bytes); - if(set != NULL) - { - if(duplicate_this_set != NULL) - copy_fd_set(set,duplicate_this_set,num_fds); - else - zero_fd_set(set,num_fds); - - result = set; - } - - RETURN(result); - return(result); -} - -/****************************************************************************/ - -STATIC VOID -free_fd_set(fd_set * set) -{ - if(set != NULL) - free(set); -} - -/****************************************************************************/ - -STATIC struct fd * -get_file_descriptor(int file_descriptor) -{ - struct fd * result = NULL; - struct fd * fd; - - __stdio_lock(); - - if(file_descriptor < 0 || file_descriptor >= __num_fd) - goto out; - - assert( __fd != NULL ); - - fd = __fd[file_descriptor]; - - assert( fd != NULL ); - - if(FLAG_IS_CLEAR(fd->fd_Flags,FDF_IN_USE)) - goto out; - - result = fd; - - out: - - __stdio_unlock(); - - return(result); -} - -/****************************************************************************/ - -STATIC VOID -fix_datestamp(struct DateStamp * ds) -{ - const LONG ticks_per_minute = 60 * TICKS_PER_SECOND; - const LONG minutes_per_day = 24 * 60; - - assert( ds != NULL ); - - while(ds->ds_Minute >= minutes_per_day || - ds->ds_Tick >= ticks_per_minute) - { - if(ds->ds_Minute >= minutes_per_day) - { - ds->ds_Days++; - - ds->ds_Minute -= minutes_per_day; - } - - if(ds->ds_Tick >= ticks_per_minute) - { - ds->ds_Minute++; - - ds->ds_Tick -= ticks_per_minute; - } - } -} - -/****************************************************************************/ - -STATIC struct DateStamp * -timeval_to_datestamp(struct DateStamp * ds,const struct timeval * tv) -{ - assert( ds != NULL && tv != NULL ); - - ds->ds_Days = (tv->tv_secs / (24 * 60 * 60)); - ds->ds_Minute = (tv->tv_secs % (24 * 60 * 60)) / 60; - ds->ds_Tick = (tv->tv_secs % 60) * TICKS_PER_SECOND + (TICKS_PER_SECOND * tv->tv_micro) / 1000000; - - fix_datestamp(ds); - - return(ds); -} - -/****************************************************************************/ - -STATIC VOID -add_dates(struct DateStamp * to,const struct DateStamp * from) -{ - assert( to != NULL && from != NULL ); - - to->ds_Tick += from->ds_Tick; - to->ds_Minute += from->ds_Minute; - to->ds_Days += from->ds_Days; - - fix_datestamp(to); -} - -/****************************************************************************/ - -STATIC VOID -map_descriptor_sets( - const fd_set * input_fds, - int num_input_fds, - - fd_set * socket_fds, - int num_socket_fds, - int * total_socket_fd_ptr, - - fd_set * file_fds, - int num_file_fds, - int * total_file_fd_ptr) -{ - ENTER(); - - SHOWPOINTER(input_fds); - SHOWVALUE(num_input_fds); - - SHOWPOINTER(socket_fds); - SHOWVALUE(num_socket_fds); - - SHOWPOINTER(file_fds); - SHOWVALUE(num_file_fds); - - /* This routine maps file descriptor sets - * from one format to another. We map - * socket descriptors and regular file - * descriptor sets. - */ - if(input_fds != NULL && num_input_fds > 0) - { - int total_socket_fd; - int total_file_fd; - struct fd * fd; - int file_fd; - - total_socket_fd = (*total_socket_fd_ptr); - total_file_fd = (*total_file_fd_ptr); - - SHOWVALUE(total_socket_fd); - SHOWVALUE(total_file_fd); - - for(file_fd = 0 ; file_fd < num_input_fds ; file_fd++) - { - if(NOT FD_ISSET(file_fd,input_fds)) - continue; - - D(("descriptor %ld is set",file_fd)); - - fd = get_file_descriptor(file_fd); - if(fd == NULL) - { - SHOWMSG("but no file is attached to it"); - continue; - } - - /* Is this a socket descriptor? */ - if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) - { - int socket_fd = (int)fd->fd_DefaultFile; - - D(("corresponds to socket #%ld",socket_fd)); - - if(socket_fds != NULL && socket_fd < num_socket_fds) - { - SHOWMSG("setting it"); - - FD_SET(socket_fd,socket_fds); - - if(total_socket_fd < socket_fd+1) - total_socket_fd = socket_fd+1; - } - else - { - SHOWMSG("can't set it, though"); - } - } - else - { - /* We watch files bound to console streams and disk - files which may have data stored in them. */ - if(FLAG_IS_SET(fd->fd_Flags,FDF_STDIO)) - { - SHOWMSG("this is a file, or otherwise unsuitable"); - continue; - } - - if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE)) - { - SHOWMSG("this is an interactive stream"); - } - else - { - D_S(struct FileInfoBlock,fib); - - /* Let's see if we can examine the file. Some file systems - may not support this. */ - if(CANNOT __safe_examine_file_handle(fd->fd_DefaultFile,fib)) - { - SHOWMSG("file is unusable; we cannot examine the file."); - continue; - } - - /* If we can't make assumptions about the file position, then - this better be a pipe. */ - if(FLAG_IS_CLEAR(fd->fd_Flags,FDF_CACHE_POSITION) && fib->fib_DirEntryType != ST_PIPEFILE) - { - SHOWMSG("file is unusable; it is not a file system and not a pipe."); - continue; - } - } - - if(file_fds != NULL && file_fd < num_file_fds) - { - SHOWMSG("setting it"); - - FD_SET(file_fd,file_fds); - - if(total_file_fd < file_fd+1) - total_file_fd = file_fd+1; - } - else - { - SHOWMSG("can't set it, though"); - } - } - } - - (*total_socket_fd_ptr) = total_socket_fd; - (*total_file_fd_ptr) = total_file_fd; - - SHOWVALUE(total_socket_fd); - SHOWVALUE(total_file_fd); - } - - LEAVE(); -} - -/****************************************************************************/ - -STATIC VOID -remap_descriptor_sets( - const fd_set * socket_fds, - int num_socket_fds, - - const fd_set * file_fds, - int num_file_fds, - - fd_set * output_fds, - int num_output_fds) -{ - ENTER(); - - SHOWPOINTER(socket_fds); - SHOWVALUE(num_socket_fds); - - SHOWPOINTER(file_fds); - SHOWVALUE(num_file_fds); - - SHOWPOINTER(output_fds); - SHOWVALUE(num_output_fds); - - /* This routine reverses the mapping established - * above. We map the file and socket descriptor - * sets back into the original set. - */ - if(output_fds != NULL && num_output_fds > 0) - { - zero_fd_set(output_fds,num_output_fds); - - if(socket_fds != NULL && num_socket_fds > 0) - { - struct fd * fd; - int output_fd; - int socket_fd; - - SHOWMSG("taking care of the sockets"); - - for(socket_fd = 0 ; socket_fd < num_socket_fds ; socket_fd++) - { - if(NOT FD_ISSET(socket_fd,socket_fds)) - continue; - - for(output_fd = 0 ; output_fd < num_output_fds ; output_fd++) - { - fd = get_file_descriptor(output_fd); - if(fd != NULL && FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET) && (int)fd->fd_DefaultFile == socket_fd) - { - assert( output_fd < num_output_fds ); - assert( FLAG_IS_SET(__fd[output_fd]->fd_Flags,FDF_IS_SOCKET) ); - - D(("setting file %ld for socket #%ld",output_fd,socket_fd)); - - FD_SET(output_fd,output_fds); - } - } - } - } - - if(file_fds != NULL && num_file_fds > 0) - { - int file_fd; - - SHOWMSG("taking care of the files"); - - for(file_fd = 0 ; file_fd < num_file_fds ; file_fd++) - { - if(FD_ISSET(file_fd,file_fds)) - { - int output_fd = file_fd; - - assert( output_fd < num_output_fds ); - assert( FLAG_IS_CLEAR(__fd[output_fd]->fd_Flags,FDF_IS_SOCKET) ); - - D(("setting file %ld",file_fd)); - - FD_SET(output_fd,output_fds); - } - } - } - } - else - { - SHOWMSG("no output necessary"); - } - - LEAVE(); -} - -/****************************************************************************/ - -STATIC VOID -get_num_descriptors_used(int num_fds,int * num_socket_used_ptr,int * num_file_used_ptr) -{ - int num_socket_used = 0; - int num_file_used = 0; - int which_file_fd; - struct fd * fd; - - assert( num_socket_used_ptr != NULL ); - assert( num_file_used_ptr != NULL ); - - SHOWMSG("figuring out which file descriptors are in use"); - - for(which_file_fd = 0 ; which_file_fd < num_fds ; which_file_fd++) - { - fd = get_file_descriptor(which_file_fd); - if(fd != NULL) - { - if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) - { - int which_socket_fd = (int)fd->fd_DefaultFile; - - if(num_socket_used < which_socket_fd+1) - num_socket_used = which_socket_fd+1; - } - else - { - if(num_file_used < which_file_fd+1) - num_file_used = which_file_fd+1; - } - } - } - - (*num_socket_used_ptr) = num_socket_used; - (*num_file_used_ptr) = num_file_used; -} - -/****************************************************************************/ - int select(int num_fds,fd_set *read_fds,fd_set *write_fds,fd_set *except_fds,struct timeval *timeout) { - fd_set * backup_socket_read_fds = NULL; - fd_set * backup_socket_write_fds = NULL; - fd_set * backup_socket_except_fds = NULL; + int result; - fd_set * backup_file_read_fds = NULL; - fd_set * backup_file_write_fds = NULL; + result = __select(num_fds,read_fds,write_fds,except_fds,timeout,NULL); - fd_set * socket_read_fds = NULL; - fd_set * socket_write_fds = NULL; - fd_set * socket_except_fds = NULL; - int total_socket_fd; - - fd_set * file_read_fds = NULL; - fd_set * file_write_fds = NULL; - int total_file_fd; - - struct fd * fd; - int result = ERROR; - - int num_socket_used; - int num_file_used; - int i; - - ENTER(); - - SHOWVALUE(num_fds); - SHOWPOINTER(read_fds); - SHOWPOINTER(write_fds); - SHOWPOINTER(except_fds); - SHOWPOINTER(timeout); - - if(timeout != NULL) - { - SHOWVALUE(timeout->tv_secs); - SHOWVALUE(timeout->tv_micro); - } - - assert(__SocketBase != NULL); - - if(__check_abort_enabled) - __check_abort(); - - /* Figure out the number of file and socket descriptors in use. */ - get_num_descriptors_used(num_fds,&num_socket_used,&num_file_used); - - SHOWVALUE(num_socket_used); - SHOWVALUE(num_file_used); - - /* Dynamically allocate the tables to keep track of which descriptor - * is ready for I/O. - */ - if(read_fds != NULL) - { - if(num_socket_used > 0) - { - SHOWMSG("allocating read socket fd_set"); - - socket_read_fds = allocate_fd_set(num_socket_used,NULL); - if(socket_read_fds == NULL) - goto out; - } - - if(num_file_used > 0) - { - SHOWMSG("allocating read file fd_set"); - - file_read_fds = allocate_fd_set(num_file_used,NULL); - if(file_read_fds == NULL) - goto out; - } - } - - if(write_fds != NULL) - { - if(num_socket_used > 0) - { - SHOWMSG("allocating write socket fd_set"); - - socket_write_fds = allocate_fd_set(num_socket_used,NULL); - if(socket_write_fds == NULL) - goto out; - } - - if(num_file_used > 0) - { - SHOWMSG("allocating write file fd_set"); - - file_write_fds = allocate_fd_set(num_file_used,NULL); - if(file_write_fds == NULL) - goto out; - } - } - - if(except_fds != NULL) - { - if(num_socket_used > 0) - { - SHOWMSG("allocating except socket fd_set"); - - socket_except_fds = allocate_fd_set(num_socket_used,NULL); - if(socket_except_fds == NULL) - goto out; - } - } - - total_socket_fd = 0; - total_file_fd = 0; - - SHOWMSG("mapping the fd_sets"); - - /* Translate from the tables the caller provided to us - * to the local copies, which take the files and sockets - * into account. - */ - __stdio_lock(); - - map_descriptor_sets(read_fds, num_fds, socket_read_fds, num_socket_used, &total_socket_fd, file_read_fds, num_file_used, &total_file_fd); - map_descriptor_sets(write_fds, num_fds, socket_write_fds, num_socket_used, &total_socket_fd, file_write_fds, num_file_used, &total_file_fd); - map_descriptor_sets(except_fds, num_fds, socket_except_fds, num_socket_used, &total_socket_fd, NULL, 0, &total_file_fd); - - __stdio_unlock(); - - /* Wait for socket input? */ - if(total_socket_fd > 0) - { - SHOWMSG("we have to deal with sockets"); - - /* Wait for file input, too? */ - if((total_file_fd > 0) && (timeout == NULL || timeout->tv_secs > 0 || timeout->tv_micro > 0)) - { - struct DateStamp stop_when; - struct timeval zero; - ULONG break_mask; - BOOL got_input; - BOOL got_output; - - SHOWMSG("we also have to deal with files"); - - /* We may poll the sockets and files several times in a row. - * The results stored in the tables can tell only a single - * story, though. This is why we need to keep a backup copy - * of each table around, to be used for each iteration of - * the loop in which the sockets and files are checked. - */ - if(read_fds != NULL) - { - if(num_socket_used > 0) - { - SHOWMSG("allocating backup read socket fd_set"); - - backup_socket_read_fds = allocate_fd_set(num_socket_used,socket_read_fds); - if(backup_socket_read_fds == NULL) - goto out; - } - - if(num_file_used > 0) - { - SHOWMSG("allocating backup read file fd_set"); - - backup_file_read_fds = allocate_fd_set(num_file_used,file_read_fds); - if(backup_file_read_fds == NULL) - goto out; - } - } - - if(write_fds != NULL) - { - if(num_socket_used > 0) - { - SHOWMSG("allocating backup write socket fd_set"); - - backup_socket_write_fds = allocate_fd_set(num_socket_used,socket_write_fds); - if(backup_socket_write_fds == NULL) - goto out; - } - - if(num_file_used > 0) - { - SHOWMSG("allocating backup write file fd_set"); - - backup_file_write_fds = allocate_fd_set(num_file_used,file_write_fds); - if(backup_file_write_fds == NULL) - goto out; - } - } - - if(except_fds != NULL) - { - if(num_socket_used > 0) - { - SHOWMSG("allocating backup except socket fd_set"); - - backup_socket_except_fds = allocate_fd_set(num_socket_used,socket_except_fds); - if(backup_socket_except_fds == NULL) - goto out; - } - } - - /* We are going to poll all streams; for the timeout - * feature to work, we absolutely must know when to - * stop polling. - * - * Why aren't we using asynchronous DOS packets? - * The answer is that once a packet is sent, you - * cannot easily abort it. Polling is safer in - * that respect. Yes, I know that ACTION_STACK - * can be used to fake input to a console stream, - * but I'd rather not rely upon it. - */ - if(timeout != NULL) - { - struct DateStamp datestamp_timeout; - - PROFILE_OFF(); - DateStamp(&stop_when); - PROFILE_ON(); - - add_dates(&stop_when,timeval_to_datestamp(&datestamp_timeout,timeout)); - } - else - { - /* No timeout, poll until we are interrupted - * or get input from any of the files. It's - * not really necessary to initialize this - * timeval, but it keeps the compiler happy. - */ - memset(&stop_when,0,sizeof(stop_when)); - } - - while(TRUE) - { - /* Check for break signal. */ - if(__check_abort_enabled) - __check_abort(); - - /* Delay for a tick to avoid busy-waiting. */ - PROFILE_OFF(); - Delay(1); - PROFILE_ON(); - - /* This tells WaitSelect() to poll the sockets for input. */ - zero.tv_secs = 0; - zero.tv_micro = 0; - - /* Signals to stop on; we want to stop when a break signal arrives. */ - if(__check_abort_enabled) - break_mask = SIGBREAKF_CTRL_C; - else - break_mask = 0; - - /* Check for socket input. */ - PROFILE_OFF(); - result = __WaitSelect(total_socket_fd,socket_read_fds,socket_write_fds,socket_except_fds,&zero,&break_mask); - PROFILE_ON(); - - /* Stop if a break signal arrives. */ - if((result < 0 && __get_errno() == EINTR) || FLAG_IS_SET(break_mask,SIGBREAKF_CTRL_C)) - { - SetSignal(SIGBREAKF_CTRL_C,SIGBREAKF_CTRL_C); - __check_abort(); - } - - /* Stop if the return value from WaitSelect() is negative (timeout, abort or serious error). */ - if(result < 0) - break; - - /* Check all files for input. We also poll - * them for input, but each with a little - * delay of about 1 microsecond. We stop - * as soon as we find one file that has - * input in it. - */ - for(i = 0 ; i < total_file_fd ; i++) - { - got_input = got_output = FALSE; - - fd = get_file_descriptor(i); - if(fd != NULL) - { - if(file_read_fds != NULL && FD_ISSET(i,file_read_fds)) - { - if(FLAG_IS_SET(fd->fd_Flags,FDF_READ)) - { - assert( FLAG_IS_CLEAR(fd->fd_Flags,FDF_IS_SOCKET) && FLAG_IS_CLEAR(fd->fd_Flags,FDF_STDIO) ); - - /* Does this one have input? */ - PROFILE_OFF(); - - if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE)) - { - /* For an interactive stream, we simply ask. */ - if(WaitForChar(fd->fd_DefaultFile,1)) - got_input = TRUE; - } - else - { - D_S(struct FileInfoBlock,fib); - - /* For a file we check how much data is now in the file and - compare it against the current file position. If there's - unread data in the file, we will be able to read from it. - For pipes, any data reported to be in the "file" indicates - that there is something worth reading available. */ - if(__safe_examine_file_handle(fd->fd_DefaultFile,fib)) - { - if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) - { - /* Is there new data to read? */ - if((ULONG)fib->fib_Size > fd->fd_Position) - got_input = TRUE; - } - else - { - /* Does the pipe contain any data to read? */ - if(fib->fib_Size != 0) - got_input = TRUE; - } - } - } - - PROFILE_ON(); - } - } - - if(file_write_fds != NULL && FD_ISSET(i,file_write_fds)) - { - /* If this is a writable stream, assume that we can - * write to it. ZZZ AmigaDOS needs a method to check - * if the write will block. - */ - if(FLAG_IS_SET(fd->fd_Flags,FDF_WRITE)) - { - assert( FLAG_IS_CLEAR(fd->fd_Flags,FDF_IS_SOCKET) ); - - got_output = TRUE; - } - } - } - - if(got_input || got_output) - { - /* Mark one more descriptor as - * having input/output. - */ - result++; - } - - if(file_read_fds != NULL && NOT got_input) - FD_CLR(i,file_read_fds); - - if(file_write_fds != NULL && NOT got_output) - FD_CLR(i,file_write_fds); - } - - /* Did we get any input? If so, stop polling. */ - if(result > 0) - break; - - /* If a timeout was set, check if we are already - * beyond the point of time when we should have - * stopped polling. - */ - if(timeout != NULL) - { - struct DateStamp now; - - PROFILE_OFF(); - DateStamp(&now); - PROFILE_ON(); - - if(CompareDates(&now,&stop_when) <= 0) - break; - } - - /* No I/O ready yet. Restore the sets and retry... */ - copy_fd_set(socket_read_fds, backup_socket_read_fds, num_socket_used); - copy_fd_set(socket_write_fds, backup_socket_write_fds, num_socket_used); - copy_fd_set(socket_except_fds, backup_socket_except_fds, num_socket_used); - - copy_fd_set(file_read_fds, backup_file_read_fds, num_file_used); - copy_fd_set(file_write_fds, backup_file_write_fds, num_file_used); - } - } - else - { - ULONG break_mask; - - if(__check_abort_enabled) - break_mask = SIGBREAKF_CTRL_C; - else - break_mask = 0; - - PROFILE_OFF(); - result = __WaitSelect(total_socket_fd,socket_read_fds,socket_write_fds,socket_except_fds,timeout,&break_mask); - PROFILE_ON(); - - if((result < 0 && __get_errno() == EINTR) || FLAG_IS_SET(break_mask,SIGBREAKF_CTRL_C)) - { - SetSignal(SIGBREAKF_CTRL_C,SIGBREAKF_CTRL_C); - __check_abort(); - } - } - } - else - { - /* Wait for file input? */ - if((total_file_fd > 0) && (timeout == NULL || timeout->tv_secs > 0 || timeout->tv_micro > 0)) - { - struct DateStamp stop_when; - BOOL got_input; - BOOL got_output; - - SHOWMSG("we have to deal with files"); - - if(num_file_used > 0) - { - if(read_fds != NULL) - { - SHOWMSG("allocating backup file read fd_set"); - - backup_file_read_fds = allocate_fd_set(num_file_used,file_read_fds); - if(backup_file_read_fds == NULL) - goto out; - } - - if(write_fds != NULL) - { - SHOWMSG("allocating backup file write fd_set"); - - backup_file_write_fds = allocate_fd_set(num_file_used,file_write_fds); - if(backup_file_write_fds == NULL) - goto out; - } - } - - if(timeout != NULL) - { - struct DateStamp datestamp_timeout; - - PROFILE_OFF(); - DateStamp(&stop_when); - PROFILE_ON(); - - add_dates(&stop_when,timeval_to_datestamp(&datestamp_timeout,timeout)); - } - else - { - memset(&stop_when,0,sizeof(stop_when)); - } - - while(TRUE) - { - if(__check_abort_enabled) - __check_abort(); - - PROFILE_OFF(); - Delay(1); - PROFILE_ON(); - - result = 0; - - for(i = 0 ; i < total_file_fd ; i++) - { - got_input = got_output = FALSE; - - fd = get_file_descriptor(i); - if(fd != NULL) - { - if(file_read_fds != NULL && FD_ISSET(i,file_read_fds)) - { - if(FLAG_IS_SET(fd->fd_Flags,FDF_READ)) - { - assert( FLAG_IS_CLEAR(fd->fd_Flags,FDF_IS_SOCKET) && FLAG_IS_CLEAR(fd->fd_Flags,FDF_STDIO) ); - - PROFILE_OFF(); - - if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE)) - { - if(WaitForChar(fd->fd_DefaultFile,1)) - got_input = TRUE; - } - else - { - D_S(struct FileInfoBlock,fib); - - if(__safe_examine_file_handle(fd->fd_DefaultFile,fib)) - { - if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) - { - if((ULONG)fib->fib_Size > fd->fd_Position) - got_input = TRUE; - } - else - { - if(fib->fib_Size != 0) - got_input = TRUE; - } - } - } - - PROFILE_ON(); - } - } - - if(file_write_fds != NULL && FD_ISSET(i,file_write_fds)) - { - if(FLAG_IS_SET(fd->fd_Flags,FDF_WRITE)) - { - assert( FLAG_IS_CLEAR(fd->fd_Flags,FDF_IS_SOCKET) ); - - got_output = TRUE; - } - } - } - - if(got_input || got_output) - result++; - - if(file_read_fds != NULL && NOT got_input) - FD_CLR(i,file_read_fds); - - if(file_write_fds != NULL && NOT got_output) - FD_CLR(i,file_write_fds); - } - - if(result > 0) - break; - - if(timeout != NULL) - { - struct DateStamp now; - - PROFILE_OFF(); - DateStamp(&now); - PROFILE_ON(); - - if(CompareDates(&now,&stop_when) <= 0) - break; - } - - copy_fd_set(file_read_fds, backup_file_read_fds, num_file_used); - copy_fd_set(file_write_fds, backup_file_write_fds, num_file_used); - } - } - else - { - SHOWMSG("no files to worry about"); - } - } - - /* The descriptor sets remain unchanged in - * case of error. - */ - if(result >= 0) - { - SHOWMSG("remapping fd_sets"); - - __stdio_lock(); - - remap_descriptor_sets(socket_read_fds, total_socket_fd, file_read_fds, total_file_fd, read_fds, num_fds); - remap_descriptor_sets(socket_write_fds, total_socket_fd, file_write_fds, total_file_fd, write_fds, num_fds); - remap_descriptor_sets(socket_except_fds, total_socket_fd, NULL, 0, except_fds, num_fds); - - __stdio_unlock(); - } - - if(__check_abort_enabled) - __check_abort(); - - out: - - free_fd_set(socket_read_fds); - free_fd_set(socket_write_fds); - free_fd_set(socket_except_fds); - - free_fd_set(file_read_fds); - free_fd_set(file_write_fds); - - free_fd_set(backup_socket_read_fds); - free_fd_set(backup_socket_write_fds); - free_fd_set(backup_socket_except_fds); - - free_fd_set(backup_file_read_fds); - free_fd_set(backup_file_write_fds); - - RETURN(result); return(result); } diff --git a/library/socket_select_signal.c b/library/socket_select_signal.c new file mode 100644 index 0000000..314b0ad --- /dev/null +++ b/library/socket_select_signal.c @@ -0,0 +1,1141 @@ +/* + * $Id: socket_select_signal.c,v 1.1 2006-04-05 07:53:24 obarthel Exp $ + * + * :ts=4 + * + * Portable ISO 'C' (1994) runtime library for the Amiga computer + * Copyright (c) 2002-2006 by Olaf Barthel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Neither the name of Olaf Barthel nor the names of contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. + */ + +#if defined(SOCKET_SUPPORT) + +/****************************************************************************/ + +#ifndef _SOCKET_HEADERS_H +#include "socket_headers.h" +#endif /* _SOCKET_HEADERS_H */ + +/****************************************************************************/ + +#ifndef _STDLIB_MEMORY_H +#include "stdlib_memory.h" +#endif /* _STDLIB_MEMORY_H */ + +/****************************************************************************/ + +STATIC VOID +copy_fd_set(fd_set * to,fd_set * from,int num_fds) +{ + ENTER(); + + SHOWPOINTER(to); + SHOWPOINTER(from); + SHOWVALUE(num_fds); + + if(to != NULL && from != NULL && num_fds > 0) + { + size_t num_bytes; + + num_bytes = sizeof(unsigned long) * ((num_fds + 31) / 32); + + SHOWVALUE(num_bytes); + + memmove(to,from,num_bytes); + } + + LEAVE(); +} + +/****************************************************************************/ + +STATIC VOID +zero_fd_set(fd_set * set,int num_fds) +{ + ENTER(); + + SHOWPOINTER(set); + SHOWVALUE(num_fds); + + if(set != NULL && num_fds > 0) + { + size_t num_bytes; + + num_bytes = sizeof(unsigned long) * ((num_fds + 31) / 32); + + SHOWVALUE(num_bytes); + + memset(set,0,num_bytes); + } + + LEAVE(); +} + +/****************************************************************************/ + +STATIC fd_set * +allocate_fd_set(int num_fds,fd_set * duplicate_this_set) +{ + fd_set * result = NULL; + size_t num_bytes; + fd_set * set; + + ENTER(); + + assert( num_fds > 0 ); + + SHOWVALUE(num_fds); + + num_bytes = sizeof(unsigned long) * ((num_fds + 31) / 32); + + SHOWVALUE(num_bytes); + + set = (fd_set *)malloc(num_bytes); + if(set != NULL) + { + if(duplicate_this_set != NULL) + copy_fd_set(set,duplicate_this_set,num_fds); + else + zero_fd_set(set,num_fds); + + result = set; + } + + RETURN(result); + return(result); +} + +/****************************************************************************/ + +STATIC VOID +free_fd_set(fd_set * set) +{ + if(set != NULL) + free(set); +} + +/****************************************************************************/ + +STATIC struct fd * +get_file_descriptor(int file_descriptor) +{ + struct fd * result = NULL; + struct fd * fd; + + __stdio_lock(); + + if(file_descriptor < 0 || file_descriptor >= __num_fd) + goto out; + + assert( __fd != NULL ); + + fd = __fd[file_descriptor]; + + assert( fd != NULL ); + + if(FLAG_IS_CLEAR(fd->fd_Flags,FDF_IN_USE)) + goto out; + + result = fd; + + out: + + __stdio_unlock(); + + return(result); +} + +/****************************************************************************/ + +STATIC VOID +fix_datestamp(struct DateStamp * ds) +{ + const LONG ticks_per_minute = 60 * TICKS_PER_SECOND; + const LONG minutes_per_day = 24 * 60; + + assert( ds != NULL ); + + while(ds->ds_Minute >= minutes_per_day || + ds->ds_Tick >= ticks_per_minute) + { + if(ds->ds_Minute >= minutes_per_day) + { + ds->ds_Days++; + + ds->ds_Minute -= minutes_per_day; + } + + if(ds->ds_Tick >= ticks_per_minute) + { + ds->ds_Minute++; + + ds->ds_Tick -= ticks_per_minute; + } + } +} + +/****************************************************************************/ + +STATIC struct DateStamp * +timeval_to_datestamp(struct DateStamp * ds,const struct timeval * tv) +{ + assert( ds != NULL && tv != NULL ); + + ds->ds_Days = (tv->tv_secs / (24 * 60 * 60)); + ds->ds_Minute = (tv->tv_secs % (24 * 60 * 60)) / 60; + ds->ds_Tick = (tv->tv_secs % 60) * TICKS_PER_SECOND + (TICKS_PER_SECOND * tv->tv_micro) / 1000000; + + fix_datestamp(ds); + + return(ds); +} + +/****************************************************************************/ + +STATIC VOID +add_dates(struct DateStamp * to,const struct DateStamp * from) +{ + assert( to != NULL && from != NULL ); + + to->ds_Tick += from->ds_Tick; + to->ds_Minute += from->ds_Minute; + to->ds_Days += from->ds_Days; + + fix_datestamp(to); +} + +/****************************************************************************/ + +STATIC VOID +map_descriptor_sets( + const fd_set * input_fds, + int num_input_fds, + + fd_set * socket_fds, + int num_socket_fds, + int * total_socket_fd_ptr, + + fd_set * file_fds, + int num_file_fds, + int * total_file_fd_ptr) +{ + ENTER(); + + SHOWPOINTER(input_fds); + SHOWVALUE(num_input_fds); + + SHOWPOINTER(socket_fds); + SHOWVALUE(num_socket_fds); + + SHOWPOINTER(file_fds); + SHOWVALUE(num_file_fds); + + /* This routine maps file descriptor sets + * from one format to another. We map + * socket descriptors and regular file + * descriptor sets. + */ + if(input_fds != NULL && num_input_fds > 0) + { + int total_socket_fd; + int total_file_fd; + struct fd * fd; + int file_fd; + + total_socket_fd = (*total_socket_fd_ptr); + total_file_fd = (*total_file_fd_ptr); + + SHOWVALUE(total_socket_fd); + SHOWVALUE(total_file_fd); + + for(file_fd = 0 ; file_fd < num_input_fds ; file_fd++) + { + if(NOT FD_ISSET(file_fd,input_fds)) + continue; + + D(("descriptor %ld is set",file_fd)); + + fd = get_file_descriptor(file_fd); + if(fd == NULL) + { + SHOWMSG("but no file is attached to it"); + continue; + } + + /* Is this a socket descriptor? */ + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) + { + int socket_fd = (int)fd->fd_DefaultFile; + + D(("corresponds to socket #%ld",socket_fd)); + + if(socket_fds != NULL && socket_fd < num_socket_fds) + { + SHOWMSG("setting it"); + + FD_SET(socket_fd,socket_fds); + + if(total_socket_fd < socket_fd+1) + total_socket_fd = socket_fd+1; + } + else + { + SHOWMSG("can't set it, though"); + } + } + else + { + /* We watch files bound to console streams and disk + files which may have data stored in them. */ + if(FLAG_IS_SET(fd->fd_Flags,FDF_STDIO)) + { + SHOWMSG("this is a file, or otherwise unsuitable"); + continue; + } + + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE)) + { + SHOWMSG("this is an interactive stream"); + } + else + { + D_S(struct FileInfoBlock,fib); + + /* Let's see if we can examine the file. Some file systems + may not support this. */ + if(CANNOT __safe_examine_file_handle(fd->fd_DefaultFile,fib)) + { + SHOWMSG("file is unusable; we cannot examine the file."); + continue; + } + + /* If we can't make assumptions about the file position, then + this better be a pipe. */ + if(FLAG_IS_CLEAR(fd->fd_Flags,FDF_CACHE_POSITION) && fib->fib_DirEntryType != ST_PIPEFILE) + { + SHOWMSG("file is unusable; it is not a file system and not a pipe."); + continue; + } + } + + if(file_fds != NULL && file_fd < num_file_fds) + { + SHOWMSG("setting it"); + + FD_SET(file_fd,file_fds); + + if(total_file_fd < file_fd+1) + total_file_fd = file_fd+1; + } + else + { + SHOWMSG("can't set it, though"); + } + } + } + + (*total_socket_fd_ptr) = total_socket_fd; + (*total_file_fd_ptr) = total_file_fd; + + SHOWVALUE(total_socket_fd); + SHOWVALUE(total_file_fd); + } + + LEAVE(); +} + +/****************************************************************************/ + +STATIC VOID +remap_descriptor_sets( + const fd_set * socket_fds, + int num_socket_fds, + + const fd_set * file_fds, + int num_file_fds, + + fd_set * output_fds, + int num_output_fds) +{ + ENTER(); + + SHOWPOINTER(socket_fds); + SHOWVALUE(num_socket_fds); + + SHOWPOINTER(file_fds); + SHOWVALUE(num_file_fds); + + SHOWPOINTER(output_fds); + SHOWVALUE(num_output_fds); + + /* This routine reverses the mapping established + * above. We map the file and socket descriptor + * sets back into the original set. + */ + if(output_fds != NULL && num_output_fds > 0) + { + zero_fd_set(output_fds,num_output_fds); + + if(socket_fds != NULL && num_socket_fds > 0) + { + struct fd * fd; + int output_fd; + int socket_fd; + + SHOWMSG("taking care of the sockets"); + + for(socket_fd = 0 ; socket_fd < num_socket_fds ; socket_fd++) + { + if(NOT FD_ISSET(socket_fd,socket_fds)) + continue; + + for(output_fd = 0 ; output_fd < num_output_fds ; output_fd++) + { + fd = get_file_descriptor(output_fd); + if(fd != NULL && FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET) && (int)fd->fd_DefaultFile == socket_fd) + { + assert( output_fd < num_output_fds ); + assert( FLAG_IS_SET(__fd[output_fd]->fd_Flags,FDF_IS_SOCKET) ); + + D(("setting file %ld for socket #%ld",output_fd,socket_fd)); + + FD_SET(output_fd,output_fds); + } + } + } + } + + if(file_fds != NULL && num_file_fds > 0) + { + int file_fd; + + SHOWMSG("taking care of the files"); + + for(file_fd = 0 ; file_fd < num_file_fds ; file_fd++) + { + if(FD_ISSET(file_fd,file_fds)) + { + int output_fd = file_fd; + + assert( output_fd < num_output_fds ); + assert( FLAG_IS_CLEAR(__fd[output_fd]->fd_Flags,FDF_IS_SOCKET) ); + + D(("setting file %ld",file_fd)); + + FD_SET(output_fd,output_fds); + } + } + } + } + else + { + SHOWMSG("no output necessary"); + } + + LEAVE(); +} + +/****************************************************************************/ + +STATIC VOID +get_num_descriptors_used(int num_fds,int * num_socket_used_ptr,int * num_file_used_ptr) +{ + int num_socket_used = 0; + int num_file_used = 0; + int which_file_fd; + struct fd * fd; + + assert( num_socket_used_ptr != NULL ); + assert( num_file_used_ptr != NULL ); + + SHOWMSG("figuring out which file descriptors are in use"); + + for(which_file_fd = 0 ; which_file_fd < num_fds ; which_file_fd++) + { + fd = get_file_descriptor(which_file_fd); + if(fd != NULL) + { + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_SOCKET)) + { + int which_socket_fd = (int)fd->fd_DefaultFile; + + if(num_socket_used < which_socket_fd+1) + num_socket_used = which_socket_fd+1; + } + else + { + if(num_file_used < which_file_fd+1) + num_file_used = which_file_fd+1; + } + } + } + + (*num_socket_used_ptr) = num_socket_used; + (*num_file_used_ptr) = num_file_used; +} + +/****************************************************************************/ + +int +__select(int num_fds,fd_set *read_fds,fd_set *write_fds,fd_set *except_fds,struct timeval *timeout,ULONG * signal_mask_ptr) +{ + fd_set * backup_socket_read_fds = NULL; + fd_set * backup_socket_write_fds = NULL; + fd_set * backup_socket_except_fds = NULL; + + fd_set * backup_file_read_fds = NULL; + fd_set * backup_file_write_fds = NULL; + + fd_set * socket_read_fds = NULL; + fd_set * socket_write_fds = NULL; + fd_set * socket_except_fds = NULL; + int total_socket_fd; + + fd_set * file_read_fds = NULL; + fd_set * file_write_fds = NULL; + int total_file_fd; + + ULONG signal_mask; + + struct fd * fd; + int result = ERROR; + + int num_socket_used; + int num_file_used; + int i; + + ENTER(); + + SHOWVALUE(num_fds); + SHOWPOINTER(read_fds); + SHOWPOINTER(write_fds); + SHOWPOINTER(except_fds); + SHOWPOINTER(timeout); + + if(timeout != NULL) + { + SHOWVALUE(timeout->tv_secs); + SHOWVALUE(timeout->tv_micro); + } + + assert(__SocketBase != NULL); + + if(signal_mask_ptr != NULL) + { + signal_mask = (*signal_mask_ptr); + (*signal_mask_ptr) = 0; + } + else + { + signal_mask = 0; + } + + if(__check_abort_enabled) + __check_abort(); + + /* Figure out the number of file and socket descriptors in use. */ + get_num_descriptors_used(num_fds,&num_socket_used,&num_file_used); + + SHOWVALUE(num_socket_used); + SHOWVALUE(num_file_used); + + /* Dynamically allocate the tables to keep track of which descriptor + * is ready for I/O. + */ + if(read_fds != NULL) + { + if(num_socket_used > 0) + { + SHOWMSG("allocating read socket fd_set"); + + socket_read_fds = allocate_fd_set(num_socket_used,NULL); + if(socket_read_fds == NULL) + goto out; + } + + if(num_file_used > 0) + { + SHOWMSG("allocating read file fd_set"); + + file_read_fds = allocate_fd_set(num_file_used,NULL); + if(file_read_fds == NULL) + goto out; + } + } + + if(write_fds != NULL) + { + if(num_socket_used > 0) + { + SHOWMSG("allocating write socket fd_set"); + + socket_write_fds = allocate_fd_set(num_socket_used,NULL); + if(socket_write_fds == NULL) + goto out; + } + + if(num_file_used > 0) + { + SHOWMSG("allocating write file fd_set"); + + file_write_fds = allocate_fd_set(num_file_used,NULL); + if(file_write_fds == NULL) + goto out; + } + } + + if(except_fds != NULL) + { + if(num_socket_used > 0) + { + SHOWMSG("allocating except socket fd_set"); + + socket_except_fds = allocate_fd_set(num_socket_used,NULL); + if(socket_except_fds == NULL) + goto out; + } + } + + total_socket_fd = 0; + total_file_fd = 0; + + SHOWMSG("mapping the fd_sets"); + + /* Translate from the tables the caller provided to us + * to the local copies, which take the files and sockets + * into account. + */ + __stdio_lock(); + + map_descriptor_sets(read_fds, num_fds, socket_read_fds, num_socket_used, &total_socket_fd, file_read_fds, num_file_used, &total_file_fd); + map_descriptor_sets(write_fds, num_fds, socket_write_fds, num_socket_used, &total_socket_fd, file_write_fds, num_file_used, &total_file_fd); + map_descriptor_sets(except_fds, num_fds, socket_except_fds, num_socket_used, &total_socket_fd, NULL, 0, &total_file_fd); + + __stdio_unlock(); + + /* Wait for socket input? */ + if(total_socket_fd > 0) + { + SHOWMSG("we have to deal with sockets"); + + /* Wait for file input, too? */ + if((total_file_fd > 0) && (timeout == NULL || timeout->tv_secs > 0 || timeout->tv_micro > 0)) + { + struct DateStamp stop_when; + struct timeval zero; + ULONG break_mask; + BOOL got_input; + BOOL got_output; + + SHOWMSG("we also have to deal with files"); + + /* We may poll the sockets and files several times in a row. + * The results stored in the tables can tell only a single + * story, though. This is why we need to keep a backup copy + * of each table around, to be used for each iteration of + * the loop in which the sockets and files are checked. + */ + if(read_fds != NULL) + { + if(num_socket_used > 0) + { + SHOWMSG("allocating backup read socket fd_set"); + + backup_socket_read_fds = allocate_fd_set(num_socket_used,socket_read_fds); + if(backup_socket_read_fds == NULL) + goto out; + } + + if(num_file_used > 0) + { + SHOWMSG("allocating backup read file fd_set"); + + backup_file_read_fds = allocate_fd_set(num_file_used,file_read_fds); + if(backup_file_read_fds == NULL) + goto out; + } + } + + if(write_fds != NULL) + { + if(num_socket_used > 0) + { + SHOWMSG("allocating backup write socket fd_set"); + + backup_socket_write_fds = allocate_fd_set(num_socket_used,socket_write_fds); + if(backup_socket_write_fds == NULL) + goto out; + } + + if(num_file_used > 0) + { + SHOWMSG("allocating backup write file fd_set"); + + backup_file_write_fds = allocate_fd_set(num_file_used,file_write_fds); + if(backup_file_write_fds == NULL) + goto out; + } + } + + if(except_fds != NULL) + { + if(num_socket_used > 0) + { + SHOWMSG("allocating backup except socket fd_set"); + + backup_socket_except_fds = allocate_fd_set(num_socket_used,socket_except_fds); + if(backup_socket_except_fds == NULL) + goto out; + } + } + + /* We are going to poll all streams; for the timeout + * feature to work, we absolutely must know when to + * stop polling. + * + * Why aren't we using asynchronous DOS packets? + * The answer is that once a packet is sent, you + * cannot easily abort it. Polling is safer in + * that respect. Yes, I know that ACTION_STACK + * can be used to fake input to a console stream, + * but I'd rather not rely upon it. + */ + if(timeout != NULL) + { + struct DateStamp datestamp_timeout; + + PROFILE_OFF(); + DateStamp(&stop_when); + PROFILE_ON(); + + add_dates(&stop_when,timeval_to_datestamp(&datestamp_timeout,timeout)); + } + else + { + /* No timeout, poll until we are interrupted + * or get input from any of the files. It's + * not really necessary to initialize this + * timeval, but it keeps the compiler happy. + */ + memset(&stop_when,0,sizeof(stop_when)); + } + + while(TRUE) + { + /* Check for break signal. */ + if(__check_abort_enabled) + __check_abort(); + + /* Delay for a tick to avoid busy-waiting. */ + PROFILE_OFF(); + Delay(1); + PROFILE_ON(); + + /* This tells WaitSelect() to poll the sockets for input. */ + zero.tv_secs = 0; + zero.tv_micro = 0; + + /* Signals to stop on; we want to stop when a break signal arrives. */ + break_mask = signal_mask; + + if(__check_abort_enabled) + break_mask |= SIGBREAKF_CTRL_C; + + /* Check for socket input. */ + PROFILE_OFF(); + result = __WaitSelect(total_socket_fd,socket_read_fds,socket_write_fds,socket_except_fds,&zero,&break_mask); + PROFILE_ON(); + + /* Stop if a break signal arrives. */ + if((result < 0 && __get_errno() == EINTR) || FLAG_IS_SET(break_mask,SIGBREAKF_CTRL_C)) + { + SetSignal(SIGBREAKF_CTRL_C,SIGBREAKF_CTRL_C); + __check_abort(); + } + + /* Stop if the return value from WaitSelect() is negative (timeout, abort or serious error). */ + if(result < 0) + { + /* Update the signal mask if we received an interesting + signal. */ + if(signal_mask_ptr != NULL && (break_mask & signal_mask) != 0) + (*signal_mask_ptr) = signal_mask & break_mask; + + break; + } + + /* Check all files for input. We also poll + * them for input, but each with a little + * delay of about 1 microsecond. We stop + * as soon as we find one file that has + * input in it. + */ + for(i = 0 ; i < total_file_fd ; i++) + { + got_input = got_output = FALSE; + + fd = get_file_descriptor(i); + if(fd != NULL) + { + if(file_read_fds != NULL && FD_ISSET(i,file_read_fds)) + { + if(FLAG_IS_SET(fd->fd_Flags,FDF_READ)) + { + assert( FLAG_IS_CLEAR(fd->fd_Flags,FDF_IS_SOCKET) && FLAG_IS_CLEAR(fd->fd_Flags,FDF_STDIO) ); + + /* Does this one have input? */ + PROFILE_OFF(); + + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE)) + { + /* For an interactive stream, we simply ask. */ + if(WaitForChar(fd->fd_DefaultFile,1)) + got_input = TRUE; + } + else + { + D_S(struct FileInfoBlock,fib); + + /* For a file we check how much data is now in the file and + compare it against the current file position. If there's + unread data in the file, we will be able to read from it. + For pipes, any data reported to be in the "file" indicates + that there is something worth reading available. */ + if(__safe_examine_file_handle(fd->fd_DefaultFile,fib)) + { + if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) + { + /* Is there new data to read? */ + if((ULONG)fib->fib_Size > fd->fd_Position) + got_input = TRUE; + } + else + { + /* Does the pipe contain any data to read? */ + if(fib->fib_Size != 0) + got_input = TRUE; + } + } + } + + PROFILE_ON(); + } + } + + if(file_write_fds != NULL && FD_ISSET(i,file_write_fds)) + { + /* If this is a writable stream, assume that we can + * write to it. ZZZ AmigaDOS needs a method to check + * if the write will block. + */ + if(FLAG_IS_SET(fd->fd_Flags,FDF_WRITE)) + { + assert( FLAG_IS_CLEAR(fd->fd_Flags,FDF_IS_SOCKET) ); + + got_output = TRUE; + } + } + } + + if(got_input || got_output) + { + /* Mark one more descriptor as + * having input/output. + */ + result++; + } + + if(file_read_fds != NULL && NOT got_input) + FD_CLR(i,file_read_fds); + + if(file_write_fds != NULL && NOT got_output) + FD_CLR(i,file_write_fds); + } + + /* Did we receive a stop signal? */ + if(signal_mask_ptr != NULL && (break_mask & signal_mask) != 0) + { + /* Leave the interesting signal mask bits set. */ + (*signal_mask_ptr) = signal_mask & break_mask; + break; + } + + /* Did we get any input? If so, stop polling. */ + if(result > 0) + break; + + /* If a timeout was set, check if we are already + * beyond the point of time when we should have + * stopped polling. + */ + if(timeout != NULL) + { + struct DateStamp now; + + PROFILE_OFF(); + DateStamp(&now); + PROFILE_ON(); + + if(CompareDates(&now,&stop_when) <= 0) + break; + } + + /* No I/O ready yet. Restore the sets and retry... */ + copy_fd_set(socket_read_fds, backup_socket_read_fds, num_socket_used); + copy_fd_set(socket_write_fds, backup_socket_write_fds, num_socket_used); + copy_fd_set(socket_except_fds, backup_socket_except_fds, num_socket_used); + + copy_fd_set(file_read_fds, backup_file_read_fds, num_file_used); + copy_fd_set(file_write_fds, backup_file_write_fds, num_file_used); + } + } + else + { + ULONG break_mask; + + break_mask = signal_mask; + + if(__check_abort_enabled) + break_mask |= SIGBREAKF_CTRL_C; + + PROFILE_OFF(); + result = __WaitSelect(total_socket_fd,socket_read_fds,socket_write_fds,socket_except_fds,timeout,&break_mask); + PROFILE_ON(); + + if((result < 0 && __get_errno() == EINTR) || FLAG_IS_SET(break_mask,SIGBREAKF_CTRL_C)) + { + SetSignal(SIGBREAKF_CTRL_C,SIGBREAKF_CTRL_C); + __check_abort(); + } + + if(signal_mask_ptr != NULL && (break_mask & signal_mask) != 0) + (*signal_mask_ptr) = signal_mask & break_mask; + } + } + else + { + /* Wait for file input? */ + if((total_file_fd > 0) && (timeout == NULL || timeout->tv_secs > 0 || timeout->tv_micro > 0)) + { + struct DateStamp stop_when; + BOOL got_input; + BOOL got_output; + + SHOWMSG("we have to deal with files"); + + if(num_file_used > 0) + { + if(read_fds != NULL) + { + SHOWMSG("allocating backup file read fd_set"); + + backup_file_read_fds = allocate_fd_set(num_file_used,file_read_fds); + if(backup_file_read_fds == NULL) + goto out; + } + + if(write_fds != NULL) + { + SHOWMSG("allocating backup file write fd_set"); + + backup_file_write_fds = allocate_fd_set(num_file_used,file_write_fds); + if(backup_file_write_fds == NULL) + goto out; + } + } + + if(timeout != NULL) + { + struct DateStamp datestamp_timeout; + + PROFILE_OFF(); + DateStamp(&stop_when); + PROFILE_ON(); + + add_dates(&stop_when,timeval_to_datestamp(&datestamp_timeout,timeout)); + } + else + { + memset(&stop_when,0,sizeof(stop_when)); + } + + while(TRUE) + { + if(__check_abort_enabled) + __check_abort(); + + PROFILE_OFF(); + Delay(1); + PROFILE_ON(); + + result = 0; + + for(i = 0 ; i < total_file_fd ; i++) + { + got_input = got_output = FALSE; + + fd = get_file_descriptor(i); + if(fd != NULL) + { + if(file_read_fds != NULL && FD_ISSET(i,file_read_fds)) + { + if(FLAG_IS_SET(fd->fd_Flags,FDF_READ)) + { + assert( FLAG_IS_CLEAR(fd->fd_Flags,FDF_IS_SOCKET) && FLAG_IS_CLEAR(fd->fd_Flags,FDF_STDIO) ); + + PROFILE_OFF(); + + if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE)) + { + if(WaitForChar(fd->fd_DefaultFile,1)) + got_input = TRUE; + } + else + { + D_S(struct FileInfoBlock,fib); + + if(__safe_examine_file_handle(fd->fd_DefaultFile,fib)) + { + if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION)) + { + if((ULONG)fib->fib_Size > fd->fd_Position) + got_input = TRUE; + } + else + { + if(fib->fib_Size != 0) + got_input = TRUE; + } + } + } + + PROFILE_ON(); + } + } + + if(file_write_fds != NULL && FD_ISSET(i,file_write_fds)) + { + if(FLAG_IS_SET(fd->fd_Flags,FDF_WRITE)) + { + assert( FLAG_IS_CLEAR(fd->fd_Flags,FDF_IS_SOCKET) ); + + got_output = TRUE; + } + } + } + + if(got_input || got_output) + result++; + + if(file_read_fds != NULL && NOT got_input) + FD_CLR(i,file_read_fds); + + if(file_write_fds != NULL && NOT got_output) + FD_CLR(i,file_write_fds); + } + + /* Check for a stop signal. */ + if(signal_mask != 0 && (SetSignal(0,0) & signal_mask) != 0) + { + /* Remember which signal bits were set, and clear the + signal mask. Note that if the signal mask includes the + standard break signal bit, then we must not clear the + break signal. The ^C checking depends upon it to + remain set. */ + (*signal_mask_ptr) = signal_mask & SetSignal(0,signal_mask & ~SIGBREAKF_CTRL_C); + break; + } + + if(result > 0) + break; + + if(timeout != NULL) + { + struct DateStamp now; + + PROFILE_OFF(); + DateStamp(&now); + PROFILE_ON(); + + if(CompareDates(&now,&stop_when) <= 0) + break; + } + + copy_fd_set(file_read_fds, backup_file_read_fds, num_file_used); + copy_fd_set(file_write_fds, backup_file_write_fds, num_file_used); + } + } + else + { + SHOWMSG("no files to worry about"); + + if(signal_mask != 0) + (*signal_mask_ptr) = signal_mask & SetSignal(0,signal_mask & ~SIGBREAKF_CTRL_C); + } + } + + /* The descriptor sets remain unchanged in + * case of error. + */ + if(result >= 0) + { + SHOWMSG("remapping fd_sets"); + + __stdio_lock(); + + remap_descriptor_sets(socket_read_fds, total_socket_fd, file_read_fds, total_file_fd, read_fds, num_fds); + remap_descriptor_sets(socket_write_fds, total_socket_fd, file_write_fds, total_file_fd, write_fds, num_fds); + remap_descriptor_sets(socket_except_fds, total_socket_fd, NULL, 0, except_fds, num_fds); + + __stdio_unlock(); + } + + if(__check_abort_enabled) + __check_abort(); + + out: + + free_fd_set(socket_read_fds); + free_fd_set(socket_write_fds); + free_fd_set(socket_except_fds); + + free_fd_set(file_read_fds); + free_fd_set(file_write_fds); + + free_fd_set(backup_socket_read_fds); + free_fd_set(backup_socket_write_fds); + free_fd_set(backup_socket_except_fds); + + free_fd_set(backup_file_read_fds); + free_fd_set(backup_file_write_fds); + + RETURN(result); + return(result); +} + +/****************************************************************************/ + +#endif /* SOCKET_SUPPORT */ diff --git a/library/socket_wait_select.c b/library/socket_wait_select.c new file mode 100644 index 0000000..6dc0a49 --- /dev/null +++ b/library/socket_wait_select.c @@ -0,0 +1,56 @@ +/* + * $Id: socket_wait_select.c,v 1.1 2006-04-05 07:53:24 obarthel Exp $ + * + * :ts=4 + * + * Portable ISO 'C' (1994) runtime library for the Amiga computer + * Copyright (c) 2002-2006 by Olaf Barthel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Neither the name of Olaf Barthel nor the names of contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. + */ + +#if defined(SOCKET_SUPPORT) + +/****************************************************************************/ + +#ifndef _SOCKET_HEADERS_H +#include "socket_headers.h" +#endif /* _SOCKET_HEADERS_H */ + +/****************************************************************************/ + +int +wait_select(int num_fds,fd_set *read_fds,fd_set *write_fds,fd_set *except_fds,struct timeval *timeout,ULONG * signal_mask) +{ + int result; + + result = __select(num_fds,read_fds,write_fds,except_fds,timeout,signal_mask); + + return(result); +} + +/****************************************************************************/ + +#endif /* SOCKET_SUPPORT */