Skip to content

Conversation

EdmondDantes
Copy link
Contributor

No description provided.

…lect

       Replace per-connection coroutines with centralized event loop using stream_select() to monitor all active sockets simultaneously. Main loop now handles both new connections
       and existing client requests through single event monitoring, eliminating "Bad File handler" errors by proper socket lifecycle management.

       Key changes:
       - Single stream_select() monitors server + all active client sockets
       - handleSingleRequest() processes one request per invocation
       - Automatic cleanup of closed connections from activeConnections array
       - Proper Keep-Alive support through socket reuse
       - Replace php_poll2_async with network_async_accept_incoming() to prevent EventLoop conflicts
       - Add network_async_connect_socket() for consistent async connection handling
       - Fix SSL socket EventLoop duplication in accept and close operations
       - Add proper poll_event cleanup in SSL sockets to prevent memory leaks
       - Create SSL async tests for timeout, client-server, and concurrent operations

+ test 025-ssl_stream_socket_accept_timeout.phpt
       - Replace corrupted base64 certificate with valid RSA 2048-bit cert
       - Use temporary files instead of data:// URIs for Windows OpenSSL compatibility
       - Add explicit TLS crypto methods for server and client contexts
       - Fix client SSL connection by adding ssl:// prefix to server address
       - Add proper cleanup of temporary certificate files
       - Extract valid certificate from test 026 to ssl_test_cert.pem and ssl_test_key.pem
       - Replace malformed certificate in test 025 with valid shared certificate
       - Simplify test 026 by removing temp file creation, use external cert files
       - Fix SKIPIF bug in test 027 and update to use shared certificate files
       - Improve code reuse, maintainability, and test reliability
         Replace broken stream_select logic with simple coroutine-per-connection.
         Each socket now handled by dedicated handleSocket() coroutine for proper keep-alive.
…ore waiting

         - Try accept() immediately instead of waiting for READ event first
         - Only wait in event loop if accept() returns EAGAIN/EWOULDBLOCK
         - Eliminates unnecessary syscalls when connections are already ready
         - Refactor code structure with centralized error handling via goto
         - Use EXPECTED/UNEXPECTED macros and const variables for better optimization
…tations

       Apply deferred socket closure mechanism to prevent LibUV race conditions in both xp_socket.c and xp_ssl.c modules. Previously, closesocket() was called before poll_event
       cleanup, causing potential race conditions where LibUV operations could access already-closed socket descriptors.

       Changes:
       - xp_socket.c: Fix php_sockop_close() to transfer socket ownership to LibUV before disposal
       - xp_ssl.c: Apply same fix to php_openssl_sockop_close() with SSL-specific cleanup preserved
       - Move Windows async logic (shutdown/polling) before socket ownership transfer in both modules
       - When poll_event exists, transfer socket via poll_event->socket and ZEND_ASYNC_EVENT_SET_CLOSE_FD flag
       - Call poll_event dispose immediately after flag setting for proper cleanup order
       - libuv_reactor.c: Add libuv_close_poll_handle_cb() with cross-platform fd/socket closure
       - zend_async_API.h: Define ZEND_ASYNC_EVENT_F_CLOSE_FD flag for conditional descriptor closure

       This ensures LibUV completes all pending operations before socket closure across both socket implementations.
       Implements a new poll proxy structure that allows multiple consumers
       to share a single zend_async_poll_event_t, reducing file descriptor
       and libuv handle usage.

       Key features:
       - zend_async_poll_proxy_t holds reference to shared poll event
       - Event aggregation: proxy events are OR'ed into base event
       - Reference counting for automatic cleanup
       - Standard event interface (start/stop/dispose/callbacks)

       Changes:
       - Add zend_async_poll_proxy_t structure and typedef
       - Add libuv_new_poll_proxy_event() creation function
       - Implement proxy start/stop/dispose methods with event aggregation
       - Add ZEND_ASYNC_NEW_POLL_PROXY_EVENT() macros
       - Update zend_async_reactor_register() signature and calls
       - Add zend_async_new_poll_proxy_event_fn function pointer

       Usage:
         zend_async_poll_event_t *base = ZEND_ASYNC_NEW_POLL_EVENT(fd, 0, events);
         zend_async_poll_proxy_t *proxy = ZEND_ASYNC_NEW_POLL_PROXY_EVENT(base, specific_events);

       This enables efficient FD sharing while maintaining clean abstraction.
        Corrects the poll proxy implementation to properly handle multiple
        proxies sharing the same base poll event through proper event
        aggregation and proxy tracking.

        Key fixes:
        - Add proxies array to async_poll_event_t for tracking active proxies
        - Fix libuv_poll_proxy_start(): use bitwise check (events & proxy->events)
          to only add missing events, preventing unnecessary libuv restarts
        - Fix libuv_poll_proxy_stop(): properly aggregate remaining proxy events
          after removing stopped proxy, ensuring correct event mask
        - Add proxy array management: async_poll_add/remove_proxy() helpers
        - Add memory management: free proxies array in libuv_poll_dispose()
        - Fix dispose: call stop() before releasing base event reference

        This ensures correct FD sharing where multiple proxies can safely
        use overlapping events on the same file descriptor without conflicts.

        Before: stop() incorrectly removed events other proxies might need
        After: stop() recalculates events from all remaining active proxies
