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

Increased thread-safety by using Lock context manager syntax #192

Merged
merged 9 commits into from
Jan 3, 2024

Conversation

f3ndot
Copy link
Collaborator

@f3ndot f3ndot commented Dec 5, 2023

The current code base makes liberal use of Lock.acquire() and Lock.release() throughout, however that syntax can suffer from missing release calls in branched logic flows. Indeed, #125 was opened due to a missing release.

Thankfully Python's threading module supports the context manager protocol for Lock which bakes in a guaranteed release after the inner code runs or excepts at any time.

This PR refactors almost all existing acquired/released locks to use the context manager syntax instead, removing the risk of a missing release. A small amount of refactoring was required to deal with cases where some functions intentionally release their lock in the middle of their code to sleep and recursively call themselves.

One utility function was added to simplify where the main receive loop function paired a Lock's locking/releasing action to a corresponding socket's nonblocking/blocking state. This also guarantees both are "toggled" in all possible branched flows too.

This PR passes pytest --check-functionality with the test Docker image running, but I've not tried it in a project yet. I suggest @tayler6000 gives it a good once-over and testing in a real application context before considering it for merging. I'm most interested in testing the "sleep 5 seconds, try (de)registering again" logic to see if that's preserved.

f3ndot and others added 8 commits December 4, 2023 21:03
This is a safer and more readable way to prevent accidental missed release()
calls on logical branching of code/execution flow. Functions that may not be
readily converted to this format have been tagged as TODOs for potential future
investigation after refactoring.
Accomplished by refactoring released lock with sleep and recursive call out of
the middle of the function and instead raising an exception to the main calling
register() function. This should be functionally equivalent whilst allowing for
the context manager syntax, which is safer against missing release() calls.
Accomplished by refactoring released lock with sleep and recursive call out of
the middle of the function and instead raising an exception to the main calling
deregister() function. This should be functionally equivalent whilst allowing
for the context manager syntax, which is safer against missing release() calls.
This is just cleaner code without changing much. __(de)register() no longer
safely locks on its, but only their public equivalents call them anyway. We can
hoist the context manager wrapping there and prevent two functions from being
entirely indented by a `with ...:` statement.
…blocking()

Like the other preceding commits, this should eliminate the risk of missing
release() and corresponding setblocking(True) calls for this function.
…cking

Since recv_loop pairs acquiring a lock and setting a socket as non-blocking
together, as well as their corresponding release/blocking, combining them into
a single context manager function makes it safer.
@tayler6000
Copy link
Owner

Thank you for this PR @f3ndot

It is not only a major improvement, but it is well written and I can tell you read the CONTRIBUTING.md. I hope to get this merged soon.

Thank you again!

@tayler6000 tayler6000 self-requested a review January 3, 2024 22:14
@tayler6000 tayler6000 added bug Something isn't working security Security issues labels Jan 3, 2024
@tayler6000 tayler6000 self-assigned this Jan 3, 2024
Copy link
Owner

@tayler6000 tayler6000 left a comment

Choose a reason for hiding this comment

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

All of these changes look great, thank you for your thorough edits.

Tested "sleep 5 seconds, try (de)registering again" logic with the same script I used to create the feature. The feature still works as intended.

Thank you again!

@tayler6000 tayler6000 merged commit c5f8a55 into tayler6000:master Jan 3, 2024
2 checks passed
@f3ndot f3ndot deleted the fix/Issue-125 branch January 3, 2024 23:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working security Security issues
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants