diff options
-rw-r--r-- | milkway/mw-poll-win32.c | 191 | ||||
-rw-r--r-- | milkway/mw-poll.h | 4 |
2 files changed, 188 insertions, 7 deletions
diff --git a/milkway/mw-poll-win32.c b/milkway/mw-poll-win32.c index 81d2ef2..ad88ba7 100644 --- a/milkway/mw-poll-win32.c +++ b/milkway/mw-poll-win32.c @@ -18,15 +18,18 @@ * Boston, MA 02111-1307, USA. */ #include "milkwayint.h" -#include "mw-poll-sys-private.h" +#include "mw-poll-win32-private.h" #include <time.h> #include <stdlib.h> +#include <stdio.h> #ifdef MW_OS_WINDOWS #include <windows.h> +#define WIN32_POLL_DEBUG (0) + static mw_poll_win32_t* mw_poll_win32_init(mw_poll_win32_t *self) { @@ -42,24 +45,198 @@ mw_poll_win32_new(void) mw_poll_win32_t *self = mw_object_alloc(MW_POLL_WIN32_TYPE); if (!self) return NULL; - return (mw_poll_t*)mw_poll_win32_init(self, MW_POLL_WIN32_TYPE); + return (mw_poll_t*)mw_poll_win32_init(self); +} + +static int +poll_rest (mw_bool_t poll_msgs, + HANDLE *handles, + int nhandles, + mw_pollfd_t *fds, + mw_uint_t nfds, + int timeout) +{ + DWORD ready; + mw_pollfd_t *f; + int recursed_result; + + if (poll_msgs) { + /* Wait for either messages or handles + * -> Use MsgWaitForMultipleObjectsEx + */ + if (WIN32_POLL_DEBUG) + printf (" MsgWaitForMultipleObjectsEx(%d, %d)\n", nhandles, timeout); + + ready = MsgWaitForMultipleObjectsEx (nhandles, handles, timeout, + QS_ALLINPUT, MWMO_ALERTABLE); + + if (ready == WAIT_FAILED) { + fprintf (stderr, "MsgWaitForMultipleObjectsEx failed.\n"); + } + } else if (nhandles == 0) { + /* No handles to wait for, just the timeout */ + if (timeout == INFINITE) { + ready = WAIT_FAILED; + } else { + SleepEx (timeout, TRUE); + ready = WAIT_TIMEOUT; + } + } else { + /* Wait for just handles + * -> Use WaitForMultipleObjectsEx + */ + if (WIN32_POLL_DEBUG) + printf (" WaitForMultipleObjectsEx(%d, %d)\n", nhandles, timeout); + + ready = WaitForMultipleObjectsEx (nhandles, handles, FALSE, timeout, TRUE); + if (ready == WAIT_FAILED) { + fprintf (stderr, "WaitForMultipleObjectsEx failed.\n"); + } + } + + if (WIN32_POLL_DEBUG) + printf (" wait returns %ld%s\n", + ready, + (ready == WAIT_FAILED ? " (WAIT_FAILED)" : + (ready == WAIT_TIMEOUT ? " (WAIT_TIMEOUT)" : + (poll_msgs && ready == WAIT_OBJECT_0 + nhandles ? " (msg)" : "")))); + + if (ready == WAIT_FAILED) { + return -1; + } else if (ready == WAIT_TIMEOUT || + ready == WAIT_IO_COMPLETION) { + return 0; + } else if (poll_msgs && ready == WAIT_OBJECT_0 + nhandles) { + for (f = fds; f < &fds[nfds]; ++f) + if (f->fd == MW_WIN32_MSG_HANDLE && f->events & MW_IO_IN) + f->revents |= MW_IO_IN; + + /* If we have a timeout, or no handles to poll, be satisfied + * with just noticing we have messages waiting. + */ + if (timeout != 0 || nhandles == 0) + return 1; + + /* If no timeout and handles to poll, recurse to poll them, + * too. + */ + recursed_result = poll_rest (FALSE, handles, nhandles, fds, nfds, 0); + return (recursed_result == -1) ? -1 : 1 + recursed_result; + } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) { + for (f = fds; f < &fds[nfds]; ++f) { + if ((HANDLE) f->fd == handles[ready - WAIT_OBJECT_0]) { + f->revents = f->events; + if (WIN32_POLL_DEBUG) + printf (" got event %p\n", (HANDLE) f->fd); + } + } + + /* If no timeout and polling several handles, recurse to poll + * the rest of them. + */ + if (timeout == 0 && nhandles > 1) { + /* Remove the handle that fired */ + int i; + if (ready < nhandles - 1) + for (i = ready - WAIT_OBJECT_0 + 1; i < nhandles; i++) + handles[i-1] = handles[i]; + nhandles--; + recursed_result = poll_rest (FALSE, handles, nhandles, fds, nfds, 0); + return (recursed_result == -1) ? -1 : 1 + recursed_result; + } + return 1; + } + + return 0; } static int mw_poll_win32_poll(mw_poll_t *super, - mw_pollfd_t *fds; - mw_uint_t nfds; + mw_pollfd_t *fds, + mw_uint_t nfds, int timeout) { - mw_poll_win32_t *self = (mw_poll_win32_t*)super; + /* mw_poll_win32_t *self = (mw_poll_win32_t*)super; */ + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + mw_bool_t poll_msgs = MW_FALSE; + mw_pollfd_t *f; + int nhandles = 0; + int retval; + + if (WIN32_POLL_DEBUG) + printf ("MWPollWin32: waiting for"); + + for (f = fds; f < &fds[nfds]; ++f) { + if (f->fd == MW_WIN32_MSG_HANDLE && (f->events & MW_IO_IN)) { + if (WIN32_POLL_DEBUG && !poll_msgs) + printf (" MSG"); + poll_msgs = MW_TRUE; + } else if (f->fd > 0) { + /* Don't add the same handle several times into the array, as + * docs say that is not allowed, even if it actually does seem + * to work. + */ + int i; + + for (i = 0; i < nhandles; i++) + if (handles[i] == (HANDLE) f->fd) + break; + + if (i == nhandles) { + if (nhandles == MAXIMUM_WAIT_OBJECTS) { + fprintf (stderr, "Too many handles to wait for!\n"); + break; + } else { + if (WIN32_POLL_DEBUG) + printf (" %p", (HANDLE) f->fd); + handles[nhandles++] = (HANDLE) f->fd; + } + } + } + } + if (WIN32_POLL_DEBUG) + printf ("\n"); + + for (f = fds; f < &fds[nfds]; ++f) + f->revents = 0; + + if (timeout == -1) + timeout = INFINITE; + + /* Polling for several things? */ + if (nhandles > 1 || (nhandles > 0 && poll_msgs)) { + /* First check if one or several of them are immediately + * available + */ + retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, 0); + + /* If not, and we have a significant timeout, poll again with + * timeout then. Note that this will return indication for only + * one event, or only for messages. We ignore timeouts less than + * ten milliseconds as they are mostly pointless on Windows, the + * MsgWaitForMultipleObjectsEx() call will timeout right away + * anyway. + */ + if (retval == 0 && (timeout == INFINITE || timeout >= 10)) + retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, timeout); + } else { + /* Just polling for one thing, so no need to check first if + * available immediately + */ + retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, timeout); + } + + if (retval == -1) + for (f = fds; f < &fds[nfds]; ++f) + f->revents = 0; - return 0; + return retval; } static void mw_poll_win32_finalize(mw_object_t *super) { - MW_SUPER_FINALIZE(super, MW_POLL_WIN32_TYPE); + MW_SUPER_FINALIZE(super, MW_POLL_WIN32_TYPE)(super); } static void diff --git a/milkway/mw-poll.h b/milkway/mw-poll.h index 2365ed0..d38e345 100644 --- a/milkway/mw-poll.h +++ b/milkway/mw-poll.h @@ -56,6 +56,10 @@ typedef enum MW_IO_NVAL = 1 << 5 } mw_poll_event_t; +#ifdef MW_OS_WINDOWS +#define MW_WIN32_MSG_HANDLE 20091031 +#endif + struct mw_pollfd { mw_intptr_t fd; |