-
Notifications
You must be signed in to change notification settings - Fork 7.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stream_select() timeout useless for pipes on Windows #16889
Comments
Sad story: Lines 20 to 30 in f44eaac
and then: php-src/main/streams/plain_wrapper.c Lines 398 to 421 in f44eaac
So if there is nothing to read, we sleep for 32 seconds (in theory; likely way more in practice). Something enlightening for German speakers: https://learn.microsoft.com/de-de/windows/win32/api/winsock2/nf-winsock2-select |
I may be wrong on the specifics but I do know those tests got stuck for way longer than 32 seconds. I ended up having to abort them after about half an hour or so. Very suboptimal either way lol |
With the default scheduler frequency on Windows (assuming usleep doesn't spin-wait; I didn't look at the impl) this could sleep for as long as 5000 seconds, or well over an hour. |
Yeah, that looks like what happens if no data is coming in. The code has been introduced by 0c98279, with some good reasoning (although still an excessive 18 sec wait in theory), and later got changed to what we have since 6f3dd4d. However, even drastically reducing this wait won't help in this case, since the timeout is on |
So I modified win32/select.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/win32/select.c b/win32/select.c
index f443325cf0..f404ae3982 100644
--- a/win32/select.c
+++ b/win32/select.c
@@ -135,7 +135,12 @@ PHPAPI int php_select(php_socket_t max_fd, fd_set *rfds, fd_set *wfds, fd_set *e
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((uint32_t)handle_slot_to_fd[i], &aread);
+ DWORD avail_read = 0;
+ if (!PeekNamedPipe(handles[i], NULL, 0, NULL, &avail_read, NULL) || avail_read > 0) {
+ FD_SET((uint32_t)handle_slot_to_fd[i], &aread);
+ } else {
+ retcode--;
+ }
}
if (SAFE_FD_ISSET(handle_slot_to_fd[i], wfds)) {
FD_SET((uint32_t)handle_slot_to_fd[i], &awrite); The timeout works fine this way, but terminating the process won't work (so there are errors reported in the following), because we're going through the shell (cmd.exe) and killing this process, won't kill php.exe. I don't think there is any way to kill grandchild processes from PHP, and even doing this in C would be tedious (would need to enumerate all processes, and kill those with the given parent procid; not even sure what to do with grandchildren). The alternative would be to bypass the shell, but that will not work if there are redirects involved. |
Do you know why select wasn't implemented this way to begin with? Seems like a simple enough fix considering the impact of this issue & how often it's been reported. |
No, I have no idea. Maybe that has just been overlooked, or not deemed a holistic approach (after all, only read pipes would be catered to; and the proper solution for having non blocking pipes on Windows would be overlapped IO). Or maybe few use Anyhow, I'm going to re-purpose this ticket for the more general |
run-tests.php
timeout doesn't work on WindowsPipes are blocking on Windows, but `php_select()` always returns them as ready for read/write. This renders the `stream_select()` timeout useless, what can cause a following read to block for a very long time. While there is no general fix (and least not within reach for a stable version), we can at least cater to the important case of read pipes by peeking the pipe to check whether data is available. If there is none, we do not add the handle to the read set.
Description
php-src/run-tests.php
Line 1187 in f44eaac
stream_select()
and pipes don't play nice together on Windows. It returns immediately & causes it to get stuck insidefread()
.Could use
socket
instead ofpipe
forstdout
andstderr
, (a la #5777), but this might break tests that usestream_select()
onSTDOUT
orSTDERR
.This was the real issue causing me trouble with #16849, as
run-tests.php
did not kill the test after the allotted time & the build hung forever.(Side note: This code looks dodgy too. What if only
stderr
has output? it's gonna block instdout
until something happens:php-src/run-tests.php
Lines 1200 to 1213 in f44eaac
PHP Version
8.1/8.2/8.3/8.4
Operating System
Windows
The text was updated successfully, but these errors were encountered: