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

Use re-entrant lock. #1014

Merged
merged 6 commits into from
Sep 17, 2024
Merged

Use re-entrant lock. #1014

merged 6 commits into from
Sep 17, 2024

Conversation

btimby
Copy link
Contributor

@btimby btimby commented Mar 5, 2024

While using django-prometheus I found a situation where adding a metric to the registry caused a read from cache. I had cache metrics enabled, which attempted to add cache metrics to the registry. This resulting in a deadlock as the second registry call attempted to obtain the lock already held lower on the call stack.

I changed usage to an RLock, which allows the lock to be obtained in a nested fashion within a call stack. This still provides thread safety.

@@ -1,4 +1,5 @@
import math
from threading import _PyRLock # type: ignore[attr-defined]
Copy link

Choose a reason for hiding this comment

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

why not from threading import RLock?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

RLock does not have a locked() method.

https://docs.python.org/3/library/threading.html#rlock-objects

However, _PyRLock provides _count which I am using to provide that method to satisfy the rest of the code's usage of the lock.

Copy link
Contributor Author

@btimby btimby Jun 25, 2024

Choose a reason for hiding this comment

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

Look closely at threading, RLock is a factory function that either returns an instance of _CRLock or _PyRLock. The former does not have a _count attribute though the latter does. I am choosing _PyRLock explicitly in order to implement locked().

Copy link

Choose a reason for hiding this comment

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

nice! Sorry I missed that!

Copy link
Contributor Author

@btimby btimby Jun 25, 2024

Choose a reason for hiding this comment

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

No problem. I had to go re-read the threading module source to jog my memory 😄

Copy link
Member

Choose a reason for hiding this comment

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

Unless I am missing something only tests depend on locked(). Could we instead use RLock in the code, and change the test to check the lock in a different manner? E.g. trying to acquire the lock with blocked=false and testing if we acquired the lock or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Or maybe more easily just derive the class from RLock and implement locked() method using blocked=False as you suggest.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, nevermind, as I mentioned above, RLock is a factory function (can't derive). Could instead have a locked(lock) -> bool function used in tests.

Copy link
Member

Choose a reason for hiding this comment

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

That seems reasonable to me, it's only one place in tests so we don't need anything too fancy.

Copy link
Member

@csmarchbanks csmarchbanks left a comment

Choose a reason for hiding this comment

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

I have concerns with using _PyRLock as it appears that both behavior may change between cpython and python, and at least some people appear to want to deprecate or remove the function in Python 3.13: https://discuss.python.org/t/should-we-deprecate-threading-pyrlock-in-3-13/27297.

@@ -1,4 +1,5 @@
import math
from threading import _PyRLock # type: ignore[attr-defined]
Copy link
Member

Choose a reason for hiding this comment

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

Unless I am missing something only tests depend on locked(). Could we instead use RLock in the code, and change the test to check the lock in a different manner? E.g. trying to acquire the lock with blocked=false and testing if we acquired the lock or not.

@btimby
Copy link
Contributor Author

btimby commented Aug 2, 2024

@csmarchbanks Only one test needed to check the status of the lock. Therefore, I simply switched all locks to RLock and added a utility function (in the test file) for that test.

Copy link
Member

@csmarchbanks csmarchbanks left a comment

Choose a reason for hiding this comment

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

The new implementation looks good to me! However the diffs show changes from other recently merged PRs. Any chance you could clean those up with a rebase?

Signed-off-by: Ben Timby <btimby@gmail.com>
Signed-off-by: Ben Timby <btimby@gmail.com>
Signed-off-by: Ben Timby <btimby@gmail.com>
Signed-off-by: Ben Timby <btimby@gmail.com>
Signed-off-by: Ben Timby <btimby@gmail.com>
Signed-off-by: Ben Timby <btimby@gmail.com>
Copy link
Member

@csmarchbanks csmarchbanks left a comment

Choose a reason for hiding this comment

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

I force pushed your branch to remove the extraneous commits/diffs so this is in a mergeable state now. No other changes were made.

Thanks!

@csmarchbanks csmarchbanks merged commit 0014e97 into prometheus:master Sep 17, 2024
11 checks passed
@btimby
Copy link
Contributor Author

btimby commented Sep 17, 2024

@csmarchbanks Thanks! I was not sure what to do and have not had a chance to research it.

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

Successfully merging this pull request may close these issues.

3 participants