Use "pcntl_fork" to fork processes
This commit is contained in:
parent
b10cd8772c
commit
9a6141dcbe
5 changed files with 112 additions and 27 deletions
|
@ -29,6 +29,7 @@ if (php_sapi_name() !== 'cli') {
|
|||
}
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBA;
|
||||
|
@ -65,6 +66,8 @@ if (DI::mode()->isInstall()) {
|
|||
die("Friendica isn't properly installed yet.\n");
|
||||
}
|
||||
|
||||
DI::mode()->setExecutor(Mode::DAEMON);
|
||||
|
||||
DI::config()->load();
|
||||
|
||||
if (empty(DI::config()->get('system', 'pidfile'))) {
|
||||
|
@ -144,34 +147,35 @@ Logger::notice('Starting worker daemon.', ["pid" => $pid]);
|
|||
if (!$foreground) {
|
||||
echo "Starting worker daemon.\n";
|
||||
|
||||
// Switch over to daemon mode.
|
||||
if ($pid = pcntl_fork()) {
|
||||
return; // Parent
|
||||
}
|
||||
|
||||
fclose(STDIN); // Close all of the standard
|
||||
|
||||
// Enabling this seem to block a running php process with 100% CPU usage when there is an outpout
|
||||
// fclose(STDOUT); // file descriptors as we
|
||||
// fclose(STDERR); // are running as a daemon.
|
||||
|
||||
DBA::disconnect();
|
||||
|
||||
// Fork a daemon process
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
echo "Daemon couldn't be forked.\n";
|
||||
Logger::warning('Could not fork daemon');
|
||||
exit(1);
|
||||
} elseif ($pid) {
|
||||
// The parent process continues here
|
||||
echo 'Child process started with pid ' . $pid . ".\n";
|
||||
Logger::notice('Child process started', ['pid' => $pid]);
|
||||
file_put_contents($pidfile, $pid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// We now are in the child process
|
||||
register_shutdown_function('shutdown');
|
||||
|
||||
// Make the child the main process, detach it from the terminal
|
||||
if (posix_setsid() < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($pid = pcntl_fork()) {
|
||||
return; // Parent
|
||||
}
|
||||
// Closing all existing connections with the outside
|
||||
fclose(STDIN);
|
||||
|
||||
$pid = getmypid();
|
||||
file_put_contents($pidfile, $pid);
|
||||
|
||||
// We lose the database connection upon forking
|
||||
DBA::reconnect();
|
||||
// And now connect the database again
|
||||
DBA::connect();
|
||||
}
|
||||
|
||||
DI::config()->set('system', 'worker_daemon_mode', true);
|
||||
|
|
|
@ -28,7 +28,7 @@ if (php_sapi_name() !== 'cli') {
|
|||
|
||||
use Dice\Dice;
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Process;
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Update;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\DI;
|
||||
|
@ -59,6 +59,8 @@ $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['worker']])
|
|||
DI::init($dice);
|
||||
$a = DI::app();
|
||||
|
||||
DI::mode()->setExecutor(Mode::WORKER);
|
||||
|
||||
// Check the database structure and possibly fixes it
|
||||
Update::check($a->getBasePath(), true, DI::mode());
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@ $dice = $dice->addRule(Friendica\App\Mode::class, ['call' => [['determineRunMode
|
|||
|
||||
$a = \Friendica\DI::app();
|
||||
|
||||
\Friendica\DI::mode()->setExecutor(\Friendica\App\Mode::INDEX);
|
||||
|
||||
$a->runFrontend(
|
||||
$dice->create(\Friendica\App\Module::class),
|
||||
$dice->create(\Friendica\App\Router::class),
|
||||
|
|
|
@ -38,6 +38,11 @@ class Mode
|
|||
const DBCONFIGAVAILABLE = 4;
|
||||
const MAINTENANCEDISABLED = 8;
|
||||
|
||||
const UNDEFINED = 0;
|
||||
const INDEX = 1;
|
||||
const DAEMON = 2;
|
||||
const WORKER = 3;
|
||||
|
||||
const BACKEND_CONTENT_TYPES = ['application/jrd+json', 'text/xml',
|
||||
'application/rss+xml', 'application/atom+xml', 'application/activity+json'];
|
||||
|
||||
|
@ -47,6 +52,12 @@ class Mode
|
|||
*/
|
||||
private $mode;
|
||||
|
||||
/***
|
||||
* @var int Who executes this Application
|
||||
*
|
||||
*/
|
||||
private $executor = self::UNDEFINED;
|
||||
|
||||
/**
|
||||
* @var bool True, if the call is a backend call
|
||||
*/
|
||||
|
@ -163,6 +174,31 @@ class Mode
|
|||
return ($this->mode & $mode) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the execution mode
|
||||
*
|
||||
* @param integer $executor Execution Mode
|
||||
* @return void
|
||||
*/
|
||||
public function setExecutor(int $executor)
|
||||
{
|
||||
$this->executor = $executor;
|
||||
|
||||
// Daemon and worker are always backend
|
||||
if (in_array($executor, [self::DAEMON, self::WORKER])) {
|
||||
$this->isBackend = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*isBackend = true;*
|
||||
* get the execution mode
|
||||
*
|
||||
* @return int Execution Mode
|
||||
*/
|
||||
public function getExecutor()
|
||||
{
|
||||
return $this->executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install mode is when the local config file is missing or the DB schema hasn't been installed yet.
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
namespace Friendica\Core;
|
||||
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
|
@ -1180,6 +1181,44 @@ class Worker
|
|||
self::killStaleWorkers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork a child process
|
||||
*
|
||||
* @param boolean $do_cron
|
||||
* @return void
|
||||
*/
|
||||
private static function forkProcess(bool $do_cron)
|
||||
{
|
||||
// Children inherit their parent's database connection.
|
||||
// To avoid problems we disconnect and connect both parent and child
|
||||
DBA::disconnect();
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
DBA::connect();
|
||||
Logger::warning('Could not spawn worker');
|
||||
return;
|
||||
} elseif ($pid) {
|
||||
// The parent process continues here
|
||||
DBA::connect();
|
||||
Logger::info('Spawned new worker', ['cron' => $do_cron, 'pid' => $pid]);
|
||||
return;
|
||||
}
|
||||
|
||||
// We now are in the new worker
|
||||
DBA::connect();
|
||||
Logger::info('Worker spawned', ['cron' => $do_cron, 'pid' => getmypid()]);
|
||||
|
||||
DI::process()->start();
|
||||
|
||||
self::processQueue($do_cron);
|
||||
|
||||
self::unclaimProcess();
|
||||
|
||||
DI::process()->end();
|
||||
Logger::info('Worker ended', ['cron' => $do_cron, 'pid' => getmypid()]);
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a new worker
|
||||
*
|
||||
|
@ -1189,13 +1228,15 @@ class Worker
|
|||
*/
|
||||
public static function spawnWorker($do_cron = false)
|
||||
{
|
||||
$command = 'bin/worker.php';
|
||||
|
||||
$args = ['no_cron' => !$do_cron];
|
||||
|
||||
$a = DI::app();
|
||||
$process = new Core\Process(DI::logger(), DI::mode(), DI::config(), DI::modelProcess(), $a->getBasePath(), getmypid());
|
||||
$process->run($command, $args);
|
||||
// Worker and daemon are started from the command line.
|
||||
// This means that this is executed by a PHP interpreter without runtime limitations
|
||||
if (in_array(DI::mode()->getExecutor(), [Mode::DAEMON, Mode::WORKER])) {
|
||||
self::forkProcess($do_cron);
|
||||
} else {
|
||||
$process = new Core\Process(DI::logger(), DI::mode(), DI::config(),
|
||||
DI::modelProcess(), DI::app()->getBasePath(), getmypid());
|
||||
$process->run('bin/worker.php', ['no_cron' => !$do_cron]);
|
||||
}
|
||||
|
||||
// after spawning we have to remove the flag.
|
||||
if (DI::config()->get('system', 'worker_daemon_mode', false)) {
|
||||
|
|
Loading…
Reference in a new issue