Skip to content

Conversation

@NathanFreeman
Copy link
Contributor

@NathanFreeman NathanFreeman commented Oct 26, 2025

Hello everyone,

In the issue swoole/swoole-src#5890, a developer inquired about the possibility of enabling coroutine support for the FTP extension in a Swoole environment. After a thorough analysis of the PHP FTP extension source code, we identified that the current implementation of the my_poll function uses a hardcoded traditional poll mechanism to handle socket read/write events.

This synchronous I/O model lacks the flexibility of an underlying event-driven architecture, causing FTP operations to block the entire process and preventing effective collaboration with Swoole's coroutine scheduler. Specifically, when an FTP read/write operation is initiated, my_poll enters a blocking wait state, during which coroutine switching cannot occur, thereby breaking the concurrency of Swoole coroutines.

To resolve this issue, we have refactored the my_poll function in ftp.c with the following key modifications:

  1. Replace php_pollfd_for_ms with a function pointer named php_ftp_pollfd_for_ms to achieve a pluggable architecture for the FTP socket polling mechanism.

  2. During the extension initialization phase, this pointer defaults to pointing to the original php_pollfd_for_ms implementation, ensuring that existing synchronous logic remains unaffected.

  3. In the Swoole extension, this function pointer can be replaced with a coroutine-supported polling function (such as a non-blocking version based on an event loop), thereby enabling non-blocking FTP socket operations in the main thread.

Through these improvements, the FTP extension can now deeply integrate with Swoole's coroutine runtime, enabling truly non-blocking, coroutine-friendly FTP client operations.

This modification does not affect the original FTP logic and introduces no syntactic changes.

With this change, I can easily assign a value to php_ftp_pollfd_for_ms in my own extension, enabling coroutine support for FTP without any disruptive intrusions into the FTP code.

@NathanFreeman
Copy link
Contributor Author

<?php
use function Swoole\Coroutine\run;
use function Swoole\Coroutine\go;

run(function() {
    go(function() {
    	// The file upload time is long.
    	$connection = ftp_connect('127.0.0.1', 21, 10);
    	$result = ftp_login($connection, 'ftpuser', '123456');
    	$result = ftp_put($connection, '3gfile', '3gfile', FTP_BINARY);
    });

    // The file upload time is short.
    go(function() {
    	$connection = ftp_connect('127.0.0.1', 21, 10);
    	$result = ftp_login($connection, 'ftpuser', '123456');
    	$result = ftp_put($connection, '3kfile', '3kfile', FTP_BINARY);
    	var_dump($result, 456);
    });  
});

In the synchronous blocking model, the program must handle upload tasks sequentially: the 3KB small file can only start uploading after the 3GB large file has been completely transferred. During this process, significant time is consumed waiting for network I/O.

After integrating coroutine-enabled FTP support, Swoole's event-driven scheduler will proactively suspend the current coroutine and yield CPU resources when the 3GB file upload encounters network I/O blocking. At this point, the scheduler immediately activates and executes the coroutine responsible for uploading the 3KB file. Since the small file upload can be completed almost instantaneously, the overall system processing efficiency is significantly enhanced, maximizing resource utilization.

@NathanFreeman
Copy link
Contributor Author

The implementation steps to enable FTP coroutine support in Swoole are straightforward and clear. see swoole/swoole-src#5899

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant