Fix an Injector.get() thread safety regression (#215)

One of the thread safety tests has been failing on various PyPy
versions[1]:

    _____________ TestThreadSafety.test_singleton_scope_is_thread_safe _____________
    Traceback (most recent call last):
      File "/home/runner/work/injector/injector/injector_test.py", line 863, in test_singleton_scope_is_thread_safe
        assert a is b
    AssertionError: assert <injector_test.TestThreadSafety.setup.<locals>.XXX object at 0x0000000006b80d08> is <injector_test.TestThreadSafety.setup.<locals>.XXX object at 0x0000000006b810c0>

SingletonScope's get() was protected with a lock already but Injector.get()
wasn't and there are things in there that are totally likely to produce
invalid results if executed from multiple contexts concurrently.

Rather than try to nail down the exact issue that caused this, figure
out why it's been failing only on PyPy etc. I figured it's ok to go for
the big hammer here and protect the whole method with the lock. This way
we don't have to wonder about the details that can be *really* difficult
to figure out.

I don't think this is gonna make anything meaningfully slower –
ClassProvider and CallableProvider are already locked (indirecty,
through Injector.args_to_inject() being synchronized) and I expect
these to be involved in vast majority of the injection work.

[1] https://github.com/python-injector/injector/actions/runs/4156039132/jobs/7189544034
This commit is contained in:
Jakub Stasiak 2023-02-12 13:51:15 +01:00 committed by GitHub
parent 90aa2cc877
commit 87826b3542
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 1 additions and 0 deletions

View File

@ -898,6 +898,7 @@ class Injector:
def _log_prefix(self) -> str: def _log_prefix(self) -> str:
return '>' * (len(self._stack) + 1) + ' ' return '>' * (len(self._stack) + 1) + ' '
@synchronized(lock)
def get(self, interface: Type[T], scope: Union[ScopeDecorator, Type[Scope], None] = None) -> T: def get(self, interface: Type[T], scope: Union[ScopeDecorator, Type[Scope], None] = None) -> T:
"""Get an instance of the given interface. """Get an instance of the given interface.