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

Complex chains of circular dependencies makes Typhoon to inject nil #77

Closed
cesteban opened this issue Sep 12, 2013 · 10 comments
Closed
Labels

Comments

@cesteban
Copy link
Contributor

ComponentA is singleton and uses init injection, ComponentB is prototype and uses property injection.

1.- Some component asks for ComponentA: ComponentA is marked as currently resolving, and Typhoon initiates its instantiation.
2.- ComponentA has a circular dependency on ComponentB, correctly detected.
3.- Typhoon tries to build ComponentB. It has a dependency on ComponentA, detected as circular (and left for later), and another dependency on ComponentC which in turn depends on ComponentA again!
4.- When building ComponentC, ComponentA must be built, so it is allocated and marked as currently resolving once more -> This overwrites the dictionary value of ComponentA!!!
5.- When ComponentA is ready (correctly injected with the still not complete instance of ComponentB), it is injected in ComponentC and marked as done resolving -> This means the only instance of ComponentA that we still had in the registry is deleted!
6.- ComponentC is ready and injected in ComponentB. Now it is turn of circular dependencies of B, which includes A. Typhoon injects the instance of A that is supposed to be stashed in _currentlyResolvingReferences
7.- [_currentlyResolvingReferences objectForKey:componentKey] returns nil
8.- ComponentB is injected with a nil.

_currentlyResolvingReferences must be promoted to act as a temp stash local to the current cycle. I believe @jasperblues was asking for something like this in #57.

I'm working on a fix for this issue, I'll let you know. I tried to build a failing test but it is very difficult to reproduce using simple classes. Furthermore, the bug only appears sometimes, as the order in which assembly definitions are instantiated is not defined, and has a great influence.

@jasperblues
Copy link
Member

Sounds like a tragic scene in a spaghetti western! ;) . . . look forward to the fix! :)

@jasperblues
Copy link
Member

By allowing circular deps with prototype scope to participate as initializer arguments, we're definitely moving away from Spring territory here. . . the price might be that we have to make the factory methods on TyphoonComponentFactory (eg componentForKey, componentForType) @synchronized . .

Either that or use one stash per request.

@ghost ghost assigned cesteban Sep 12, 2013
@jasperblues
Copy link
Member

Will the new/pending "weak singleton" scope address your issue?

The idea behind "weak singleton" is that its the same as singleton, except it goes away (ie dealloc'd) when nothing else besides Typhoon is using it. . . (except during instantiation of course, when it would have to be a strong reference).

@cesteban
Copy link
Contributor Author

No, this is a bug, we should not inject nils, no matter the scope of the components. Fix is very easy: use a little bit more complex structure for the _currentlyResolvingDefinition: a dictionary of stacks. For each key we hold and stack, so every component that asks for a prototype dependency has its own instance allocated and stashed during its building. Then the instance is popped, not disrupting other currently resolving references.

cesteban pushed a commit to cesteban/Typhoon that referenced this issue Sep 12, 2013
cesteban pushed a commit to cesteban/Typhoon that referenced this issue Sep 12, 2013
…h currently resolving references locally
@jasperblues
Copy link
Member

If we go this way what happens with calls from multiple threads to [factory componentForKey:] . . . ? I think we'll need to make it @synchronized.

@jasperblues
Copy link
Member

is this closed?

@jasperblues
Copy link
Member

@cesteban Perhaps to test we could:

  • Loop 1000 times. If all pass test passes.

@cesteban
Copy link
Contributor Author

Not closed yet. We need to address the concurrency issues and polish a little bit.

@cesteban
Copy link
Contributor Author

@jasperblues that test you suggest would be unstable, which means useless. No, I need to find an STR, but it is kind of tricky, and I don't think it is a priority right now. What do you think?

@jasperblues
Copy link
Member

Agree - not a current priority.

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

No branches or pull requests

2 participants