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

process immediate exit after sigterm #36

Closed
Ekstazi opened this issue Feb 25, 2019 · 6 comments
Closed

process immediate exit after sigterm #36

Ekstazi opened this issue Feb 25, 2019 · 6 comments
Labels

Comments

@Ekstazi
Copy link
Contributor

Ekstazi commented Feb 25, 2019

Some example code to reproduce

test.php

<?php
require __DIR__.'/vendor/autoload.php';

$onInterrupt = \Amp\Loop::onSignal(SIGINT, function ($reference){
	echo "sigterm\n";
	//\Amp\Loop::cancel($reference);
});
\Amp\Loop::run(function() use($onInterrupt ){
	$process = new \Amp\Process\Process("php test-process.php");
	$pid = yield $process->start();
	\Amp\ByteStream\pipe($process->getStdout(), \Amp\ByteStream\getStdout());
	\Amp\ByteStream\pipe($process->getStderr(), \Amp\ByteStream\getStderr());
	$code = yield $process->join();
	echo $code;
	if(file_exists("/proc/{$pid}")) {
		echo "Process still running\n";
	}
	yield new \Amp\Delayed(10000);
	\Amp\Loop::cancel($onInterrupt);
});

test-process.php

<?php
require __DIR__.'/vendor/autoload.php';

//pcntl_async_signals(true);
\Amp\Loop::onSignal(SIGINT, function (){
	echo "sigterm from parent\n";
});
echo "Start\n";
\Amp\Loop::repeat(100, function(){
	echo "test\n";
	sleep(1);
});
\Amp\Loop::run();
echo "exit\n";

Steps to reproduce

  1. I start script test.php. Example output was:
Start
test
test
test

Process list:

26434 pts/7    S+     0:00 php ./test.php
26435 pts/7    S+     0:00 sh -c { (php test-process.php) <&3 3<&- 3>/dev/null & } 3<&0;pid=$!; echo $pid >&3; wait $pid; RC=$?; echo $RC >&3; exit $RC
26436 pts/7    S+     0:00 php test-process.php
  1. I send sigint(ctrl+c) to script. Output was:
^C0Process still running
sigterm from parent
test
sigterm

Processs list:

27301 pts/7    S+     0:00 php ./test.php
27303 pts/7    S+     0:00 php test-process.php
  1. I wait for script to finish. Output was:
test
test
test
test
test
test
test
test
test
test

Actual behavior

As you can see when sigint received php closed process wrapper (sh -c) and send sigint to test-process.php. amp/Process wait for process wrapper to finish but doesn't wait for test-process.php. But pipes will continue to work after that. The child process only killed after main process exit.

Expected behavior

Process::join only resolves when test-process.php finished/halted/cancelled.

@Ekstazi Ekstazi changed the title process immediate exir after sigterm process immediate exit after sigterm Feb 25, 2019
@trowski
Copy link
Member

trowski commented Feb 26, 2019

I was not able to reproduce this on macOS, but I was able to reproduce this on Ubuntu. The sh process terminates in both, but on macOS join() does not resolve until the child PHP process exits, whereas on Ubuntu join() resolves as soon as the sh process ends.

@trowski trowski added the bug label Feb 26, 2019
@Ekstazi
Copy link
Contributor Author

Ekstazi commented Feb 26, 2019

Thx for fix. But signals still broadcasted to child process. How i can prevent it ? I handled signals in my parent process and want to send it to child only from code.

@Ekstazi
Copy link
Contributor Author

Ekstazi commented Feb 26, 2019

Also signal handling not worked after that fix. I changed test.php scrip:

<?php
require __DIR__.'/vendor/autoload.php';

\Amp\Loop::run(function(){
	$process = new \Amp\Process\Process("php test-process.php");
	$pid = yield $process->start();
	\Amp\ByteStream\pipe($process->getStdout(), \Amp\ByteStream\getStdout());
	\Amp\ByteStream\pipe($process->getStderr(), \Amp\ByteStream\getStderr());
	$onInterrupt = \Amp\Loop::onSignal(SIGINT, function ($reference) use ($process){
		echo "sigterm\n";
		yield new \Amp\Delayed(5000);
		echo "terminating child\n";
		$process->signal(SIGINT);
		\Amp\Loop::cancel($reference);
	});
	$code = yield $process->join();
	echo $code;
	if(file_exists("/proc/{$pid}")) {
		echo "Process still running\n";
	}
	yield new \Amp\Delayed(10000);
	\Amp\Loop::cancel($onInterrupt);
});

As you can see:

  1. i started test-process.php
  2. wait for sigint
  3. if sigint received then wait 5s and send it child

As i can see sigint briadcasted on step 2 but i can't send it manually on step 3. May be need to use posix_kill instead of proc_terminate ?

@Ekstazi
Copy link
Contributor Author

Ekstazi commented Feb 26, 2019

https://stackoverflow.com/questions/34926587/signal-being-forwarded-to-children-for-the-symfony-process-component

As i understand i cannot prevent SIGINT sent to process group. But problem with sending signal to children process from code still exist

@trowski
Copy link
Member

trowski commented Feb 26, 2019

Fix sending signals to the child process in d51bd9a using posix_kill instead of proc_terminate as you suggested, since the latter was sending the signal to the wrapper child instead of the target child. Obviously the Process::signal() method is not commonly used since this wasn't caught sooner. Generally child signal handlers catch signals from the console which are forwarded to all children. As you discovered, there is no way to change that behavior unless you change the group ID of the child process.

@Ekstazi
Copy link
Contributor Author

Ekstazi commented Feb 26, 2019

Thx. I use Process::signal in my code.

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

No branches or pull requests

2 participants