Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Windows: Process::start hangs for exactly 60s with ext-uv (Handle Inheritance Deadlock in ProcessWrapper)

Aperta
#80 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
25/100
Tipo di issue
Bug
Chiarezza
Da chiarire
Stato di attività
Ferma
Stack tecnologico
php

Direzione di ricerca

Inizia eseguendo la riproduzione fornita di Process::start con UvDriver e StreamDriver, quindi esamina il codice C di PHP SocketConnector e windows-process-wrapper descritto nel report. Il lavoro è concluso quando il percorso Windows UvDriver non rimane più bloccato per 60 secondi durante l'avvio di un processo; la direzione proposta consiste nel valutare named-pipe IPC.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

I have identified a critical blocking issue when using amphp/process on Windows with the uv extension enabled (using Revolt\EventLoop\Driver\UvDriver).

Any attempt to spawn a process results in a 60-second hang, rendering the library unusable. This also breaks amphp/dns out-of-the-box on Windows, as its default WindowsDnsConfigLoader spawns a PowerShell process to retrieve system DNS settings.

Environment:
  • OS: Windows 11
  • PHP: 8.5.2
  • Extensions: uv (enabled)
  • Driver: Revolt\EventLoop\Driver\UvDriver
  • Package: amphp/process (latest 2.0.3) & amphp/windows-process-wrapper (v1.2.0)
Reproduction Script:

The hang occurs only when UvDriver is active. Using StreamDriver works instantly.

<?php
require __DIR__ . '/vendor/autoload.php';

use Amp\Process\Process;
use Revolt\EventLoop;

echo "Driver: " . get_class(EventLoop::getDriver()) . "\n";

$start = microtime(true);
echo "Starting process...\n";

// This line hangs for exactly 60 seconds
$process = Process::start('cmd /c echo Hello');

$process->join();
echo "Done in " . (microtime(true) - $start) . "s\n";
Deep Analysis & Debugging:

I performed extensive debugging by modifying both the PHP SocketConnector and recompiling the C ProcessWrapper.exe with logging.

Trace Findings:

  1. TCP Connection: ProcessWrapper.exe successfully connects to the PHP server socket (127.0.0.1:0).
  2. Handshake: The security token exchange completes instantly.
  3. The Deadlock: The data transmission hangs exactly when the Wrapper calls CreateProcessW to spawn the actual command (e.g., cmd.exe).
  4. Wrapper Internal State: My custom logs inside the Wrapper C-code show that it successfully sends the PID signal and calls send(). It finishes its work instantly (< 1s).
  5. PHP State: UvDriver does not receive the onReadable event for the PID data until exactly 60 seconds later (Windows TCP timeout).
Root Cause: IOCP Handle Inheritance Deadlock

The issue is likely caused by Socket Handle Inheritance combined with libuv's IOCP implementation.

  1. The Leak: PHP's proc_open on Windows hardcodes bInheritHandles = TRUE. This causes the ProcessWrapper.exe to inherit the parent's (PHP) TCP server socket handle.
  2. The Deadlock: ext-uv ties all sockets to an IOCP port. When the ProcessWrapper.exe spawns the actual child process, that "grandchild" also inherits the server socket handle.
  3. The Conflict: The server socket handle is now held by a grandchild process while being bound to the parent's IOCP. The Windows kernel detects a conflict or deadlock in the I/O completion mechanism for this socket. The send() call in the Wrapper blocks (or the OS buffers the data but doesn't notify the parent) until the system TCP timeout (60s) forces a reset/flush.
Verification of Potential Fixes

I tried to fix this by modifying windows-process-wrapper (C-code) to use Selective Handle Inheritance:

  • I utilized STARTUPINFOEX with UpdateProcThreadAttribute and PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
  • I explicitly whitelisted only the 3 pipes (STDIN/STDOUT/STDERR) for inheritance.
  • Result: The 60-second hang persisted.

Even though the Wrapper logic ensured the socket handle wasn't passed to the grandchild, the deadlock remained. This suggests that the conflict is deeper within libuv/PHP interaction or that proc_open's initial handle leakage cannot be fully mitigated by the child process wrapper alone.

Conclusion & Solution

Since patching the Wrapper to filter handles did not resolve the issue, and fixing proc_open in PHP core is out of scope, the architecture of amphp/process on Windows appears incompatible with ext-uv when using TCP sockets for IPC.

Proposed Long-Term Fix:
When UvDriver is detected on Windows, the library should ideally switch to using Named Pipes (\\.\pipe\...) instead of TCP sockets. Named Pipes are natively supported by libuv (and IOCP) and handle inheritance/duplication much more gracefully than TCP sockets in this context.

Lingua principale
PHP
Stelle
260
Fork
29
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di amphp/process

Tutte le issue di amphp/process

Issue simili

Altre issue su PHP

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.