X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FUtil%2FLock.php;h=eba264ad932d6ba6357c5fd566cafbc8c7ff8a30;hb=1e9bff88bc05fce154edee9f047e7d8922184140;hp=03e8545adfdaadc20866268ce7b8c1156648955c;hpb=32f61016d368401890443efc861fff0032ac6c42;p=friendica.git diff --git a/src/Util/Lock.php b/src/Util/Lock.php index 03e8545adf..eba264ad93 100644 --- a/src/Util/Lock.php +++ b/src/Util/Lock.php @@ -1,62 +1,211 @@ connect($memcache_host, $memcache_port)) { + return false; } + return $memcache; + } + + /** + * @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) + { $got_lock = false; $start = time(); - do { - dba:p("LOCK TABLE `locks` WRITE"); - $lock = dba::select('locks', array('locked'), array('name' => $fn_name), array('limit' => 1)); + // 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)); + } + } - if ((dbm::is_result($lock)) AND !$lock['locked']) { - dba::update('locks', array('locked' => true), array('name' => $fn_name)); - $got_lock = true; - } elseif (!dbm::is_result($lock)) { - dbm::insert('locks', array('name' => $fn_name, 'locked' => true)); + $memcache = self::connectMemcache(); + if (is_object($memcache)) { + $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)) { + $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) || ($pid == getmypid())) { + $lock = false; + } + } + if (is_bool($lock)) { + $memcache->set($cachekey, getmypid(), MEMCACHE_COMPRESSED, 300); + $got_lock = true; + } + + dba::unlock(); + + if (!$got_lock && ($timeout > 0)) { + usleep(rand(10000, 200000)); + } + } while (!$got_lock && ((time() - $start) < $timeout)); + + return $got_lock; + } + + do { + dba::lock('locks'); + $lock = dba::selectFirst('locks', ['locked', 'pid'], ['name' => $fn_name]); + + 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)) { + $lock['locked'] = false; + } + // We want to lock something that was already locked by us? So we got the lock. + if ($lock['pid'] == getmypid()) { + $got_lock = true; + } + } + if (!$lock['locked']) { + dba::update('locks', ['locked' => true, 'pid' => getmypid()], ['name' => $fn_name]); + $got_lock = true; + } + } elseif (!DBM::is_result($lock)) { + dba::insert('locks', ['name' => $fn_name, 'locked' => true, 'pid' => getmypid()]); $got_lock = true; } - dbm::p("UNLOCK TABLES"); + dba::unlock(); - if (!$got_lock) { - sleep($wait_sec); + if (!$got_lock && ($timeout > 0)) { + usleep(rand(100000, 2000000)); } - } while (!$got_lock AND ((time() - $start) < $timeout)); - - logger('lock_function: function ' . $fn_name . ' with blocking = ' . $block . ' got_lock = ' . $got_lock . ' time = ' . (time() - $start), LOGGER_DEBUG); + } while (!$got_lock && ((time() - $start) < $timeout)); return $got_lock; } - public static function remove($fn_name) { - dba::update('locks', array('locked' => false), array('name' => $fn_name)); + /** + * @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) + { + 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); + + if (!is_bool($lock)) { + if ((int)$lock == getmypid()) { + $memcache->delete($cachekey); + } + } + return; + } + + dba::update('locks', ['locked' => false, 'pid' => 0], ['name' => $fn_name, 'pid' => getmypid()]); + return; + } - logger('unlock_function: released lock for function ' . $fn_name, LOGGER_DEBUG); + /** + * @brief Removes all lock that were set by us + * @return void + */ + 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', ['locked' => false, 'pid' => 0], ['pid' => getmypid()]); return; } }