}
use Dice\Dice;
+use Friendica\App\Mode;
use Friendica\Core\Logger;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
die("Friendica isn't properly installed yet.\n");
}
+DI::mode()->setExecutor(Mode::DAEMON);
+
DI::config()->load();
if (empty(DI::config()->get('system', 'pidfile'))) {
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);
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;
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());
$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),
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'];
*/
private $mode;
+ /***
+ * @var int Who executes this Application
+ *
+ */
+ private $executor = self::UNDEFINED;
+
/**
* @var bool True, if the call is a backend call
*/
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.
namespace Friendica\Core;
+use Friendica\App\Mode;
use Friendica\Core;
use Friendica\Database\DBA;
use Friendica\DI;
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
*
*/
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)) {