On Tue, 2012-12-18 at 14:51 +0100, Svante Signell wrote: > On Mon, 2012-12-17 at 23:02 +0100, Svante Signell wrote: > > On Mon, 2012-12-17 at 20:58 +0100, Richard Braun wrote: > > > On Mon, Dec 17, 2012 at 03:12:10AM +0100, Svante Signell wrote: ... > As a follow-up the attached patch combines Richards zero timeout for > select()-based calls to hurdselect to also work with poll(). One example > is ntpdate. The timeout also seems to be OK, maybe further testing > needed?. This is a workaround until the split into three cases are > ready, and until the poll code is updated.
Now the 3-way split of hurdselect is updated, see the attached patch. Hopefully the trivial faults are solved by now and we can proceed with the poll-specific changes towards POSIX compliance. Attached is also some test code to check the functionality of the patched version,.That can be checked with the poll/select patches as proposed previously (and with the buggy current version). Of course the #if 1 case has to be resolved, My findings is that they are needed (with the new code, not the old) until a safe timeout is communicated for the file descriptors to be ready in the poll case. Thanks and Merry Christmas, Svante (note my change of ISP mail supplier) Comments and feedbacks are of course encouraged :-)
--- a/hurd/hurdselect.c 2012-12-16 12:56:25.000000000 +0100 +++ b/hurd/hurdselect.c 2012-12-19 19:15:18.000000000 +0100 @@ -50,6 +50,13 @@ _hurd_select (int nfds, fd_set rfds, wfds, xfds; int firstfd, lastfd; mach_msg_timeout_t to = 0; + /* XXX: A timeout of 0 returns immediately, even if no file + descriptors are ready. This is correct according to POSIX.1-2001. + As many programs rely on file descriptors being ready for a + timeout of zero use 1 msec as the minimum delay for poll */ + /* FIXME: replace with something better */ + mach_msg_timeout_t to_eff = 1; + struct { struct hurd_userlink ulink; @@ -68,6 +75,64 @@ _hurd_select (int nfds, assert (sizeof (union typeword) == sizeof (mach_msg_type_t)); assert (sizeof (uint32_t) == sizeof (mach_msg_type_t)); + enum { + DELAY = -1, + SELECT = 0, + POLL = 1 + } ispoll; + + if (nfds == 0) + ispoll = DELAY; + else if (pollfds) + ispoll = POLL; + else + ispoll = SELECT; + + union + { + mach_msg_header_t head; +#ifdef MACH_MSG_TRAILER_MINIMUM_SIZE + struct + { + mach_msg_header_t head; + NDR_record_t ndr; + error_t err; + } error; + struct + { + mach_msg_header_t head; + NDR_record_t ndr; + error_t err; + int result; + mach_msg_trailer_t trailer; + } success; +#else + struct + { + mach_msg_header_t head; + union typeword err_type; + error_t err; + } error; + struct + { + mach_msg_header_t head; + union typeword err_type; + error_t err; + union typeword result_type; + int result; + } success; +#endif + } msg; + mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT); + error_t msgerr; +#define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */ +#ifdef MACH_MSG_TYPE_BIT + const union typeword inttype = + { type: + { MACH_MSG_TYPE_INTEGER_T, sizeof (integer_t) * 8, 1, 1, 0, 0 } + }; +#endif + if (nfds < 0 || nfds > FD_SETSIZE) { errno = EINVAL; @@ -84,20 +149,108 @@ _hurd_select (int nfds, to = (timeout->tv_sec * 1000 + (timeout->tv_nsec + 999999) / 1000000); - if (strcmp(program_invocation_short_name, "vi") && strcmp(program_invocation_short_name, "vim") && strcmp(program_invocation_short_name, "vimdiff") && !to) - to = 1; } if (sigmask && __sigprocmask (SIG_SETMASK, sigmask, &oset)) return -1; - if (pollfds) + if (nfds > _hurd_dtablesize) + nfds = _hurd_dtablesize; + + switch (ispoll) { + case DELAY: + /* We are just a pure timeout. */ + portset = __mach_reply_port (); + + err = 0; + + /* Now wait for reply messages. */ + if (!err) + { + /* Now wait for io_select_reply messages on PORT, + timing out as appropriate. */ + while ((msgerr = __mach_msg (&msg.head, + MACH_RCV_MSG | options, + 0, sizeof msg, portset, to, + MACH_PORT_NULL)) == MACH_MSG_SUCCESS) + { + /* We got a message. Decode it. */ + if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID && + msg.head.msgh_size >= sizeof msg.error && + !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && +#ifdef MACH_MSG_TYPE_BIT + msg.error.err_type.word == inttype.word +#endif + ) + { + /* This is a properly formatted message so far. + See if it is a success or a failure. */ + if (msg.error.err == EINTR && + msg.head.msgh_size == sizeof msg.error) + { + /* EINTR response; poll for further responses + and then return quickly. */ + err = EINTR; + goto exit; + } + if (msg.error.err || + msg.head.msgh_size != sizeof msg.success || +#ifdef MACH_MSG_TYPE_BIT + msg.success.result_type.word != inttype.word || +#endif + (msg.success.result & SELECT_ALL) == 0) + { + /* Error or bogus reply. Simulate readiness. */ + __mach_msg_destroy (&msg.head); + msg.success.result = SELECT_ALL; + } + } /* end ) { */ + + if (msg.head.msgh_remote_port != MACH_PORT_NULL) + __mach_port_deallocate (__mach_task_self (), + msg.head.msgh_remote_port); + } /* while */ + + exit: + if (err == MACH_RCV_TIMED_OUT) + /* This is the normal value for ERR. We might have timed + out and read no messages. Otherwise, after receiving + the first message, we poll for more messages. We + receive with a timeout of 0 to effect a poll, so ERR is + MACH_RCV_TIMED_OUT when the poll finds no message + waiting. */ + err = 0; + + } /* if(!err) */ + + /* Destroy PORTSET, but only if it's not actually the reply + port for a single descriptor (in which case it's + destroyed in the previous loop; not doing it here is just + a bit more efficient). */ + __mach_port_destroy (__mach_task_self (), portset); + + if (err) + { + errno = err; + return -1; + } + else + return 0; + break; + + case POLL: /* Collect interesting descriptors from the user's `pollfd' array. We do a first pass that reads the user's array before taking any locks. The second pass then only touches our own stack, and gets the port references. */ + to_eff = to; +#if 1 + if (to == 0) + to_eff = 1; +#endif + for (i = 0; i < nfds; ++i) if (pollfds[i].fd >= 0) { @@ -132,6 +285,7 @@ _hurd_select (int nfds, } /* If one descriptor is bogus, we fail completely. */ + /* This is a bug in the current implementation, see POSIX 2001 */ while (i-- > 0) if (d[i].type != 0) _hurd_port_free (&d[i].cell->port, @@ -142,6 +296,7 @@ _hurd_select (int nfds, __mutex_unlock (&_hurd_dtable_lock); HURD_CRITICAL_END; + /* This is a bug in the current implementation, see POSIX 2001 */ if (i < nfds) { if (sigmask) @@ -150,11 +305,197 @@ _hurd_select (int nfds, return -1; } + firstfd = 0; lastfd = i - 1; - firstfd = i == 0 ? lastfd : 0; - } - else - { + err = 0; + got = 0; + + /* Send them all io_select request messages. */ + portset = MACH_PORT_NULL; + + for (i = firstfd; i <= lastfd; ++i) + if (d[i].type) + { + int type = d[i].type; + d[i].reply_port = __mach_reply_port (); + err = __io_select (d[i].io_port, d[i].reply_port, + firstfd == lastfd ? to_eff : 0, + &type); + switch (err) + { + case MACH_RCV_TIMED_OUT: + /* No immediate response. This is normal. */ + err = 0; + if (firstfd == lastfd) + /* When there's a single descriptor, we don't need a + portset, so just pretend we have one, but really + use the single reply port. */ + portset = d[i].reply_port; + else if (got == 0) + /* We've got multiple reply ports, so we need a port set to + multiplex them. */ + { + /* We will wait again for a reply later. */ + if (portset == MACH_PORT_NULL) + /* Create the portset to receive all the replies on. */ + err = __mach_port_allocate (__mach_task_self (), + MACH_PORT_RIGHT_PORT_SET, + &portset); + if (! err) + /* Put this reply port in the port set. */ + __mach_port_move_member (__mach_task_self (), + d[i].reply_port, portset); + } + break; + + default: + /* No other error should happen. Callers of select + don't expect to see errors, so we simulate + readiness of the erring object and the next call + hopefully will get the error again. */ + type = SELECT_ALL; + /* FALLTHROUGH */ + + case 0: + /* We got an answer. */ + if ((type & SELECT_ALL) == 0) + /* Bogus answer; treat like an error, as a fake positive. */ + type = SELECT_ALL; + + /* This port is already ready already. */ + d[i].type &= type; + d[i].type |= SELECT_RETURNED; + ++got; + break; + } + _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port); + } + + /* Now wait for reply messages. */ + if (!err && got == 0) + { + /* Now wait for io_select_reply messages on PORT, + timing out as appropriate. */ + while ((msgerr = __mach_msg (&msg.head, + MACH_RCV_MSG | options, + 0, sizeof msg, portset, + firstfd == lastfd ? 0 : to_eff, + MACH_PORT_NULL)) == MACH_MSG_SUCCESS) + { + /* We got a message. Decode it. */ + if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID && + msg.head.msgh_size >= sizeof msg.error && + !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && +#ifdef MACH_MSG_TYPE_BIT + msg.error.err_type.word == inttype.word +#endif + ) + { + /* This is a properly formatted message so far. + See if it is a success or a failure. */ + if (msg.error.err == EINTR && + msg.head.msgh_size == sizeof msg.error) + { + /* EINTR response; poll for further responses + and then return quickly. */ + err = EINTR; + goto poll; + } + if (msg.error.err || + msg.head.msgh_size != sizeof msg.success || +#ifdef MACH_MSG_TYPE_BIT + msg.success.result_type.word != inttype.word || +#endif + (msg.success.result & SELECT_ALL) == 0) + { + /* Error or bogus reply. Simulate readiness. */ + __mach_msg_destroy (&msg.head); + msg.success.result = SELECT_ALL; + } + + /* Look up the respondent's reply port and record its + readiness. */ + int had = got; + for (i = firstfd; i <= lastfd; ++i) + if (d[i].type + && d[i].reply_port == msg.head.msgh_local_port) + { + d[i].type &= msg.success.result; + d[i].type |= SELECT_RETURNED; + ++got; + } + assert (got > had); + } + + if (msg.head.msgh_remote_port != MACH_PORT_NULL) + __mach_port_deallocate (__mach_task_self (), + msg.head.msgh_remote_port); + + if (got) + poll: + { + /* Poll for another message. */ + to_eff = 0; + options |= MACH_RCV_TIMEOUT; + } + } + + if (err == MACH_RCV_TIMED_OUT) + /* This is the normal value for ERR. We might have timed out and + read no messages. Otherwise, after receiving the first message, + we poll for more messages. We receive with a timeout of 0 to + effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no + message waiting. */ + err = 0; + + if (got) + /* At least one descriptor is known to be ready now, so we will + return success. */ + err = 0; + } + + for (i = firstfd; i <= lastfd; ++i) + if (d[i].type) + __mach_port_destroy (__mach_task_self (), d[i].reply_port); + if ((firstfd != lastfd) && (portset != MACH_PORT_NULL)) + /* Destroy PORTSET, but only if it's not actually the reply + port for a single descriptor (in which case it's destroyed + in the previous loop; not doing it here is just a bit more + efficient). */ + __mach_port_destroy (__mach_task_self (), portset); + + if (err) + { + if (sigmask) + __sigprocmask (SIG_SETMASK, &oset, NULL); + return __hurd_fail (err); + } + + /* Fill in the `revents' members of the user's array. */ + for (i = 0; i < nfds; ++i) + { + int type = d[i].type; + int_fast16_t revents = 0; + + if (type & SELECT_RETURNED) + { + if (type & SELECT_READ) + revents |= POLLIN; + if (type & SELECT_WRITE) + revents |= POLLOUT; + if (type & SELECT_URG) + revents |= POLLPRI; + } + pollfds[i].revents = revents; + } + + if (sigmask && __sigprocmask (SIG_SETMASK, &oset, NULL)) + return -1; + + return got; + break; + + case SELECT: /* Collect interested descriptors from the user's fd_set arguments. Use local copies so we can't crash from user bogosity. */ @@ -174,11 +515,8 @@ _hurd_select (int nfds, HURD_CRITICAL_BEGIN; __mutex_lock (&_hurd_dtable_lock); - if (nfds > _hurd_dtablesize) - nfds = _hurd_dtablesize; - /* Collect the ports for interesting FDs. */ - firstfd = lastfd = -1; + lastfd = -1; for (i = 0; i < nfds; ++i) { int type = 0; @@ -203,8 +541,6 @@ _hurd_select (int nfds, break; } lastfd = i; - if (firstfd == -1) - firstfd = i; } } @@ -218,20 +554,11 @@ _hurd_select (int nfds, errno = EBADF; return -1; } - } - - - err = 0; - got = 0; - - /* Send them all io_select request messages. */ + firstfd = 0; + err = 0; + got = 0; - if (firstfd == -1) - /* But not if there were no ports to deal with at all. - We are just a pure timeout. */ - portset = __mach_reply_port (); - else - { + /* Send them all io_select request messages. */ portset = MACH_PORT_NULL; for (i = firstfd; i <= lastfd; ++i) @@ -239,10 +566,7 @@ _hurd_select (int nfds, { int type = d[i].type; d[i].reply_port = __mach_reply_port (); - err = __io_select (d[i].io_port, d[i].reply_port, - /* Poll only if there's a single descriptor. */ - (firstfd == lastfd) ? to : 0, - &type); + err = __io_select (d[i].io_port, d[i].reply_port, 0, &type); switch (err) { case MACH_RCV_TIMED_OUT: @@ -292,99 +616,51 @@ _hurd_select (int nfds, } _hurd_port_free (&d[i].cell->port, &d[i].ulink, d[i].io_port); } - } - - /* Now wait for reply messages. */ - if (!err && got == 0) - { - /* Now wait for io_select_reply messages on PORT, - timing out as appropriate. */ - union + /* Now wait for reply messages. */ + if (!err && got == 0) { - mach_msg_header_t head; -#ifdef MACH_MSG_TRAILER_MINIMUM_SIZE - struct - { - mach_msg_header_t head; - NDR_record_t ndr; - error_t err; - } error; - struct - { - mach_msg_header_t head; - NDR_record_t ndr; - error_t err; - int result; - mach_msg_trailer_t trailer; - } success; -#else - struct + /* Now wait for io_select_reply messages on PORT, + timing out as appropriate. */ + while ((msgerr = __mach_msg (&msg.head, + MACH_RCV_MSG | options, + 0, sizeof msg, portset, to, + MACH_PORT_NULL)) == MACH_MSG_SUCCESS) { - mach_msg_header_t head; - union typeword err_type; - error_t err; - } error; - struct - { - mach_msg_header_t head; - union typeword err_type; - error_t err; - union typeword result_type; - int result; - } success; -#endif - } msg; - mach_msg_option_t options = (timeout == NULL ? 0 : MACH_RCV_TIMEOUT); - error_t msgerr; - while ((msgerr = __mach_msg (&msg.head, - MACH_RCV_MSG | options, - 0, sizeof msg, portset, to, - MACH_PORT_NULL)) == MACH_MSG_SUCCESS) - { - /* We got a message. Decode it. */ -#define IO_SELECT_REPLY_MSGID (21012 + 100) /* XXX */ -#ifdef MACH_MSG_TYPE_BIT - const union typeword inttype = - { type: - { MACH_MSG_TYPE_INTEGER_T, sizeof (integer_t) * 8, 1, 1, 0, 0 } - }; -#endif - if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID && - msg.head.msgh_size >= sizeof msg.error && - !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && + /* We got a message. Decode it. */ + if (msg.head.msgh_id == IO_SELECT_REPLY_MSGID && + msg.head.msgh_size >= sizeof msg.error && + !(msg.head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && #ifdef MACH_MSG_TYPE_BIT - msg.error.err_type.word == inttype.word + msg.error.err_type.word == inttype.word #endif - ) - { - /* This is a properly formatted message so far. - See if it is a success or a failure. */ - if (msg.error.err == EINTR && - msg.head.msgh_size == sizeof msg.error) + ) { - /* EINTR response; poll for further responses - and then return quickly. */ - err = EINTR; - goto poll; - } - if (msg.error.err || - msg.head.msgh_size != sizeof msg.success || + /* This is a properly formatted message so far. + See if it is a success or a failure. */ + if (msg.error.err == EINTR && + msg.head.msgh_size == sizeof msg.error) + { + /* EINTR response; poll for further responses + and then return quickly. */ + err = EINTR; + goto poll2; + } + if (msg.error.err || + msg.head.msgh_size != sizeof msg.success || #ifdef MACH_MSG_TYPE_BIT - msg.success.result_type.word != inttype.word || + msg.success.result_type.word != inttype.word || #endif - (msg.success.result & SELECT_ALL) == 0) - { - /* Error or bogus reply. Simulate readiness. */ - __mach_msg_destroy (&msg.head); - msg.success.result = SELECT_ALL; - } - - /* Look up the respondent's reply port and record its - readiness. */ - { - int had = got; - if (firstfd != -1) + (msg.success.result & SELECT_ALL) == 0) + { + /* Error or bogus reply. Simulate readiness. */ + __mach_msg_destroy (&msg.head); + msg.success.result = SELECT_ALL; + } + + /* Look up the respondent's reply port and record its + readiness. */ + int had = got; for (i = firstfd; i <= lastfd; ++i) if (d[i].type && d[i].reply_port == msg.head.msgh_local_port) @@ -393,106 +669,88 @@ _hurd_select (int nfds, d[i].type |= SELECT_RETURNED; ++got; } - assert (got > had); - } - } + assert (got > had); + } - if (msg.head.msgh_remote_port != MACH_PORT_NULL) - __mach_port_deallocate (__mach_task_self (), - msg.head.msgh_remote_port); + if (msg.head.msgh_remote_port != MACH_PORT_NULL) + __mach_port_deallocate (__mach_task_self (), + msg.head.msgh_remote_port); - if (got) - poll: - { - /* Poll for another message. */ - to = 0; - options |= MACH_RCV_TIMEOUT; + if (got) + poll2: + { + /* Poll for another message. */ + to = 0; + options |= MACH_RCV_TIMEOUT; + } } - } - - if (err == MACH_RCV_TIMED_OUT) - /* This is the normal value for ERR. We might have timed out and - read no messages. Otherwise, after receiving the first message, - we poll for more messages. We receive with a timeout of 0 to - effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no - message waiting. */ - err = 0; - - if (got) - /* At least one descriptor is known to be ready now, so we will - return success. */ - err = 0; - } - - if (firstfd != -1) - for (i = firstfd; i <= lastfd; ++i) - if (d[i].type) - __mach_port_destroy (__mach_task_self (), d[i].reply_port); - if (firstfd == -1 || (firstfd != lastfd && portset != MACH_PORT_NULL)) - /* Destroy PORTSET, but only if it's not actually the reply port for a - single descriptor (in which case it's destroyed in the previous loop; - not doing it here is just a bit more efficient). */ - __mach_port_destroy (__mach_task_self (), portset); - if (err) - { - if (sigmask) - __sigprocmask (SIG_SETMASK, &oset, NULL); - return __hurd_fail (err); - } + if (err == MACH_RCV_TIMED_OUT) + /* This is the normal value for ERR. We might have timed out and + read no messages. Otherwise, after receiving the first message, + we poll for more messages. We receive with a timeout of 0 to + effect a poll, so ERR is MACH_RCV_TIMED_OUT when the poll finds no + message waiting. */ + err = 0; - if (pollfds) - /* Fill in the `revents' members of the user's array. */ - for (i = 0; i < nfds; ++i) - { - int type = d[i].type; - int_fast16_t revents = 0; + if (got) + /* At least one descriptor is known to be ready now, so we will + return success. */ + err = 0; + } - if (type & SELECT_RETURNED) - { - if (type & SELECT_READ) - revents |= POLLIN; - if (type & SELECT_WRITE) - revents |= POLLOUT; - if (type & SELECT_URG) - revents |= POLLPRI; - } + for (i = firstfd; i <= lastfd; ++i) + if (d[i].type) + __mach_port_destroy (__mach_task_self (), d[i].reply_port); + if ((firstfd != lastfd) && (portset != MACH_PORT_NULL)) + /* Destroy PORTSET, but only if it's not actually the reply port for a + single descriptor (in which case it's destroyed in the previous loop; + not doing it here is just a bit more efficient). */ + __mach_port_destroy (__mach_task_self (), portset); + + if (err) + { + if (sigmask) + __sigprocmask (SIG_SETMASK, &oset, NULL); + return __hurd_fail (err); + } - pollfds[i].revents = revents; - } - else - { /* Below we recalculate GOT to include an increment for each operation allowed on each fd. */ got = 0; /* Set the user bitarrays. We only ever have to clear bits, as all desired ones are initially set. */ - if (firstfd != -1) - for (i = firstfd; i <= lastfd; ++i) - { - int type = d[i].type; + for (i = firstfd; i <= lastfd; ++i) + { + int type = d[i].type; + + if ((type & SELECT_RETURNED) == 0) + type = 0; + + if (type & SELECT_READ) + got++; + else if (readfds) + FD_CLR (i, readfds); + if (type & SELECT_WRITE) + got++; + else if (writefds) + FD_CLR (i, writefds); + if (type & SELECT_URG) + got++; + else if (exceptfds) + FD_CLR (i, exceptfds); + } - if ((type & SELECT_RETURNED) == 0) - type = 0; + if (sigmask && __sigprocmask (SIG_SETMASK, &oset, NULL)) + return -1; - if (type & SELECT_READ) - got++; - else if (readfds) - FD_CLR (i, readfds); - if (type & SELECT_WRITE) - got++; - else if (writefds) - FD_CLR (i, writefds); - if (type & SELECT_URG) - got++; - else if (exceptfds) - FD_CLR (i, exceptfds); - } - } + return got; + break; - if (sigmask && __sigprocmask (SIG_SETMASK, &oset, NULL)) - return -1; + default: + errno = EINVAL; + return -1; - return got; + } /* switch ispoll */ }
#undef HAVE_POLL_H #define HAVE_POLL_H 1 #include <stdlib.h> #include <stdio.h> #include <signal.h> #include <string.h> #include <errno.h> // Included by <ctype.h> or <bits/types.h> #include <bits/types.h> #include <time.h> /* timespec definition */ #include <sys/time.h> /* timeval definition */ // Included by <time.h> if _TIME_H is defined #include <bits/time.h> /* timeval definition */ #include <bits/types.h> #ifdef HAVE_POLL_H # include <poll.h> #else /* According to POSIX.1-2001 */ # include <sys/select.h> #endif /* Struct pollfd defined here */ //struct pollfd { // int fd; /* file descriptor */ // short events; /* requested events */ // short revents; /* returned events */ //}; /* Struct fdset defined here */ /* typedef struct __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS]; # define __FDS_BITS(set) ((set)->__fds_bits) } fd_set; gcc -E: typedef long int __fd_mask; __fd_mask fds_bits[256 / (8 * (int) sizeof (__fd_mask)) */ int main(void) { int i, j, nfds = 0, nfound; int stime, etime; time_t *currtime = 0; char ans[2] = " "; char c[2] = " "; enum { DELAY = -1, POLL = 0, SELECT = 1 } ispoll; /* poll stuff */ struct pollfd fds[3]; int timeout = 5; /* select stuff */ int fd[3]; fd_set readfds, writefds, exceptfds; struct timeval timevalue = {5,0}; printf("Program test_poll+select\n"); printf("Test poll or select [p,s]: p? "); int key_stroke = 0; for (i = 0; ((char)key_stroke != '\n') && (i < 2); i++) { key_stroke = getchar(); ans[i] = (char)key_stroke; } ans[1] = '\0'; if( ans[0] != '\n') printf("ans = %s\n", ans); printf ("Number of file descriptors [0(delay), 1, 2, 3]: 0? "); c[0] = (char)getchar(); if (c[0] != '\n') { c[1] = '\0'; nfds = strtol(c, NULL, 10); } else c[0] = '\0'; printf( "nfds=%d\n", nfds); if (nfds == 0) ispoll = DELAY; else if (strcmp(ans,"s") != 0) ispoll = POLL; else ispoll = SELECT; switch (ispoll) { case DELAY: printf("\nDELAY test\n"); printf("\nInput data\n"); printf("timeout=%d seconds.\n", timeout); printf("\nOutput data\n"); if (strcmp(ans,"s") != 0) { stime = (int)time(currtime); nfound = poll(fds, 0, timeout*1000); etime = (int)time(currtime); printf("POLL timeout delay= %d(%d) seconds.\n", timeout, etime-stime); } else { stime = (int)time(currtime); nfound = select(0, NULL, NULL, NULL, &timevalue); etime = (int)time(currtime); printf("SELECT timeout delay= %d(%d) seconds.\n", (int)timevalue.tv_sec, etime-stime); } break; return 0; case POLL: printf("\nPOLL test\n"); for(i = 0; i <= 2; ++i) { fds[i].fd = i; fds[i].revents = 0; } fds[0].events = POLLIN; fds[1].events = POLLOUT; fds[2].events = POLLIN | POLLOUT; printf("#define POLLIN 001 /* There is data to read. */\n"); printf("#define POLLPRI 002 /* There is urgent data to read. */\n"); printf("#define POLLOUT 004 /* Writing now will not block. */\n"); printf("#define POLLERR 010 /* Error condition. */\n"); printf("#define POLLHUP 020 /* Hung up. */\n"); printf("#define POLLNVAL 040 /* Invalid polling request. */\n"); printf("\nInput data\n"); printf("timeout=%d seconds.\n", timeout); for (j=0; j < nfds; ++j) printf("fds[%d].{fd,events,revents}={%d,%d,%d}\n", j, fds[j].fd, fds[j].events, fds[j].revents); stime = (int)time(currtime); nfound = poll(fds, (unsigned int)nfds, timeout*1000); etime = (int)time(currtime); printf("\nOutput data\n"); printf("nfound=%d\n", nfound); for (j=0; j < nfds; ++j) { printf( "fds[%d].{fd,events,revents}={%d,%d,%d}\n", j, fds[j].fd, fds[j].events, fds[j].revents); if (nfound == -1) { perror("Poll error"); printf("error=%s, revents[%d]=%d\n", strerror(errno), j, fds[j].revents); printf("Timed out %d(%d) seconds.\n", timeout, etime-stime); } else if (nfound == 0) printf("Timed out %d(%d) seconds.\n", timeout, etime-stime); else printf("Data is available now, delay=%d\n", etime-stime); } break; return 0; case SELECT: printf("\nSELECT test\n"); for(i = 0; i <= 2; ++i) fd[i] = i; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); FD_SET(fd[0], &readfds); FD_SET(fd[1], &writefds); FD_SET(fd[2], &exceptfds); if (nfds == 1) { /* Watch stdin (fd 0) to see when it has input. */ stime = (int)time(currtime); nfound = select(nfds + 1, &readfds, (fd_set*)0, (fd_set*)0, &timevalue); etime = (int)time(currtime); } else if (nfds == 2) { stime = (int)time(currtime); // nfound = select(nfds + 1, &readfds, &writefds, (fd_set*)0, &timevalue); nfound = select(nfds + 1, &readfds, (fd_set*)0, &exceptfds, &timevalue); etime = (int)time(currtime); } else { stime = (int)time(currtime); nfound = select(nfds + 1, &readfds, &writefds, &exceptfds, &timevalue); etime = (int)time(currtime); } printf("nfound=%d\n", nfound); if (nfound == -1) perror("select()"); else if (nfound) printf("Data is available now, delay=%d\n", etime-stime); else printf("No data within %d(%d) seconds.\n", (int)timevalue.tv_sec, etime-stime); break; return 0; } return 0; }