diff --git a/ext/standard/tests/poll/poll_wait_timeout_overflow.phpt b/ext/standard/tests/poll/poll_wait_timeout_overflow.phpt new file mode 100644 index 000000000000..5009d3edd8ce --- /dev/null +++ b/ext/standard/tests/poll/poll_wait_timeout_overflow.phpt @@ -0,0 +1,29 @@ +--TEST-- +Io\Poll: a timeout that overflows an int of milliseconds keeps waiting +--SKIPIF-- +isAvailable()) { + die("skip poll backend not available\n"); +} +if (PHP_OS_FAMILY === 'Windows') { + die("skip POSIX only\n"); +} +?> +--FILE-- + ['pipe', 'w']], $pipes); + +$poll_ctx = new Io\Poll\Context(Io\Poll\Backend::Poll); +$watcher = $poll_ctx->add(new StreamPollHandle($pipes[1]), [Io\Poll\Event::Read]); + +// 158913790 seconds is about 5 years, and wraps around to 48ms as an int of milliseconds +$events = $poll_ctx->wait(Time\Duration::fromSeconds(158913790)); + +var_dump(count($events), $events[0] === $watcher); + +fclose($pipes[1]); +proc_close($process); +?> +--EXPECT-- +int(1) +bool(true) diff --git a/main/poll/php_poll_internal.h b/main/poll/php_poll_internal.h index 951521314393..21b85575409d 100644 --- a/main/poll/php_poll_internal.h +++ b/main/poll/php_poll_internal.h @@ -164,6 +164,12 @@ static inline int php_poll_timespec_to_ms(const struct timespec *timeout) return -1; } + /* Cap rather than wrap around: a timeout that long is as good as indefinite, + * where truncating it to an int made poll() return after a few milliseconds */ + if (timeout->tv_sec >= (INT_MAX - 1000) / 1000) { + return INT_MAX; + } + int ms = (int) (timeout->tv_sec * 1000); /* Round nanoseconds up to the next millisecond to avoid premature return */ if (timeout->tv_nsec > 0) {