*/
private static function semaphoreKey($key)
{
- $file = self::keyToFile($key);
+ $success = true;
- if (!file_exists($file)) {
- file_put_contents($file, $key);
- }
+ $temp = get_temppath();
- return ftok($file, 'f');
- }
+ $file = $temp . '/' . $key . '.sem';
- /**
- * Returns the full path to the semaphore file
- *
- * @param string $key The key of the semaphore
- *
- * @return string The full path
- */
- private static function keyToFile($key)
- {
- $temp = get_temppath();
+ if (!file_exists($file)) {
+ $success = !empty(file_put_contents($file, $key));
+ }
- return $temp . '/' . $key . '.sem';
+ return $success ? ftok($file, 'f') : false;
}
/**
/**
* (@inheritdoc)
+ *
+ * @param bool $override not necessary parameter for semaphore locks since the lock lives as long as the execution
+ * of the using function
*/
public function releaseLock($key, $override = false)
{
if (!empty(self::$semaphore[$key])) {
try {
$success = @sem_release(self::$semaphore[$key]);
- if (file_exists(self::keyToFile($key)) && $success) {
- $success = unlink(self::keyToFile($key));
- }
unset(self::$semaphore[$key]);
$this->markRelease($key);
} catch (\Exception $exception) {
$success = false;
}
- } else if ($override) {
- if ($this->acquireLock($key)) {
- $success = $this->releaseLock($key, true);
- }
}
return $success;
*/
public function getLocks(string $prefix = '')
{
- $temp = get_temppath();
- $locks = [];
- foreach (glob(sprintf('%s/%s*.sem', $temp, $prefix)) as $lock) {
- $lock = pathinfo($lock, PATHINFO_FILENAME);
- if(sem_get(self::semaphoreKey($lock))) {
- $locks[] = $lock;
+ // We can just return our own semaphore keys, since we don't know
+ // the state of other semaphores, even if the .sem files exists
+ $keys = array_keys(self::$semaphore);
+
+ if (empty($prefix)) {
+ return $keys;
+ } else {
+ $result = [];
+
+ foreach ($keys as $key) {
+ if (strpos($key, $prefix) === 0) {
+ array_push($result, $key);
+ }
}
- }
- return $locks;
+ return $result;
+ }
}
/**
*/
public function releaseAll($override = false)
{
- $success = parent::releaseAll($override);
-
- $temp = get_temppath();
- foreach (glob(sprintf('%s/*.sem', $temp)) as $lock) {
- $lock = pathinfo($lock, PATHINFO_FILENAME);
- if (!$this->releaseLock($lock, true)) {
- $success = false;
- }
- }
-
- return $success;
+ // Semaphores are just alive during a run, so there is no need to release
+ // You can just release your own locks
+ return parent::releaseAll($override);
}
}
$configMock
->shouldReceive('get')
->with('system', 'temppath', NULL, false)
- ->andReturn('/tmp/');
+ ->andReturn('/tmp');
$dice->shouldReceive('create')->with(Configuration::class)->andReturn($configMock);
// @todo Because "get_temppath()" is using static methods, we have to initialize the BaseObject
// Semaphore doesn't work with TTL
return true;
}
+
+ /**
+ * Test if semaphore locking works even for
+ */
+ public function testMissingFileNotOverriding()
+ {
+ $file = get_temppath() . '/test.sem';
+
+ $this->assertTrue(file_exists($file));
+ $this->assertFalse($this->instance->releaseLock('test', false));
+ $this->assertTrue(file_exists($file));
+ }
+
+ /**
+ * Test overriding semaphore release with already set semaphore
+ * This test proves that semaphore locks cannot get released by other instances except themselves
+ *
+ * Check for Bug https://github.com/friendica/friendica/issues/7298#issuecomment-521996540
+ * @see https://github.com/friendica/friendica/issues/7298#issuecomment-521996540
+ */
+ public function testMissingFileOverriding()
+ {
+ $file = get_temppath() . '/test.sem';
+
+ $this->assertTrue(file_exists($file));
+ $this->assertFalse($this->instance->releaseLock('test', true));
+ $this->assertTrue(file_exists($file));
+ }
}