xref: /PHP-7.2/win32/select.c (revision 7a7ec01a)
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 
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(php_socket_t max_fd,fd_set * rfds,fd_set * wfds,fd_set * efds,struct timeval * tv)37 PHPAPI int php_select(php_socket_t 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 	/* As max_fd is unsigned, non socket might overflow. */
50 	if (max_fd > (php_socket_t)INT_MAX) {
51 		return -1;
52 	}
53 
54 #define SAFE_FD_ISSET(fd, set)	(set != NULL && FD_ISSET(fd, set))
55 
56 	/* calculate how long we need to wait in milliseconds */
57 	if (tv == NULL) {
58 		ms_total = INFINITE;
59 	} else {
60 		ms_total = tv->tv_sec * 1000;
61 		ms_total += tv->tv_usec / 1000;
62 	}
63 
64 	FD_ZERO(&sock_read);
65 	FD_ZERO(&sock_write);
66 	FD_ZERO(&sock_except);
67 
68 	/* build an array of handles for non-sockets */
69 	for (i = 0; i < max_fd; i++) {
70 		if (SAFE_FD_ISSET(i, rfds) || SAFE_FD_ISSET(i, wfds) || SAFE_FD_ISSET(i, efds)) {
71 			handles[n_handles] = (HANDLE)(zend_uintptr_t)_get_osfhandle(i);
72 			if (handles[n_handles] == INVALID_HANDLE_VALUE) {
73 				/* socket */
74 				if (SAFE_FD_ISSET(i, rfds)) {
75 					FD_SET((uint32_t)i, &sock_read);
76 				}
77 				if (SAFE_FD_ISSET(i, wfds)) {
78 					FD_SET((uint32_t)i, &sock_write);
79 				}
80 				if (SAFE_FD_ISSET(i, efds)) {
81 					FD_SET((uint32_t)i, &sock_except);
82 				}
83 				if (i > sock_max_fd) {
84 					sock_max_fd = i;
85 				}
86 			} else {
87 				handle_slot_to_fd[n_handles] = i;
88 				n_handles++;
89 			}
90 		}
91 	}
92 
93 	if (n_handles == 0) {
94 		/* plain sockets only - let winsock handle the whole thing */
95 		return select(-1, rfds, wfds, efds, tv);
96 	}
97 
98 	/* mixture of handles and sockets; lets multiplex between
99 	 * winsock and waiting on the handles */
100 
101 	FD_ZERO(&aread);
102 	FD_ZERO(&awrite);
103 	FD_ZERO(&aexcept);
104 
105 	limit = GetTickCount64() + ms_total;
106 	do {
107 		retcode = 0;
108 
109 		if (sock_max_fd >= 0) {
110 			/* overwrite the zero'd sets here; the select call
111 			 * will clear those that are not active */
112 			aread = sock_read;
113 			awrite = sock_write;
114 			aexcept = sock_except;
115 
116 			tvslice.tv_sec = 0;
117 			tvslice.tv_usec = 100000;
118 
119 			retcode = select(-1, &aread, &awrite, &aexcept, &tvslice);
120 		}
121 		if (n_handles > 0) {
122 			/* check handles */
123 			DWORD wret;
124 
125 			wret = WaitForMultipleObjects(n_handles, handles, FALSE, retcode > 0 ? 0 : 100);
126 
127 			if (wret == WAIT_TIMEOUT) {
128 				/* set retcode to 0; this is the default.
129 				 * select() may have set it to something else,
130 				 * in which case we leave it alone, so this branch
131 				 * does nothing */
132 				;
133 			} else if (wret == WAIT_FAILED) {
134 				if (retcode == 0) {
135 					retcode = -1;
136 				}
137 			} else {
138 				if (retcode < 0) {
139 					retcode = 0;
140 				}
141 				for (i = 0; i < n_handles; i++) {
142 					if (WAIT_OBJECT_0 == WaitForSingleObject(handles[i], 0)) {
143 						if (SAFE_FD_ISSET(handle_slot_to_fd[i], rfds)) {
144 							FD_SET((uint32_t)handle_slot_to_fd[i], &aread);
145 						}
146 						if (SAFE_FD_ISSET(handle_slot_to_fd[i], wfds)) {
147 							FD_SET((uint32_t)handle_slot_to_fd[i], &awrite);
148 						}
149 						if (SAFE_FD_ISSET(handle_slot_to_fd[i], efds)) {
150 							FD_SET((uint32_t)handle_slot_to_fd[i], &aexcept);
151 						}
152 						retcode++;
153 					}
154 				}
155 			}
156 		}
157 	} while (retcode == 0 && (ms_total == INFINITE || GetTickCount64() < limit));
158 
159 	if (rfds) {
160 		*rfds = aread;
161 	}
162 	if (wfds) {
163 		*wfds = awrite;
164 	}
165 	if (efds) {
166 		*efds = aexcept;
167 	}
168 
169 	return retcode;
170 }
171 
172 #endif
173 
174 /*
175  * Local variables:
176  * tab-width: 4
177  * c-basic-offset: 4
178  * End:
179  * vim600: noet sw=4 ts=4 fdm=marker
180  * vim<600: noet sw=4 ts=4
181  */
182