select.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 7 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2018 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Author: Wez Furlong <wez@thebrainroom.com> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include "php.h"
  19. #include "php_network.h"
  20. /* Win32 select() will only work with sockets, so we roll our own implementation here.
  21. * - If you supply only sockets, this simply passes through to winsock select().
  22. * - If you supply file handles, there is no way to distinguish between
  23. * ready for read/write or OOB, so any set in which the handle is found will
  24. * be marked as ready.
  25. * - If you supply a mixture of handles and sockets, the system will interleave
  26. * calls between select() and WaitForMultipleObjects(). The time slicing may
  27. * cause this function call to take up to 100 ms longer than you specified.
  28. * - Calling this with NULL sets as a portable way to sleep with sub-second
  29. * accuracy is not supported.
  30. * */
  31. PHPAPI int php_select(php_socket_t max_fd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tv)
  32. {
  33. ULONGLONG ms_total, limit;
  34. HANDLE handles[MAXIMUM_WAIT_OBJECTS];
  35. int handle_slot_to_fd[MAXIMUM_WAIT_OBJECTS];
  36. int n_handles = 0, i;
  37. fd_set sock_read, sock_write, sock_except;
  38. fd_set aread, awrite, aexcept;
  39. int sock_max_fd = -1;
  40. struct timeval tvslice;
  41. int retcode;
  42. /* As max_fd is unsigned, non socket might overflow. */
  43. if (max_fd > (php_socket_t)INT_MAX) {
  44. return -1;
  45. }
  46. #define SAFE_FD_ISSET(fd, set) (set != NULL && FD_ISSET(fd, set))
  47. /* calculate how long we need to wait in milliseconds */
  48. if (tv == NULL) {
  49. ms_total = INFINITE;
  50. } else {
  51. ms_total = tv->tv_sec * 1000;
  52. ms_total += tv->tv_usec / 1000;
  53. }
  54. FD_ZERO(&sock_read);
  55. FD_ZERO(&sock_write);
  56. FD_ZERO(&sock_except);
  57. /* build an array of handles for non-sockets */
  58. for (i = 0; (uint32_t)i < max_fd; i++) {
  59. if (SAFE_FD_ISSET(i, rfds) || SAFE_FD_ISSET(i, wfds) || SAFE_FD_ISSET(i, efds)) {
  60. handles[n_handles] = (HANDLE)(zend_uintptr_t)_get_osfhandle(i);
  61. if (handles[n_handles] == INVALID_HANDLE_VALUE) {
  62. /* socket */
  63. if (SAFE_FD_ISSET(i, rfds)) {
  64. FD_SET((uint32_t)i, &sock_read);
  65. }
  66. if (SAFE_FD_ISSET(i, wfds)) {
  67. FD_SET((uint32_t)i, &sock_write);
  68. }
  69. if (SAFE_FD_ISSET(i, efds)) {
  70. FD_SET((uint32_t)i, &sock_except);
  71. }
  72. if (i > sock_max_fd) {
  73. sock_max_fd = i;
  74. }
  75. } else {
  76. handle_slot_to_fd[n_handles] = i;
  77. n_handles++;
  78. }
  79. }
  80. }
  81. if (n_handles == 0) {
  82. /* plain sockets only - let winsock handle the whole thing */
  83. return select(-1, rfds, wfds, efds, tv);
  84. }
  85. /* mixture of handles and sockets; lets multiplex between
  86. * winsock and waiting on the handles */
  87. FD_ZERO(&aread);
  88. FD_ZERO(&awrite);
  89. FD_ZERO(&aexcept);
  90. limit = GetTickCount64() + ms_total;
  91. do {
  92. retcode = 0;
  93. if (sock_max_fd >= 0) {
  94. /* overwrite the zero'd sets here; the select call
  95. * will clear those that are not active */
  96. aread = sock_read;
  97. awrite = sock_write;
  98. aexcept = sock_except;
  99. tvslice.tv_sec = 0;
  100. tvslice.tv_usec = 100000;
  101. retcode = select(-1, &aread, &awrite, &aexcept, &tvslice);
  102. }
  103. if (n_handles > 0) {
  104. /* check handles */
  105. DWORD wret;
  106. wret = WaitForMultipleObjects(n_handles, handles, FALSE, retcode > 0 ? 0 : 100);
  107. if (wret == WAIT_TIMEOUT) {
  108. /* set retcode to 0; this is the default.
  109. * select() may have set it to something else,
  110. * in which case we leave it alone, so this branch
  111. * does nothing */
  112. ;
  113. } else if (wret == WAIT_FAILED) {
  114. if (retcode == 0) {
  115. retcode = -1;
  116. }
  117. } else {
  118. if (retcode < 0) {
  119. retcode = 0;
  120. }
  121. for (i = 0; i < n_handles; i++) {
  122. if (WAIT_OBJECT_0 == WaitForSingleObject(handles[i], 0)) {
  123. if (SAFE_FD_ISSET(handle_slot_to_fd[i], rfds)) {
  124. FD_SET((uint32_t)handle_slot_to_fd[i], &aread);
  125. }
  126. if (SAFE_FD_ISSET(handle_slot_to_fd[i], wfds)) {
  127. FD_SET((uint32_t)handle_slot_to_fd[i], &awrite);
  128. }
  129. if (SAFE_FD_ISSET(handle_slot_to_fd[i], efds)) {
  130. FD_SET((uint32_t)handle_slot_to_fd[i], &aexcept);
  131. }
  132. retcode++;
  133. }
  134. }
  135. }
  136. }
  137. } while (retcode == 0 && (ms_total == INFINITE || GetTickCount64() < limit));
  138. if (rfds) {
  139. *rfds = aread;
  140. }
  141. if (wfds) {
  142. *wfds = awrite;
  143. }
  144. if (efds) {
  145. *efds = aexcept;
  146. }
  147. return retcode;
  148. }
  149. /*
  150. * Local variables:
  151. * tab-width: 4
  152. * c-basic-offset: 4
  153. * End:
  154. * vim600: noet sw=4 ts=4 fdm=marker
  155. * vim<600: noet sw=4 ts=4
  156. */