+ return($workers[0]["processes"]);
+}
+
+/**
+ * @brief Check if we should pass some slow processes
+ *
+ * When the active processes of the highest priority are using more than 2/3
+ * of all processes, we let pass slower processes.
+ *
+ * @param string $highest_priority Returns the currently highest priority
+ * @return bool We let pass a slower process than $highest_priority
+ */
+function poller_passing_slow(&$highest_priority) {
+
+ $highest_priority = 0;
+
+ $r = q("SELECT `priority`
+ FROM `process`
+ INNER JOIN `workerqueue` ON `workerqueue`.`pid` = `process`.`pid`");
+
+ // No active processes at all? Fine
+ if (!dbm::is_result($r))
+ return(false);
+
+ $priorities = array();
+ foreach ($r AS $line)
+ $priorities[] = $line["priority"];
+
+ // Should not happen
+ if (count($priorities) == 0)
+ return(false);
+
+ $highest_priority = min($priorities);
+
+ // The highest process is already the slowest one?
+ // Then we quit
+ if ($highest_priority == PRIORITY_NEGLIGIBLE)
+ return(false);
+
+ $high = 0;
+ foreach ($priorities AS $priority)
+ if ($priority == $highest_priority)
+ ++$high;
+
+ logger("Highest priority: ".$highest_priority." Total processes: ".count($priorities)." Count high priority processes: ".$high, LOGGER_DEBUG);
+ $passing_slow = (($high/count($priorities)) > (2/3));
+
+ if ($passing_slow)
+ logger("Passing slower processes than priority ".$highest_priority, LOGGER_DEBUG);
+
+ return($passing_slow);
+}
+
+/**
+ * @brief Returns the next worker process
+ *
+ * @return string SQL statement
+ */
+function poller_worker_process() {
+
+ q("START TRANSACTION;");
+
+ // Check if we should pass some low priority process
+ $highest_priority = 0;
+
+ if (poller_passing_slow($highest_priority)) {
+ // Are there waiting processes with a higher priority than the currently highest?
+ $r = q("SELECT * FROM `workerqueue`
+ WHERE `executed` = '0000-00-00 00:00:00' AND `priority` < %d
+ ORDER BY `priority`, `created` LIMIT 1", dbesc($highest_priority));
+ if (dbm::is_result($r))
+ return $r;
+
+ // Give slower processes some processing time
+ $r = q("SELECT * FROM `workerqueue`
+ WHERE `executed` = '0000-00-00 00:00:00' AND `priority` > %d
+ ORDER BY `priority`, `created` LIMIT 1", dbesc($highest_priority));
+ }
+
+ // If there is no result (or we shouldn't pass lower processes) we check without priority limit
+ if (($highest_priority == 0) OR !dbm::is_result($r))
+ $r = q("SELECT * FROM `workerqueue` WHERE `executed` = '0000-00-00 00:00:00' ORDER BY `priority`, `created` LIMIT 1");
+
+ return $r;
+}
+
+/**
+ * @brief Call the front end worker
+ */
+function call_worker() {
+ if (!Config::get("system", "frontend_worker") OR !Config::get("system", "worker")) {
+ return;
+ }
+
+ $url = get_app()->get_baseurl()."/worker";
+ fetch_url($url, false, $redirects, 1);
+}
+
+/**
+ * @brief Call the front end worker if there aren't any active
+ */
+function call_worker_if_idle() {
+ if (!Config::get("system", "frontend_worker") OR !Config::get("system", "worker")) {
+ return;
+ }
+
+ // Do we have "proc_open"? Then we can fork the poller
+ if (function_exists("proc_open")) {
+ // When was the last time that we called the worker?
+ // Less than one minute? Then we quit
+ if ((time() - get_config("system", "worker_started")) < 60) {
+ return;
+ }
+
+ set_config("system", "worker_started", time());
+
+ // Do we have enough running workers? Then we quit here.
+ if (poller_too_much_workers()) {
+ // Cleaning dead processes
+ poller_kill_stale_workers();
+ get_app()->remove_inactive_processes();
+
+ return;
+ }
+
+ poller_run_cron();
+
+ logger('Call poller', LOGGER_DEBUG);
+
+ $args = array("php", "include/poller.php", "no_cron");
+ $a = get_app();
+ $a->proc_run($args);
+ return;
+ }
+
+ // We cannot execute background processes.
+ // We now run the processes from the frontend.
+ // This won't work with long running processes.
+ poller_run_cron();
+
+ clear_worker_processes();
+
+ $workers = q("SELECT COUNT(*) AS `processes` FROM `process` WHERE `command` = 'worker.php'");
+
+ if ($workers[0]["processes"] == 0) {
+ call_worker();
+ }
+}
+
+/**
+ * @brief Removes long running worker processes
+ */
+function clear_worker_processes() {
+ $timeout = Config::get("system", "frontend_worker_timeout", 10);
+
+ /// @todo We should clean up the corresponding workerqueue entries as well
+ q("DELETE FROM `process` WHERE `created` < '%s' AND `command` = 'worker.php'",
+ dbesc(datetime_convert('UTC','UTC',"now - ".$timeout." minutes")));
+}
+
+/**
+ * @brief Runs the cron processes
+ */
+function poller_run_cron() {
+ logger('Add cron entries', LOGGER_DEBUG);
+
+ // Run the cron job that calls all other jobs
+ proc_run(PRIORITY_MEDIUM, "include/cron.php");
+
+ // Run the cronhooks job separately from cron for being able to use a different timing
+ proc_run(PRIORITY_MEDIUM, "include/cronhooks.php");
+
+ // Cleaning dead processes
+ poller_kill_stale_workers();