LibUV may return the UV_EBADF code when the remote host closes the connection while the descriptor is still present in the EventLoop. For POLL events, we handle this by ignoring the situation so that the coroutine receives the ASYNC_DISCONNECT flag.
       - Make proxy management functions zend_always_inline for performance
       - Remove premature memory deallocation in async_poll_remove_proxy()
       - Add early exit optimization in async_poll_aggregate_events()
       - Simplify async_poll_notify_proxies() - remove alloca() usage
       - Fix callback parameters: pass filtered events and exception to proxies
       - Only notify proxies with matching events for efficiency
       - Add forward declaration to fix compilation order
       - Ensure reference counting safety during callback processing

       Result: fewer callback invocations, reduced allocations, precise event delivery.
       Implement proxy events that allow multiple consumers to share a single zend_async_poll_event_t, reducing FD and libuv handle usage. Fix EV integration by returning separate
       proxy events instead of shared base events, preventing premature event cleanup when coroutines exit.
… 53-optimize-descriptor-registration-in-event-loop-exp
       Add automatic stopping of waker events for coroutines enqueued after libuv_reactor_execute. This optimization saves file descriptors and improves EV performance by cleaning
       up unused poll events immediately after reactor processing.

       Features:
       - Inline optimized functions with zend_always_inline for zero overhead
       - Circular buffer traversal with bitwise operations for power-of-2 optimization
       - Branch prediction hints with EXPECTED/UNEXPECTED macros
       - Conditional compilation under LIBUV_STOP_WAKER_EVENTS_AFTER_EXECUTE flag
         Add time-based throttling to prevent excessive ZEND_ASYNC_REACTOR_EXECUTE calls by checking reactor handles only every 100ms instead of on every tick.
       Add automatic cleanup of waker events for coroutines that are queued
       after reactor execution. When coroutines become active after ZEND_ASYNC_REACTOR_EXECUTE,
       their event lists are immediately cleared to prevent stale event data.

       - Add clean_events_for_resumed_coroutines() function to iterate through
         newly queued coroutines in circular buffer
       - Track head position before/after reactor execution
       - Clear waker events using ZEND_ASYNC_WAKER_CLEAN_EVENTS macro
       - Optimize with cached mask and item_size values
       - Add null checks for coroutine and waker validity
  Handle circular buffer reallocation during reactor execution to prevent
  crashes when cleaning waker events for resumed coroutines.

  - Track previous_data, previous_count, and previous_head before reactor
  - Detect reallocation by comparing queue->data pointers
  - After reallocation: start cleanup from previous_count position
  - Add assertion to verify tail=0 after reallocation
  - Maintain original logic for non-reallocation cases
        - Reduce UDP timeout from 2s to 0.2s in test 030
        - Reduce TCP timeout from 1s to 0.2s in test 031
        - Remove timing measurements and elapsed time checks
        - Simplify timeout validation to only check timed_out flag
        - Reduce client delay from 2s to 0.5s in TCP tes
