Skip to content

ThreadSafeSingleton provides None if called from separate thread while an earlier call is still processing. #433

@garlandhu

Description

@garlandhu

Hello, 4.6.0 seems to have created an issue with providers.ThreadSafeSingleton.

If the singleton object to be created has a lengthy init time, and the provider is called a second time while the first one is still being created from a seperate thread, the second call returns None instead of the object.

You can recreate this issue with the example below.

import threading
import unittest
from dependency_injector import providers


class ThingThatTakesLongTimeToInitAndNeedsToBeSingleton:
	def __init__(self):
		self.j = 0
		for i in range(1000000):
			self.j += i


provider_of_annoying_object = providers.ThreadSafeSingleton(
	ThingThatTakesLongTimeToInitAndNeedsToBeSingleton
)


class WorkerThatCanMoveToThreadAndCallProvider:

	def __init__(self, provider):
		super().__init__()
		self._provider = provider
		self.thing_i_made = 0

	def use_provider(self):
		self.thing_i_made = self._provider()
		# Interestingly, if you add a print statement here, it prints 3 times, not twice.


class TestProvidersInThreads(unittest.TestCase):

	def test_provider_called_in_thread(self):
		worker_1 = WorkerThatCanMoveToThreadAndCallProvider(provider_of_annoying_object)
		thread_1 = threading.Thread(target=worker_1.use_provider)

		worker_2 = WorkerThatCanMoveToThreadAndCallProvider(provider_of_annoying_object)
		thread_2 = threading.Thread(target=worker_2.use_provider)

		thread_1.start()
		thread_2.start()
		thread_1.join()
		thread_2.join()
		self.assertIsInstance(worker_1.thing_i_made, ThingThatTakesLongTimeToInitAndNeedsToBeSingleton)
		self.assertIsInstance(worker_2.thing_i_made, ThingThatTakesLongTimeToInitAndNeedsToBeSingleton)
		self.assertIs(worker_1.thing_i_made, worker_2.thing_i_made)

For versions > 4.6.0, this test fails with:

self.assertIsInstance(worker_2.thing_i_made, ThingThatTakesLongTimeToInitAndNeedsToBeSingleton)
AssertionError: None is not an instance of <class 'tests.providers.test_why_image_interface_dead.ThingThatTakesLongTimeToInitAndNeedsToBeSingleton'>

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions