1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2016 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
19 #include "php.h"
20 #include "php_network.h"
21
22 #ifdef PHP_WIN32
23
24 /* $Id$ */
25
26 /* Win32 select() will only work with sockets, so we roll our own implementation here.
27 * - If you supply only sockets, this simply passes through to winsock select().
28 * - If you supply file handles, there is no way to distinguish between
29 * ready for read/write or OOB, so any set in which the handle is found will
30 * be marked as ready.
31 * - If you supply a mixture of handles and sockets, the system will interleave
32 * calls between select() and WaitForMultipleObjects(). The time slicing may
33 * cause this function call to take up to 100 ms longer than you specified.
34 * - Calling this with NULL sets as a portable way to sleep with sub-second
35 * accuracy is not supported.
36 * */
php_select(int max_fd,fd_set * rfds,fd_set * wfds,fd_set * efds,struct timeval * tv)37 PHPAPI int php_select(int max_fd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tv)
38 {
39 ULONGLONG ms_total, limit;
40 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
41 int handle_slot_to_fd[MAXIMUM_WAIT_OBJECTS];
42 int n_handles = 0, i;
43 fd_set sock_read, sock_write, sock_except;
44 fd_set aread, awrite, aexcept;
45 int sock_max_fd = -1;
46 struct timeval tvslice;
47 int retcode;
48
49 #define SAFE_FD_ISSET(fd, set) (set != NULL && FD_ISSET(fd, set))
50
51 /* calculate how long we need to wait in milliseconds */
52 if (tv == NULL) {
53 ms_total = INFINITE;
54 } else {
55 ms_total = tv->tv_sec * 1000;
56 ms_total += tv->tv_usec / 1000;
57 }
58
59 FD_ZERO(&sock_read);
60 FD_ZERO(&sock_write);
61 FD_ZERO(&sock_except);
62
63 /* build an array of handles for non-sockets */
64 for (i = 0; i < max_fd; i++) {
65 if (SAFE_FD_ISSET(i, rfds) || SAFE_FD_ISSET(i, wfds) || SAFE_FD_ISSET(i, efds)) {
66 handles[n_handles] = (HANDLE)(zend_uintptr_t)_get_osfhandle(i);
67 if (handles[n_handles] == INVALID_HANDLE_VALUE) {
68 /* socket */
69 if (SAFE_FD_ISSET(i, rfds)) {
70 FD_SET((uint)i, &sock_read);
71 }
72 if (SAFE_FD_ISSET(i, wfds)) {
73 FD_SET((uint)i, &sock_write);
74 }
75 if (SAFE_FD_ISSET(i, efds)) {
76 FD_SET((uint)i, &sock_except);
77 }
78 if (i > sock_max_fd) {
79 sock_max_fd = i;
80 }
81 } else {
82 handle_slot_to_fd[n_handles] = i;
83 n_handles++;
84 }
85 }
86 }
87
88 if (n_handles == 0) {
89 /* plain sockets only - let winsock handle the whole thing */
90 return select(max_fd, rfds, wfds, efds, tv);
91 }
92
93 /* mixture of handles and sockets; lets multiplex between
94 * winsock and waiting on the handles */
95
96 FD_ZERO(&aread);
97 FD_ZERO(&awrite);
98 FD_ZERO(&aexcept);
99
100 limit = GetTickCount64() + ms_total;
101 do {
102 retcode = 0;
103
104 if (sock_max_fd >= 0) {
105 /* overwrite the zero'd sets here; the select call
106 * will clear those that are not active */
107 aread = sock_read;
108 awrite = sock_write;
109 aexcept = sock_except;
110
111 tvslice.tv_sec = 0;
112 tvslice.tv_usec = 100000;
113
114 retcode = select(sock_max_fd+1, &aread, &awrite, &aexcept, &tvslice);
115 }
116 if (n_handles > 0) {
117 /* check handles */
118 DWORD wret;
119
120 wret = MsgWaitForMultipleObjects(n_handles, handles, FALSE, retcode > 0 ? 0 : 100, QS_ALLEVENTS);
121
122 if (wret == WAIT_TIMEOUT) {
123 /* set retcode to 0; this is the default.
124 * select() may have set it to something else,
125 * in which case we leave it alone, so this branch
126 * does nothing */
127 ;
128 } else if (wret == WAIT_FAILED) {
129 if (retcode == 0) {
130 retcode = -1;
131 }
132 } else {
133 if (retcode < 0) {
134 retcode = 0;
135 }
136 for (i = 0; i < n_handles; i++) {
137 if (WAIT_OBJECT_0 == WaitForSingleObject(handles[i], 0)) {
138 if (SAFE_FD_ISSET(handle_slot_to_fd[i], rfds)) {
139 FD_SET((uint)handle_slot_to_fd[i], &aread);
140 }
141 if (SAFE_FD_ISSET(handle_slot_to_fd[i], wfds)) {
142 FD_SET((uint)handle_slot_to_fd[i], &awrite);
143 }
144 if (SAFE_FD_ISSET(handle_slot_to_fd[i], efds)) {
145 FD_SET((uint)handle_slot_to_fd[i], &aexcept);
146 }
147 retcode++;
148 }
149 }
150 }
151 }
152 } while (retcode == 0 && (ms_total == INFINITE || GetTickCount64() < limit));
153
154 if (rfds) {
155 *rfds = aread;
156 }
157 if (wfds) {
158 *wfds = awrite;
159 }
160 if (efds) {
161 *efds = aexcept;
162 }
163
164 return retcode;
165 }
166
167 #endif
168
169 /*
170 * Local variables:
171 * tab-width: 4
172 * c-basic-offset: 4
173 * End:
174 * vim600: noet sw=4 ts=4 fdm=marker
175 * vim<600: noet sw=4 ts=4
176 */
177