…cription for the changes:

        Optimize exception handling in ext/async with fast save/restore operations

        This commit replaces standard exception save/restore patterns with optimized
        fast variants throughout the ext/async extension to improve performance during
        coroutine context switching and async operations.

        Changes include:

        * scheduler.c: Optimize 7+ exception handling points including:
          - TRY_HANDLE_SUSPEND_EXCEPTION macro implementation
          - cancel_queued_coroutines() function
          - async_scheduler_coroutine_suspend() function
          - async_scheduler_dtor() cleanup

        * coroutine.c: Optimize save/restore pairs in:
          - async_coroutine_finalize()
          - finally_handlers_iterator_handler()

        * scope.c: Optimize scope completion callback exception handling

        * zend_common.c: Optimize zend_exception_merge() function

        * exceptions.c: Optimize async_extract_exception() function

        The optimization uses direct pointer manipulation instead of repeated EG()
        macro access, following the pattern:
          zend_object **exception_ptr = &EG(exception);
          zend_object **prev_exception_ptr = &EG(prev_exception);
          zend_exception_save_fast(exception_ptr, prev_exception_ptr);
          // ... code execution ...
          zend_exception_restore_fast(exception_ptr, prev_exception_ptr);

        This reduces overhead in exception state management during async operations
        while maintaining identical exception handling semantics.
…nup stability

        The previous clean_events_for_resumed_coroutines algorithm was unstable due to
        complex tracking of circular buffer changes during coroutine additions and
        buffer reallocations. This commit introduces a dedicated resumed_coroutines
        queue that provides a more reliable approach.

        Changes:
        - Add resumed_coroutines circular buffer to module globals
        - Initialize resumed_coroutines queue in PHP_RINIT_FUNCTION
        - Track resumed coroutines in async_scheduler_coroutine_enqueue() and async_coroutine_resume()
        - Replace complex clean_events_for_resumed_coroutines() with simple process_resumed_coroutines()
        - Update scheduler_next_tick() and fiber_entry() to use new queue-based approach
        - Add proper cleanup in both engine_shutdown() and async_scheduler_dtor() to prevent memory leaks

        Benefits:
        - Eliminates race conditions from buffer reallocation tracking
        - Simplifies logic by checking queue contents instead of head pointer changes
        - Improves code readability and maintainability
        - Fixes memory leak of 8192 bytes in circular buffer allocation
…ion-in-event-loop' into 53-optimize-descriptor-registration-in-event-loop
@EdmondDantes EdmondDantes added this to the Stabilization2 milestone Sep 21, 2025
@EdmondDantes EdmondDantes self-assigned this Sep 21, 2025
@EdmondDantes EdmondDantes added the enhancement New feature or request label Sep 21, 2025
@EdmondDantes EdmondDantes linked an issue Sep 21, 2025 that may be closed by this pull request
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR optimizes descriptor registration in the event loop to improve performance in high-concurrency scenarios. The optimization focuses on reducing overhead in the scheduler and improving exception handling patterns.

  • Optimizes event loop descriptor registration with faster exception handling functions
  • Improves scheduler performance by reducing reactor tick frequency and adding process-resumed-coroutines functionality
  • Adds comprehensive test coverage for stream operations including SSL, UDP, and timeout scenarios

Reviewed Changes

Copilot reviewed 38 out of 38 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
zend_common.c Updates exception handling to use optimized fast save/restore functions
scheduler.c Major scheduler optimizations including reactor tick throttling and resumed coroutines processing
php_async.h Adds reactor check interval constant and resumed coroutines buffer
libuv_reactor.c Enhances poll event handling with proxy support and improved error handling
coroutine.c Updates coroutine finalization and resume logic with optimized exception handling
async.c Initializes new resumed coroutines buffer and reactor optimization globals
tests/stream/* Adds extensive test coverage for SSL, UDP, TCP operations and edge cases
benchmarks/http_server_keepalive.php Optimizes HTTP server implementation for better performance

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

        * Remove --enable-opcache from configure (OPcache is built-in by default)
        * Remove -d zend_extension=opcache.so from test commands (legacy syntax)
        * Update all workflows: build.yml, build-linux.yml, build-macos.yml, build-alpine.yml, build-freebsd.yml
        * Maintains full OPcache and JIT testing functionality
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 45 out of 48 changed files in this pull request and generated 5 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@EdmondDantes EdmondDantes merged commit 4bc433b into main Sep 22, 2025
1 check passed
@EdmondDantes EdmondDantes deleted the 53-optimize-descriptor-registration-in-event-loop branch September 22, 2025 07:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optimize descriptor registration in event loop

1 participant