Commit 0278788facf for php

commit 0278788facf75c8cb232127887cc8692273b958f
Merge: 78a21e9fd5b 954f79636e4
Author: Jakub Zelenka <bukka@php.net>
Date:   Sun Nov 1 21:29:04 2026 +0100

    Merge branch 'PHP-8.6'

    * PHP-8.6:
      Report Io\Poll level watchers with buffered stream data (#24017)

diff --cc ext/standard/io_poll.c
index c2585a51d3c,583dfdbc033..ae6d5a4c984
--- a/ext/standard/io_poll.c
+++ b/ext/standard/io_poll.c
@@@ -239,8 -243,19 +243,19 @@@ static php_poll_handle_ops php_stream_p
  	.cleanup = php_stream_poll_handle_cleanup
  };

+ /* The stream buffer only, without any IO */
+ static bool php_stream_poll_handle_has_buffered_data(php_poll_handle_object *handle)
+ {
+ 	php_stream *stream = php_stream_poll_handle_get_stream(handle);
+ 	return stream && stream->writepos > stream->readpos;
+ }
+
+ static const php_poll_handle_ext_ops php_stream_poll_handle_ext_ops = {
+ 	.has_buffered_data = php_stream_poll_handle_has_buffered_data
+ };
+
  /* Handle interface internal only */
 -static int php_stream_poll_handle_implement_interface(zend_class_entry *interface, zend_class_entry *implementor)
 +static void php_stream_poll_handle_implement_interface(zend_class_entry *interface, zend_class_entry *implementor)
  {
  	if (implementor->type == ZEND_USER_CLASS) {
  		zend_error_noreturn(E_ERROR, "Io\\Poll\\Handle cannot be implemented by user classes");
@@@ -885,25 -923,87 +919,83 @@@ PHP_METHOD(Io_Poll_Context, wait
  		RETURN_THROWS();
  	}

- 	php_poll_event *events = safe_emalloc((size_t) max_events, sizeof(*events), 0);
- 	int num_events = php_poll_wait(intern->ctx, events, (int) max_events, timeout ? &timeout_ts : NULL);
+ 	/* Buffered watchers go first, then a non-blocking backend check */
+ 	php_io_poll_watcher_object **buffered = NULL;
+ 	int n_buffered = 0;
+ 	if (intern->buffered_readers) {
+ 		ZEND_HASH_FOREACH_VAL(intern->watchers, zval *zv) {
+ 			php_io_poll_watcher_object *watcher = PHP_POLL_WATCHER_OBJ_FROM_ZOBJ(Z_OBJ_P(zv));
+ 			if (watcher->disarmed || !php_io_poll_watcher_reads_buffered(watcher)
+ 					|| !watcher->ext_ops->has_buffered_data(watcher->handle)) {
+ 				continue;
+ 			}
+ 			if (!buffered) {
+ 				buffered = safe_emalloc((size_t) max_events, sizeof(*buffered), 0);
+ 			}
+ 			buffered[n_buffered++] = watcher;
+ 			if (n_buffered == max_events) {
+ 				break;
+ 			}
+ 		} ZEND_HASH_FOREACH_END();
+ 	}

- 	if (num_events < 0) {
- 		php_poll_error err = php_poll_get_error(intern->ctx);
- 		efree(events);
- 		php_io_poll_throw_failed_operation(
- 				php_io_poll_failed_wait_class_entry, "Poll wait failed", err);
- 		RETURN_THROWS();
+ 	php_poll_event *events = safe_emalloc((size_t) max_events, sizeof(*events), 0);
+ 	int num_events = 0;
+ 	if (n_buffered < max_events) {
+ 		struct timespec zero_ts = {0, 0};
+ 		num_events = php_poll_wait(intern->ctx, events, (int) max_events - n_buffered,
+ 				n_buffered ? &zero_ts : (timeout ? &timeout_ts : NULL));
+ 		if (num_events < 0) {
+ 			php_poll_error err = php_poll_get_error(intern->ctx);
+ 			if (n_buffered == 0 || err != PHP_POLL_ERR_INTERRUPTED) {
+ 				efree(events);
+ 				if (buffered) {
+ 					efree(buffered);
+ 				}
+ 				php_io_poll_throw_failed_operation(
+ 						php_io_poll_failed_wait_class_entry, "Poll wait failed", err);
+ 				RETURN_THROWS();
+ 			}
+ 			/* The buffered data is the result */
+ 			num_events = 0;
+ 		}
  	}

- 	array_init(return_value);
+ 	array_init_size(return_value, (uint32_t) (n_buffered + num_events));
+
+ 	for (int i = 0; i < n_buffered; i++) {
+ 		php_io_poll_watcher_object *watcher = buffered[i];
+ 		watcher->triggered_events = PHP_POLL_READ;
+ 		watcher->reported = true;
+
 -		zval watcher_zv;
 -		ZVAL_OBJ_COPY(&watcher_zv, &watcher->std);
 -		add_next_index_zval(return_value, &watcher_zv);
++		add_next_index_object(return_value, zend_object_copy(&watcher->std));
+ 	}

  	for (int i = 0; i < num_events; i++) {
  		php_io_poll_watcher_object *watcher = (php_io_poll_watcher_object *) events[i].data;
- 		if (watcher) {
- 			watcher->triggered_events = events[i].revents;
+ 		if (!watcher) {
+ 			continue;
+ 		}
+ 		if (watcher->watched_events & PHP_POLL_ONESHOT) {
+ 			watcher->disarmed = true;
+ 		}
+ 		if (watcher->reported) {
+ 			/* Already reported from its buffer */
+ 			watcher->triggered_events |= events[i].revents;
+ 			continue;
+ 		}
+ 		watcher->triggered_events = events[i].revents;

- 			add_next_index_object(return_value, zend_object_copy(&watcher->std));
 -		zval watcher_zv;
 -		ZVAL_OBJ_COPY(&watcher_zv, &watcher->std);
 -		add_next_index_zval(return_value, &watcher_zv);
++		add_next_index_object(return_value, zend_object_copy(&watcher->std));
+ 	}
+
+ 	/* Reset the per-wait flag. A buffer-only one-shot is still armed in the backend */
+ 	for (int i = 0; i < n_buffered; i++) {
+ 		php_io_poll_watcher_object *watcher = buffered[i];
+ 		watcher->reported = false;
+ 		if ((watcher->watched_events & PHP_POLL_ONESHOT) && !watcher->disarmed) {
+ 			watcher->disarmed = true;
+ 			php_poll_remove(intern->ctx, (int) watcher->fd);
  		}
  	}