Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot run external program inside coroutine, when custom SIGCHLD handler was established #5583

Open
alex-rsk opened this issue Nov 22, 2024 · 4 comments

Comments

@alex-rsk
Copy link

alex-rsk commented Nov 22, 2024

Please answer these questions before submitting your issue.

1. What did you do? If possible, provide a simple script for reproducing the error.

We are using swoole wrapper LaravelS, but my issue related more to the Swoole per se and IPC, than to specific laravelS problems.

We run Swoole Process, which, in turn, has been launching child processes.
As we need to watch falling of these child processes , we set custom handler for SIGCHLD in the parent process.
Child processes could call external programs (cat, touch etc). Without SIGCHLD handling in parent process, everything is working good.
But after adding SIGCHLD handler, any program cannot start.

//The sample. Process init 
..
protected function init() {
  $this->child1 = new Process(function(Process $process) {
                $process->exec('touch_launcher'); //  <-- calls `touch`
            },true,1);

            $this->child1_process_pid = $this->child1->start();
}
...
Coroutine::set(['hook_flags' => SWOOLE_HOOK_PROC]);

run(function() {
     $this->handleSignals();
})

function handleSignals() {
    {
        $callback = function() {
            $this->close(false);
        };
        Process::signal(SIGINT, $callback);

        Process::signal(SIGTERM, $callback);

        Process::signal(SIGCHLD, function ($sig) {
            while ($ref = Process::wait(false)) {
                $message = match ($ref['pid']) {
                    $this->child1_process_pid => 'Child process 1 closed',
                    $this->child2_process_pid => 'Child process 2 closed',
                };
                $this->close(message: $message);
            }
        });
    }

Calling external program in child process touch_launcher:

// Run Symfony/Process
$procResult = Process::timeout(5)->run('touch /tmp/test');
//or Swoole System
$procResult = System::exec('touch /tmp/test');

Estimated result : an external program starts.

Obtained result: Program does not start. We are getting error:

Swoole\Coroutine::exec(): The signal [SIGCHLD] is registered, cannot execute swoole_coroutine_exec

3. What did you expect to see?

An external program (any console utility, like touch could be an example) starts.

4. What did you see instead?

Program does not start, we are getting error
Swoole\Coroutine::exec(): The signal [SIGCHLD] is registered, cannot execute swoole_coroutine_exec

5. What version of Swoole are you using (show your php --ri swoole)?

5.1.4

  1. What is your machine environment used (show your uname -a & php -v & gcc -v) ?
    Linux Ubuntu 22 x86_64
@alex-rsk alex-rsk changed the title Cannot run external program inside coroutine, when custom SIGCHLD handler was established #484 Cannot run external program inside coroutine, when custom SIGCHLD handler was established Nov 22, 2024
@NathanFreeman
Copy link
Member

Because you haven't provided the complete code, I'm not very clear about the entire execution flow. This error occurs because you executed code similar to the one below, where you listen for the SIGCHLD signal while using a blocking function. Thus, when the parent process receives the SIGCHLD signal, it blocks the current coroutine. Therefore, you need to rewrite the code to use the non-blocking function System::wait to monitor the child process exit.

@NathanFreeman
Copy link
Member

NathanFreeman commented Nov 23, 2024

<?php
use Swoole\Coroutine;
use Swoole\Coroutine\System;
use Swoole\Process;

Process::signal(SIGCHLD, function(){});

$process = new Process(function () {
    echo 'Hello';
});
$process->start();

Coroutine\run(function () use ($process) {
    System::exec('ls -al');  // The signal [SIGCHLD] is registered, cannot execute swoole_coroutine_exec
});

try

<?php
use Swoole\Coroutine;
use Swoole\Coroutine\System;
use Swoole\Process;

$process = new Process(function () {
    echo 'Hello';
});
$process->start();

Coroutine\run(function () use ($process) {
    System::exec('ls -al');
    $status = System::wait();  // wait child process exit
    assert($status['pid'] === $process->pid);
    var_dump($status);
});

@grvoyt
Copy link

grvoyt commented Nov 23, 2024

We work together. Here is an example of the code.
Try this

test.php

<?php

while(1) {
    echo 'hi'.PHP_EOL;
    sleep(1);
}

main.php

<?php

use function Swoole\Coroutine\go;
use function Swoole\Coroutine\run;
use Swoole\Process;

$proc = new Process(function (Process $process) {
    $process->exec('php',['test.php']); //set your abs php
},1,1);
$proc->start();

run(function() use ($proc) {
    HandleSignals();

    go(function () use ($proc) {
        $sock = $proc->exportSocket();
        while(1) {
            $data = $sock->recvLine(timeout: 1);
            if(empty($data) ) continue;
            echo 'GET '.$data.PHP_EOL;
        }
    });

    go(function() {
       $proc = proc_open('php test.php',[],$pipes);
       if( is_resource($proc) ) {
           $status  = proc_get_status($proc);
           echo json_encode($status).PHP_EOL;
       }
       sleep(2);
       proc_close($proc);
        echo 'closed';
    });

});

function HandleSignals() {
    Process::signal(SIGTERM, function () {
       echo 'TERM'.PHP_EOL;
    });

    Process::signal(SIGCHLD, function () {
        while( $ref = Process::wait(false) ) {
            echo json_encode($ref);
        }
    });
}

A third-party program is running in the $proc variable and it returns information to stderr.

Depending on the response, we have to call various commands via proc_open asynchronously in the coroutine, as seen below.

when we call proc_open, we receive the message: Warning: proc_open(): [SIGCHLD] signal is registered, swoole_proc_open cannot be executed

@NathanFreeman
Copy link
Member

NathanFreeman commented Nov 29, 2024

@grvoyt try

<?php

use function Swoole\Coroutine\go;
use function Swoole\Coroutine\run;
use Swoole\Process;
use Swoole\Coroutine\System;

$proc = new Process(function (Process $process) {
    $process->exec('php',['test.php']); //set your abs php
},1,1);
$proc->start();

run(function() use ($proc) {
    HandleSignals();

    go(function () use ($proc) {
        $sock = $proc->exportSocket();
        while(1) {
            $data = $sock->recvLine(timeout: 1);
            if(empty($data) ) continue;
            echo 'GET '.$data.PHP_EOL;
        }
    });

    go(function() {
       $proc = proc_open('php test.php',[],$pipes);
       if( is_resource($proc) ) {
           $status  = proc_get_status($proc);
           echo json_encode($status).PHP_EOL;
       }
       sleep(2);
       proc_close($proc);
        echo 'closed';
    });

    while( $ref = System::wait() ) {
        echo json_encode($ref);
    }

});

function HandleSignals() {
    Process::signal(SIGTERM, function () {
       echo 'TERM'.PHP_EOL;
    });
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants