]> git.mxchange.org Git - friendica.git/blobdiff - src/Util/Lock.php
Merge pull request #5294 from annando/use-gravity
[friendica.git] / src / Util / Lock.php
index 7cc3472e69049a3bcdd9e750b29014aca2c6f9d9..eba264ad932d6ba6357c5fd566cafbc8c7ff8a30 100644 (file)
 <?php
-
+/**
+ * @file src/Util/Lock.php
+ */
 namespace Friendica\Util;
 
 /**
  * @file src/Util/Lock.php
  * @brief Functions for preventing parallel execution of functions
- *
  */
 
 use Friendica\Core\Config;
+use Friendica\Database\DBM;
 use Memcache;
 use dba;
-use dbm;
+
+require_once 'include/dba.php';
 
 /**
  * @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;
-                }
+class Lock
+{
+       private static $semaphore = [];
 
-                if (!Config::get('system', 'memcache')) {
-                        return false;
-                }
+       /**
+        * @brief Check for memcache and open a connection if configured
+        *
+        * @return object|boolean The memcache object - or "false" if not successful
+        */
+       private static function connectMemcache()
+       {
+               if (!function_exists('memcache_connect')) {
+                       return false;
+               }
 
-                $memcache_host = Config::get('system', 'memcache_host', '127.0.0.1');
-                $memcache_port = Config::get('system', 'memcache_port', 11211);
+               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;
+               $memcache = new Memcache;
 
-                if (!$memcache->connect($memcache_host, $memcache_port)) {
-                        return false;
-                }
+               if (!$memcache->connect($memcache_host, $memcache_port)) {
+                       return false;
+               }
 
-                return $memcache;
-        }
+               return $memcache;
+       }
 
        /**
-        * @brief Sets a lock for a given name
+        * @brief Creates a semaphore key
         *
         * @param string $fn_name Name of the lock
+        *
+        * @return ressource the semaphore key
+        */
+       private static function semaphoreKey($fn_name)
+       {
+               $temp = get_temppath();
+
+               $file = $temp.'/'.$fn_name.'.sem';
+
+               if (!file_exists($file)) {
+                       file_put_contents($file, $fn_name);
+               }
+
+               return ftok($file, 'f');
+       }
+
+       /**
+        * @brief Sets a lock for a given name
+        *
+        * @param string  $fn_name Name of the lock
         * @param integer $timeout Seconds until we give up
         *
         * @return boolean Was the lock successful?
         */
-       public static function set($fn_name, $timeout = 120) {
+       public static function set($fn_name, $timeout = 120)
+       {
                $got_lock = false;
                $start = time();
 
-               $memcache = self::memcache();
+               // The second parameter for "sem_acquire" doesn't exist before 5.6.1
+               if (function_exists('sem_get') && version_compare(PHP_VERSION, '5.6.1', '>=')) {
+                       self::$semaphore[$fn_name] = sem_get(self::semaphoreKey($fn_name));
+                       if (self::$semaphore[$fn_name]) {
+                               return sem_acquire(self::$semaphore[$fn_name], ($timeout == 0));
+                       }
+               }
+
+               $memcache = self::connectMemcache();
                if (is_object($memcache)) {
-                       $wait_sec = 0.2;
                        $cachekey = get_app()->get_hostname().";lock:".$fn_name;
 
                        do {
+                               // We only lock to be sure that nothing happens at exactly the same time
+                               dba::lock('locks');
                                $lock = $memcache->get($cachekey);
 
                                if (!is_bool($lock)) {
@@ -68,7 +105,7 @@ class 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())) {
+                                       if (!posix_kill($pid, 0) || ($pid == getmypid())) {
                                                $lock = false;
                                        }
                                }
@@ -76,21 +113,22 @@ class Lock {
                                        $memcache->set($cachekey, getmypid(), MEMCACHE_COMPRESSED, 300);
                                        $got_lock = true;
                                }
-                               if (!$got_lock) {
-                                       usleep($wait_sec * 1000000);
+
+                               dba::unlock();
+
+                               if (!$got_lock && ($timeout > 0)) {
+                                       usleep(rand(10000, 200000));
                                }
-                       } while (!$got_lock AND ((time(true) - $start) < $timeout));
+                       } while (!$got_lock && ((time() - $start) < $timeout));
 
                        return $got_lock;
                }
 
-               $wait_sec = 2;
-
                do {
                        dba::lock('locks');
-                       $lock = dba::select('locks', array('locked', 'pid'), array('name' => $fn_name), array('limit' => 1));
+                       $lock = dba::selectFirst('locks', ['locked', 'pid'], ['name' => $fn_name]);
 
-                       if (dbm::is_result($lock)) {
+                       if (DBM::is_result($lock)) {
                                if ($lock['locked']) {
                                        // When the process id isn't used anymore, we can safely claim the lock for us.
                                        if (!posix_kill($lock['pid'], 0)) {
@@ -102,20 +140,20 @@ class Lock {
                                        }
                                }
                                if (!$lock['locked']) {
-                                       dba::update('locks', array('locked' => true, 'pid' => getmypid()), array('name' => $fn_name));
+                                       dba::update('locks', ['locked' => true, 'pid' => getmypid()], ['name' => $fn_name]);
                                        $got_lock = true;
                                }
-                       } elseif (!dbm::is_result($lock)) {
-                               dba::insert('locks', array('name' => $fn_name, 'locked' => true, 'pid' => getmypid()));
+                       } elseif (!DBM::is_result($lock)) {
+                               dba::insert('locks', ['name' => $fn_name, 'locked' => true, 'pid' => getmypid()]);
                                $got_lock = true;
                        }
 
                        dba::unlock();
 
-                       if (!$got_lock) {
-                               sleep($wait_sec);
+                       if (!$got_lock && ($timeout > 0)) {
+                               usleep(rand(100000, 2000000));
                        }
-               } while (!$got_lock AND ((time() - $start) < $timeout));
+               } while (!$got_lock && ((time() - $start) < $timeout));
 
                return $got_lock;
        }
@@ -124,9 +162,21 @@ class Lock {
         * @brief Removes a lock if it was set by us
         *
         * @param string $fn_name Name of the lock
+        * @return mixed
         */
-       public static function remove($fn_name) {
-               $memcache = self::memcache();
+       public static function remove($fn_name)
+       {
+               if (function_exists('sem_get') && version_compare(PHP_VERSION, '5.6.1', '>=')) {
+                       if (empty(self::$semaphore[$fn_name])) {
+                               return false;
+                       } else {
+                               $success = @sem_release(self::$semaphore[$fn_name]);
+                               unset(self::$semaphore[$fn_name]);
+                               return $success;
+                       }
+               }
+
+               $memcache = self::connectMemcache();
                if (is_object($memcache)) {
                        $cachekey = get_app()->get_hostname().";lock:".$fn_name;
                        $lock = $memcache->get($cachekey);
@@ -139,21 +189,23 @@ class Lock {
                        return;
                }
 
-               dba::update('locks', array('locked' => false, 'pid' => 0), array('name' => $fn_name, 'pid' => getmypid()));
+               dba::update('locks', ['locked' => false, 'pid' => 0], ['name' => $fn_name, 'pid' => getmypid()]);
                return;
        }
 
        /**
         * @brief Removes all lock that were set by us
+        * @return void
         */
-       public static function removeAll() {
-               $memcache = self::memcache();
+       public static function removeAll()
+       {
+               $memcache = self::connectMemcache();
                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()));
+               dba::update('locks', ['locked' => false, 'pid' => 0], ['pid' => getmypid()]);
                return;
        }
 }