/* Win32 select() will only work with sockets, so we roll our own implementation here. * - If you supply only sockets, this simply passes through to winsock select(). * - If you supply file handles, there is no way to distinguish between * ready for read/write or OOB, so any set in which the handle is found will * be marked as ready. * - If you supply a mixture of handles and sockets, the system will interleave * calls between select() and WaitForMultipleObjects(). The time slicing may * cause this function call to take up to 100 ms longer than you specified. * - Calling this with NULL sets as a portable way to sleep with sub-second * accuracy is not supported. * */ PHPAPI int php_select(int max_fd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tv) { DWORD ms_total, limit; HANDLE handles[MAXIMUM_WAIT_OBJECTS]; int handle_slot_to_fd[MAXIMUM_WAIT_OBJECTS]; int n_handles = 0, i; fd_set sock_read, sock_write, sock_except; fd_set aread, awrite, aexcept; int sock_max_fd = -1; struct timeval tvslice; int retcode; #define SAFE_FD_ISSET(fd, set) (set != NULL && FD_ISSET(fd, set)) /* calculate how long we need to wait in milliseconds */ if (tv == NULL) { ms_total = INFINITE; } else { ms_total = tv->tv_sec * 1000; ms_total += tv->tv_usec / 1000; } FD_ZERO(&sock_read); FD_ZERO(&sock_write); FD_ZERO(&sock_except); /* build an array of handles for non-sockets */ for (i = 0; i < max_fd; i++) { if (SAFE_FD_ISSET(i, rfds) || SAFE_FD_ISSET(i, wfds) || SAFE_FD_ISSET(i, efds)) { handles[n_handles] = (HANDLE)(zend_uintptr_t)_get_osfhandle(i); if (handles[n_handles] == INVALID_HANDLE_VALUE) { /* socket */ if (SAFE_FD_ISSET(i, rfds)) { FD_SET((uint)i, &sock_read); } if (SAFE_FD_ISSET(i, wfds)) { FD_SET((uint)i, &sock_write); } if (SAFE_FD_ISSET(i, efds)) { FD_SET((uint)i, &sock_except); } if (i > sock_max_fd) { sock_max_fd = i; } } else { handle_slot_to_fd[n_handles] = i; n_handles++; } } } if (n_handles == 0) { /* plain sockets only - let winsock handle the whole thing */ return select(max_fd, rfds, wfds, efds, tv); } /* mixture of handles and sockets; lets multiplex between * winsock and waiting on the handles */ FD_ZERO(&aread); FD_ZERO(&awrite); FD_ZERO(&aexcept); limit = GetTickCount() + ms_total; do { retcode = 0; if (sock_max_fd >= 0) { /* overwrite the zero'd sets here; the select call * will clear those that are not active */ aread = sock_read; awrite = sock_write; aexcept = sock_except; tvslice.tv_sec = 0; tvslice.tv_usec = 100000; retcode = select(sock_max_fd+1, &aread, &awrite, &aexcept, &tvslice); } if (n_handles > 0) { /* check handles */ DWORD wret; wret = MsgWaitForMultipleObjects(n_handles, handles, FALSE, retcode > 0 ? 0 : 100, QS_ALLEVENTS); if (wret == WAIT_TIMEOUT) { /* set retcode to 0; this is the default. * select() may have set it to something else, * in which case we leave it alone, so this branch * does nothing */ ; } else if (wret == WAIT_FAILED) { if (retcode == 0) { retcode = -1; } } else { if (retcode < 0) { retcode = 0; } for (i = 0; i < n_handles; i++) { if (WAIT_OBJECT_0 == WaitForSingleObject(handles[i], 0)) { if (SAFE_FD_ISSET(handle_slot_to_fd[i], rfds)) { FD_SET((uint)handle_slot_to_fd[i], &aread); } if (SAFE_FD_ISSET(handle_slot_to_fd[i], wfds)) { FD_SET((uint)handle_slot_to_fd[i], &awrite); } if (SAFE_FD_ISSET(handle_slot_to_fd[i], efds)) { FD_SET((uint)handle_slot_to_fd[i], &aexcept); } retcode++; } } } } } while (retcode == 0 && (ms_total == INFINITE || GetTickCount() < limit)); if (rfds) { *rfds = aread; } if (wfds) { *wfds = awrite; } if (efds) { *efds = aexcept; } return retcode; }
/** * Win32 select() will only work with sockets, so we roll our own * implementation here. * - If you supply only sockets, this simply passes through to winsock select(). * - If you supply file handles, there is no way to distinguish between * ready for read/write or OOB, so any set in which the handle is found will * be marked as ready. * - If you supply a mixture of handles and sockets, the system will interleave * calls between select() and WaitForMultipleObjects(). The time slicing may * cause this function call to take up to 100 ms longer than you specified. * - Pipes are not checked for writability or errors (errno = ENOSYS) */ int _win_select(int max_fd, fd_set * rfds, fd_set * wfds, fd_set * efds, const struct timeval *tv) { DWORD ms_total, limit; HANDLE handles[MAXIMUM_WAIT_OBJECTS], hPipes[MAXIMUM_WAIT_OBJECTS]; int handle_slot_to_fd[MAXIMUM_WAIT_OBJECTS]; int n_handles, i, iPipes; fd_set sock_read, sock_write, sock_except; fd_set aread, awrite, aexcept; int sock_max_fd; struct timeval tvslice; int retcode; #define SAFE_FD_ISSET(fd, set) (set != NULL && FD_ISSET(fd, set)) n_handles = 0; sock_max_fd = -1; iPipes = 0; /* calculate how long we need to wait in milliseconds */ if(tv == NULL) ms_total = INFINITE; else { ms_total = tv->tv_sec * 1000; ms_total += tv->tv_usec / 1000; } /* select() may be used as a portable way to sleep */ if (!(rfds || wfds || efds)) { Sleep(ms_total); return 0; } FD_ZERO(&sock_read); FD_ZERO(&sock_write); FD_ZERO(&sock_except); /* build an array of handles for non-sockets */ for(i = 0; i < max_fd; i++) { if(SAFE_FD_ISSET(i, rfds) || SAFE_FD_ISSET(i, wfds) || SAFE_FD_ISSET(i, efds)) { unsigned long ulVal; if (__win_GetHandleType((DWORD) i) == SOCKET_HANDLE) { /* socket */ if(SAFE_FD_ISSET(i, rfds)) FD_SET(i, &sock_read); if(SAFE_FD_ISSET(i, wfds)) FD_SET(i, &sock_write); if(SAFE_FD_ISSET(i, efds)) FD_SET(i, &sock_except); if(i > sock_max_fd) sock_max_fd = i; } else { if (__win_GetHandleType((DWORD) i) == PIPE_HANDLE) hPipes[iPipes++] = (HANDLE) i; /* Pipe */ else { handles[n_handles] = (HANDLE) _get_osfhandle(i); if ((DWORD) handles[n_handles] == 0xffffffff) handles[n_handles] = (HANDLE) i; handle_slot_to_fd[n_handles] = i; n_handles++; } } } } /* multiplex between winsock select() and waiting on the handles */ FD_ZERO(&aread); FD_ZERO(&awrite); FD_ZERO(&aexcept); limit = GetTickCount() + ms_total; do { retcode = 0; if(sock_max_fd >= 0) { /* overwrite the zero'd sets here; the select call * will clear those that are not active */ aread = sock_read; awrite = sock_write; aexcept = sock_except; tvslice.tv_sec = 0; tvslice.tv_usec = 100000; if ((retcode = select(sock_max_fd + 1, &aread, &awrite, &aexcept, &tvslice)) == SOCKET_ERROR) { SetErrnoFromWinsockError(WSAGetLastError()); if (errno == ENOTSOCK) errno = EBADF; return retcode; } } if(n_handles > 0) { /* check handles */ DWORD wret; wret = MsgWaitForMultipleObjects(n_handles, handles, FALSE, retcode > 0 ? 0 : 100, QS_ALLEVENTS); if(wret == WAIT_TIMEOUT) { /* set retcode to 0; this is the default. * select() may have set it to something else, * in which case we leave it alone, so this branch * does nothing */ ; } else if(wret == WAIT_FAILED) { SetErrnoFromWinError(GetLastError()); return -1; } else { for(i = 0; i < n_handles; i++) { if(WAIT_OBJECT_0 == WaitForSingleObject(handles[i], 0)) { if(SAFE_FD_ISSET(handle_slot_to_fd[i], rfds)) { FD_SET(handle_slot_to_fd[i], &aread); } if(SAFE_FD_ISSET(handle_slot_to_fd[i], wfds)) FD_SET(handle_slot_to_fd[i], &awrite); if(SAFE_FD_ISSET(handle_slot_to_fd[i], efds)) FD_SET(handle_slot_to_fd[i], &aexcept); retcode++; } } } } /* Poll Pipes */ for(i = 0; i < iPipes; i++) { DWORD dwBytes; if(SAFE_FD_ISSET(hPipes[i], rfds)) { if (! PeekNamedPipe(hPipes[i], NULL, 0, NULL, &dwBytes, NULL)) { retcode = -1; SetErrnoFromWinError(GetLastError()); } else if (dwBytes) { FD_SET((int) hPipes[i], &aread); retcode++; } } else if (SAFE_FD_ISSET(hPipes[i], wfds) || SAFE_FD_ISSET(hPipes[i], efds)) { errno = ENOSYS; return -1; /* Not implemented */ } } /* Check for closed sockets */ for(i = 0; i < sock_max_fd; i++) { if(SAFE_FD_ISSET(i, &sock_read)) { struct sockaddr addr; int len; if (getpeername(i, &addr, &len) == SOCKET_ERROR) { int err, len; len = sizeof(err); if (getsockopt(i, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == 0 && err == WSAENOTCONN) { if (! SAFE_FD_ISSET(i, &aread)) { FD_SET(i, &aread); retcode++; } } } } } } while(retcode == 0 && (ms_total == INFINITE || GetTickCount() < limit)); if(rfds) *rfds = aread; if(wfds) *wfds = awrite; if(efds) *efds = aexcept; return retcode; }