diff --git a/include/poller.php b/include/poller.php index 22fb65a4f..60937fb7e 100644 --- a/include/poller.php +++ b/include/poller.php @@ -2,6 +2,7 @@ use Friendica\App; use Friendica\Core\Config; +use Friendica\Util\Lock; if (!file_exists("boot.php") AND (sizeof($_SERVER["argv"]) != 0)) { $directory = dirname($_SERVER["argv"][0]); @@ -19,16 +20,12 @@ require_once("boot.php"); function poller_run($argv, $argc){ global $a, $db; - if (is_null($a)) { - $a = new App(dirname(__DIR__)); - } + $a = new App(dirname(__DIR__)); - if(is_null($db)) { - @include(".htconfig.php"); - require_once("include/dba.php"); - $db = new dba($db_host, $db_user, $db_pass, $db_data); - unset($db_host, $db_user, $db_pass, $db_data); - }; + @include(".htconfig.php"); + require_once("include/dba.php"); + $db = new dba($db_host, $db_user, $db_pass, $db_data); + unset($db_host, $db_user, $db_pass, $db_data); Config::load(); @@ -41,6 +38,10 @@ function poller_run($argv, $argc){ load_hooks(); + if (($argc <= 1) OR ($argv[1] != "no_cron")) { + poller_run_cron(); + } + $a->start_process(); if ($a->min_memory_reached()) { @@ -55,11 +56,11 @@ function poller_run($argv, $argc){ return; } - if(($argc <= 1) OR ($argv[1] != "no_cron")) { - poller_run_cron(); + if ($a->max_processes_reached()) { + return; } - if ($a->max_processes_reached()) { + if (!Lock::set('poller_worker')) { return; } @@ -69,6 +70,8 @@ function poller_run($argv, $argc){ return; } + Lock::remove('poller_worker'); + $starttime = time(); while ($r = poller_worker_process()) { @@ -83,12 +86,18 @@ function poller_run($argv, $argc){ return; } + if (!Lock::set('poller_worker')) { + return; + } + // Count active workers and compare them with a maximum value that depends on the load if (poller_too_much_workers()) { logger('Active worker limit reached, quitting.', LOGGER_DEBUG); return; } + Lock::remove('poller_worker'); + if (!poller_execute($r[0])) { logger('Process execution failed, quitting.', LOGGER_DEBUG); return; @@ -724,6 +733,8 @@ function poller_run_cron() { if (array_search(__file__,get_included_files())===0){ poller_run($_SERVER["argv"],$_SERVER["argc"]); + Lock::removeAll(); + poller_unclaim_process(); get_app()->end_process(); diff --git a/src/Util/Lock.php b/src/Util/Lock.php index 1a33e819f..175ad34e3 100644 --- a/src/Util/Lock.php +++ b/src/Util/Lock.php @@ -8,6 +8,8 @@ namespace Friendica\Util; * */ +use Friendica\Core\Config; +use Memcache; use dba; use dbm; @@ -15,6 +17,31 @@ use dbm; * @brief This class contain Functions for preventing parallel execution of functions */ class Lock { + /** + * @brief Check for memcache and open a connection if configured + * + * @return object|boolean The memcache object - or "false" if not successful + */ + public static function memcache() { + if (!function_exists('memcache_connect')) { + return false; + } + + if (!Config::get('system', 'memcache')) { + return false; + } + + $memcache_host = Config::get('system', 'memcache_host', '127.0.0.1'); + $memcache_port = Config::get('system', 'memcache_port', 11211); + + $memcache = new Memcache; + + if (!$memcache->connect($memcache_host, $memcache_port)) { + return false; + } + + return $memcache; + } /** * @brief Sets a lock for a given name @@ -33,6 +60,33 @@ class Lock { $got_lock = false; $start = time(); + $memcache = self::memcache(); + if (is_object($memcache)) { + $cachekey = get_app()->get_hostname().";lock:".$fn_name; + + do { + $lock = $memcache->get($cachekey); + + if (!is_bool($lock)) { + $pid = (int)$lock; + + // When the process id isn't used anymore, we can safely claim the lock for us. + // Or we do want to lock something that was already locked by us. + if (!posix_kill($pid, 0) OR ($pid == getmypid())) { + $lock = false; + } + } + if (is_bool($lock)) { + $memcache->set($cachekey, getmypid(), MEMCACHE_COMPRESSED, 300); + $got_lock = true; + } + if (!$got_lock) { + sleep($wait_sec); + } + } while (!$got_lock AND ((time() - $start) < $timeout)); + + return $got_lock; + } do { dba::lock('locks'); $lock = dba::select('locks', array('locked', 'pid'), array('name' => $fn_name), array('limit' => 1)); @@ -73,7 +127,34 @@ class Lock { * @param string $fn_name Name of the lock */ public static function remove($fn_name) { + $memcache = self::memcache(); + if (is_object($memcache)) { + $cachekey = get_app()->get_hostname().";lock:".$fn_name; + $lock = $memcache->get($cachekey); + + if (!is_bool($lock)) { + if ((int)$lock == getmypid()) { + $memcache->delete($cachekey); + } + } + return; + } + dba::update('locks', array('locked' => false, 'pid' => 0), array('name' => $fn_name, 'pid' => getmypid())); return; } + + /** + * @brief Removes all lock that were set by us + */ + public static function removeAll() { + $memcache = self::memcache(); + if (is_object($memcache)) { + // We cannot delete all cache entries, but this doesn't matter with memcache + return; + } + + dba::update('locks', array('locked' => false, 'pid' => 0), array('pid' => getmypid())); + return; + } }