Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Lock] Remove released semaphore #27357

Merged
merged 1 commit into from
Jun 8, 2018

Conversation

jderusse
Copy link
Member

Q A
Branch? 3.4
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #27356
License MIT
Doc PR NA

This PR remove the semaphore with sem_remove. By removing without releasing the semaphore, all pending blocking acquiring will fail that's why the acquire method has also been update to handle such case

@stof
Copy link
Member

stof commented May 23, 2018

Can we have tests reproducing the issue ? AFAIK, all tests of the SemaphoreStore are green with the existing implementation, so having it non-working means that the testsuite is not covering things properly.

@stof
Copy link
Member

stof commented May 23, 2018

and you worked on an outdated version of Symfony master (or you targeted the wrong branch when opening the PR), as this conflicts.

@jderusse jderusse changed the base branch from master to 3.4 May 23, 2018 18:04
@jderusse jderusse force-pushed the fix-semaphore-leak branch 2 times, most recently from 2bdfb09 to 97026c7 Compare May 23, 2018 19:09
@jderusse
Copy link
Member Author

Added a test case and fix target branch

@nicolas-grekas nicolas-grekas added this to the 3.4 milestone May 23, 2018
@stof
Copy link
Member

stof commented May 24, 2018

I'd rather see a test about the behavior of the store (to prevent the bug beign reported) rather than a test enforcing this specific implementation (which does not prove that this implementation is the right one, but forbids replacing it)

@jderusse
Copy link
Member Author

@stof unless I missed a point, the bug reported is semaphores are not removed which fill the system device and end with an error "no space left on device". It's kinda tracking a memory leak or a un-removed files which fill a disk, that's why the bug exists whereas every tests were green.

The test asserts that amount of semaphores used on the OS does not increase when the lock is acquired then released, regardless of the implementation.
Moreover a second assertion validate the test itself by checking that the amount of semaphore has been increased when the lock was acquired, which proves that this is the right way to count the amount of semaphore used by the OS

An other way to test it would be to reproduce the reported bug. Meaning either

  • creating more than 32 000 semaphores (default value) which take ~ 2 minutes on my machine
  • reducing this limit by changing a kernel parameter in /etc/sysctl.conf (requires sudo and restart)

Do you see another way to test the behavior?

@stof
Copy link
Member

stof commented May 24, 2018

Is sem_release a useless API then ?

@jderusse
Copy link
Member Author

Wouldn't say so.

sem_release is the right way to release a lock, next pending sem_acquire will softly take the lock. BUT, it does not free the resource. If nobody want to acquire the lock, the resource will stay here (consuming 1 slot on the 32 000 available). AND because of cross concurrency, you can not be sure that nobody else use the semaphore, then you can not remove the resource safely...

sem_remove is the brute way to the release the resource. By removing it: every pending sem_acquire will fail.

If your application can control the amount of distinguished resource, you should sem_release. If not, the single workaround I found is to use the sem_remove and deal with the failure.

For the record, we have exactly the same issue with the original implementation of flock, the file is not removed and consume 1 inode, but with more than millions of inodes available, this bug has not yet been reported...

while ($blocking && !$acquired) {
$resource = sem_get($keyId);
$acquired = @sem_acquire($resource, !$blocking);
}
}

if (!$acquired) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering: can this happen at all now? do we need some sort of timeout in the while above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, if \PHP_VERSION_ID >= 50601 and !$blocking


while ($blocking && !$acquired) {
$resource = sem_get($keyId);
$acquired = @sem_acquire($resource, !$blocking);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the value of $blocking is known here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

$acquired = sem_acquire($resource, !$blocking);
$acquired = @sem_acquire($resource, !$blocking);

while ($blocking && !$acquired) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loop can be moved outside of the if/else

} else {
$acquired = sem_acquire($resource, !$blocking);
$acquired = @sem_acquire($resource, !$blocking);
Copy link
Member

@nicolas-grekas nicolas-grekas Jun 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By swapping the order of the if/else, we can simplify the condition: if (PHP>=...) elseif !$blovking throw

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@nicolas-grekas nicolas-grekas changed the title Remove released semaphore [Lock] Remove released semaphore Jun 8, 2018
@nicolas-grekas
Copy link
Member

Thank you @jderusse.

@nicolas-grekas nicolas-grekas merged commit 77b9f90 into symfony:3.4 Jun 8, 2018
nicolas-grekas added a commit that referenced this pull request Jun 8, 2018
This PR was merged into the 3.4 branch.

Discussion
----------

[Lock] Remove released semaphore

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #27356
| License       | MIT
| Doc PR        | NA

This PR remove the semaphore with `sem_remove`. By removing without releasing the semaphore, all pending blocking acquiring will fail that's why the acquire method has also been update to handle such case

Commits
-------

77b9f90 Remove released semaphore
This was referenced Jun 25, 2018
@fabpot fabpot mentioned this pull request Jun 25, 2018
@pdias
Copy link

pdias commented Nov 21, 2018

I do not know if there is anything to do with this topic, but in a console command I do this:

``
protected function execute(InputInterface $input, OutputInterface $output)
{
if (!$this->lock()) {
$output->writeln('The command is already running in another process.');
return 0;
}

    "...My task…"        

    $this->release();
}

``

And i have this warning:

PHP Fatal error: Uncaught Symfony\Component\Debug\Exception\ContextErrorException: Warning: sem_remove(): failed for SysV sempphore 139798696744064: Operation not permitted in /home/dev-my/vendor/symfony/symfony/src/Symfony/Component/Lock/Store/SemaphoreStore.php:113 Stack trace: #0 /home/dev-my/vendor/symfony/symfony/src/Symfony/Component/Lock/Lock.php(147): Symfony\Component\Lock\Store\SemaphoreStore->delete(Object(Symfony\Component\Lock\Key)) #1 /home/dev-my/vendor/symfony/symfony/src/Symfony/Component/Lock/Lock.php(63): Symfony\Component\Lock\Lock->release() #2 [internal function]: Symfony\Component\Lock\Lock->__destruct() #3 {main} thrown in /home/dev-my/vendor/symfony/symfony/src/Symfony/Component/Lock/Store/SemaphoreStore.php on line 113

I'm using SF3.4.

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants