Skip to content

Commit b69b338

Browse files
committed
php#53: Refactoring process_stream_array2
Data structures refactored, code optimized.
1 parent e9e4c76 commit b69b338

File tree

3 files changed

+77
-30
lines changed

3 files changed

+77
-30
lines changed

ext/standard/streamsfuncs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ PHP_FUNCTION(stream_select)
753753
tv_async.tv_usec = usecnull ? 0 : usec;
754754
tv_p_async = &tv_async;
755755
}
756-
retval = async_select(r_array, w_array, e_array, tv_p_async);
756+
retval = php_stream_select_async(r_array, w_array, e_array, tv_p_async);
757757
RETURN_LONG(retval >= 0 ? retval : 0);
758758
}
759759

main/network_async.c

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,8 @@ ZEND_API int php_select_async(php_socket_t max_fd, fd_set *rfds, fd_set *wfds, f
667667
typedef struct async_stream_callback_s {
668668
zend_coroutine_event_callback_t callback;
669669
io_descriptor_t fd;
670-
zval *stream;
670+
php_stream *stream;
671+
zend_async_poll_event_t *event;
671672
async_poll_event events;
672673
bool ready;
673674
struct async_stream_callback_s *next; // For linked list
@@ -712,14 +713,13 @@ static zend_always_inline bool process_stream_array(zval *streams, async_poll_ev
712713
return true;
713714
}
714715

715-
zval *elem;
716+
zval *z_stream;
716717
php_stream *stream;
717-
php_netstream_data_t *sock;
718718
int count = 0;
719719

720-
ZEND_HASH_FOREACH_VAL(Z_ARR_P(streams), elem) {
721-
ZVAL_DEREF(elem);
722-
php_stream_from_zval_no_verify(stream, elem);
720+
ZEND_HASH_FOREACH_VAL(Z_ARR_P(streams), z_stream) {
721+
ZVAL_DEREF(z_stream);
722+
php_stream_from_zval_no_verify(stream, z_stream);
723723
if (stream == NULL) continue;
724724

725725
// Try to get async event handle from socket streams first
@@ -737,6 +737,8 @@ static zend_always_inline bool process_stream_array(zval *streams, async_poll_ev
737737
return false;
738738
}
739739

740+
io_descriptor.type = is_socket ? IO_DESCRIPTOR_SOCKET : IO_DESCRIPTOR_FD;
741+
740742
// If stream has no event handle, create it.
741743
if (event_handle == NULL) {
742744
if (io_descriptor.type == IO_DESCRIPTOR_SOCKET) {
@@ -764,7 +766,8 @@ static zend_always_inline bool process_stream_array(zval *streams, async_poll_ev
764766
callback->callback.coroutine = coroutine;
765767
callback->callback.base.ref_count = 1;
766768
callback->callback.base.callback = async_stream_callback_resolve;
767-
callback->stream = elem;
769+
callback->stream = stream;
770+
callback->event = event_handle;
768771
callback->fd = io_descriptor;
769772
callback->events = events;
770773
callback->ready = false;
@@ -813,7 +816,44 @@ static void modify_stream_array(zval *stream_array, HashTable *ready_streams) {
813816
ZVAL_ARR(stream_array, ht);
814817
}
815818

816-
ZEND_API int async_select(zval *read_streams, zval *write_streams, zval *except_streams, struct timeval *tv)
819+
/**
820+
* Asynchronous select() implementation for PHP stream arrays in coroutine contexts.
821+
*
822+
* This function provides an async-compatible version of the standard select()
823+
* system call, allowing coroutines to wait for I/O events on multiple PHP streams
824+
* without blocking the entire thread.
825+
*
826+
* @param read_streams Array of streams to monitor for read events, or NULL if
827+
* not monitoring for read events. Modified to indicate
828+
* which streams are ready for reading.
829+
* @param write_streams Array of streams to monitor for write events, or NULL
830+
* if not monitoring for write events. Modified to indicate
831+
* which streams are ready for writing.
832+
* @param except_streams Array of streams to monitor for exception events, or NULL
833+
* if not monitoring for exceptions. Modified to indicate
834+
* which streams have exceptions.
835+
* @param tv Timeout specification, or NULL for infinite timeout.
836+
* Specifies maximum time to wait for events.
837+
*
838+
* @return On success, returns the number of streams that are
839+
* ready for I/O. Returns 0 if the timeout expired with no
840+
* events. Returns -1 on error, with errno set appropriately:
841+
* - EINVAL: Not called from async context or invalid input
842+
* - ENOMEM: Memory allocation failure
843+
* - EINTR: Operation interrupted
844+
* - ECANCELED: Operation was cancelled
845+
* - ETIMEDOUT: Operation timed out
846+
*
847+
* @note This function can only be called from within an async
848+
* coroutine context. Calling from regular PHP code will
849+
* result in EINVAL error.
850+
* @note The function modifies the input stream arrays to
851+
* indicate which streams triggered events, similar to
852+
* the standard select() behavior.
853+
*
854+
* @see select(2), php_poll2_async(), php_select_async()
855+
*/
856+
ZEND_API int php_stream_select_async(zval *read_streams, zval *write_streams, zval *except_streams, struct timeval *tv)
817857
{
818858
zend_coroutine_t *coroutine = ZEND_ASYNC_CURRENT_COROUTINE;
819859

@@ -827,7 +867,7 @@ ZEND_API int async_select(zval *read_streams, zval *write_streams, zval *except_
827867
// Calculate timeout in milliseconds
828868
zend_ulong timeout = 0;
829869
if (tv != NULL) {
830-
timeout = (zend_ulong)(tv->tv_sec * 1000 + tv->tv_usec / 1000);
870+
timeout = (zend_ulong)tv->tv_sec * 1000 + (zend_ulong)tv->tv_usec / 1000;
831871
}
832872

833873
zend_async_waker_new_with_timeout(coroutine, timeout, NULL);
@@ -862,26 +902,33 @@ ZEND_API int async_select(zval *read_streams, zval *write_streams, zval *except_
862902
zend_hash_init(&ready_except_streams, 0, NULL, ZVAL_PTR_DTOR, 0);
863903

864904
// Collect ready streams from triggered_events
865-
zval *callback_val;
866-
ZEND_HASH_FOREACH_VAL(coroutine->waker->triggered_events, callback_val) {
867-
if (Z_TYPE_P(callback_val) == IS_PTR) {
868-
async_stream_callback_t *cb = (async_stream_callback_t *)Z_PTR_P(callback_val);
869-
if (cb->ready) {
870-
HashTable *target = NULL;
871-
if (cb->stream_type & ASYNC_READABLE) {
872-
target = &ready_read_streams;
873-
} else if (cb->stream_type & ASYNC_WRITABLE) {
874-
target = &ready_write_streams;
875-
} else if (cb->stream_type & ASYNC_PRIORITIZED) {
876-
target = &ready_except_streams;
905+
async_stream_callback_t *cb;
906+
ZEND_HASH_FOREACH_PTR(coroutine->waker->triggered_events, cb) {
907+
if (cb->ready) {
908+
HashTable *target = NULL;
909+
if (cb->event->triggered_events & ASYNC_READABLE) {
910+
zval *dest_elem = zend_hash_next_index_insert_ptr(&ready_read_streams, cb->stream);
911+
if (dest_elem) {
912+
zval_add_ref(dest_elem);
913+
} else {
914+
zend_throw_exception(NULL, "Failed to insert ready read stream", 0);
915+
goto error;
916+
}
917+
} else if (cb->event->triggered_events & ASYNC_WRITABLE) {
918+
zval *dest_elem = zend_hash_next_index_insert_ptr(&ready_read_streams, cb->stream);
919+
if (dest_elem) {
920+
zval_add_ref(dest_elem);
921+
} else {
922+
zend_throw_exception(NULL, "Failed to insert ready write stream", 0);
923+
goto error;
877924
}
878-
879-
if (target != NULL) {
880-
zval *stream_elem = cb->stream;
881-
zval *dest_elem = zend_hash_next_index_insert(target, stream_elem);
882-
if (dest_elem) {
883-
zval_add_ref(dest_elem);
884-
}
925+
} else if (cb->event->triggered_events & ASYNC_PRIORITIZED) {
926+
zval *dest_elem = zend_hash_next_index_insert_ptr(&ready_read_streams, cb->stream);
927+
if (dest_elem) {
928+
zval_add_ref(dest_elem);
929+
} else {
930+
zend_throw_exception(NULL, "Failed to insert ready except stream", 0);
931+
goto error;
885932
}
886933
}
887934
}

main/network_async.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ ZEND_API int async_await_socket(php_netstream_data_t *sock, short events, struct
2727

2828
ZEND_API int php_poll2_async(php_pollfd *ufds, unsigned int nfds, int timeout);
2929
ZEND_API int php_select_async(php_socket_t max_fd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tv);
30-
ZEND_API int async_select(zval *read_streams, zval *write_streams, zval *except_streams, struct timeval *tv);
30+
ZEND_API int php_stream_select_async(zval *read_streams, zval *write_streams, zval *except_streams, struct timeval *tv);
3131

3232
ZEND_API int php_network_getaddrinfo_async(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
3333
ZEND_API struct hostent* php_network_gethostbyname_async(const char *name);

0 commit comments

Comments
 (0)