]> git.mxchange.org Git - friendica.git/commitdiff
Use "pcntl_fork" to fork processes
authorMichael <heluecht@pirati.ca>
Fri, 1 Jan 2021 19:35:29 +0000 (19:35 +0000)
committerMichael <heluecht@pirati.ca>
Fri, 1 Jan 2021 19:35:29 +0000 (19:35 +0000)
bin/daemon.php
bin/worker.php
index.php
src/App/Mode.php
src/Core/Worker.php

index d08aa37d15906fab61ceaf63a67707f26b0d0c24..ec507305cc826d621ba73cb5972082cab728595d 100755 (executable)
@@ -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
-       }
-
-       $pid = getmypid();
-       file_put_contents($pidfile, $pid);
+       // Closing all existing connections with the outside
+       fclose(STDIN);
 
-       // 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);
index 5698cf16ddfaca79eb69b0e1aa04d3efa515435c..52400a045a4e4b9d57051edbed9c6289ece7e412 100755 (executable)
@@ -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());
 
index fdb15fdd884cbe4a0c9080374c571fe40df7e39e..baa6818b094cfd051a6f894303a67260ed5e499a 100644 (file)
--- a/index.php
+++ b/index.php
@@ -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),
index e19de8f07f4feaba6555f4f6b9394ee37ef9b7f8..8aa812c93de35555bc28f63aa69e5d5e56c7c551 100644 (file)
@@ -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.
index 2f39a82fe5073781ec15f5766f6ef668fa450fb0..e90747e41a1a9bd04bb2b98f5648ccc962565605 100644 (file)
@@ -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)) {