-
Notifications
You must be signed in to change notification settings - Fork 72
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
Add thread safety #31
Changes from 3 commits
69d12a6
6e18211
ff41e14
f04435c
0407c2f
fb89794
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,22 +41,47 @@ public final class DependencyContainer { | |
} | ||
|
||
var definitions = [DefinitionKey : Definition]() | ||
var lock: NSRecursiveLock? | ||
|
||
/** | ||
Designated initializer for a DependencyContainer | ||
|
||
- parameter configBlock: A configuration block in which you typically put all you `register` calls. | ||
|
||
- parameter isThreadSafe: If true instance is thread safe, if false not thread safe but | ||
slight performance gain. Default is to be thread safe. | ||
- note: The `configBlock` is simply called at the end of the `init` to let you configure everything. | ||
It is only present for convenience to have a cleaner syntax when declaring and initializing | ||
your `DependencyContainer` instances. | ||
|
||
- returns: A new DependencyContainer. | ||
*/ | ||
public init(@noescape configBlock: (DependencyContainer->()) = { _ in }) { | ||
public init(isThreadSafe: Bool = true, @noescape configBlock: (DependencyContainer->()) = { _ in }) { | ||
if isThreadSafe { | ||
lock = NSRecursiveLock() | ||
} | ||
configBlock(self) | ||
} | ||
|
||
// MARK: - Thread safety | ||
func threadSafe(closure: () -> Void) { | ||
|
||
lock?.lock() | ||
defer { | ||
lock?.unlock() | ||
} | ||
closure() | ||
|
||
} | ||
|
||
func threadSafe<T>(closure: () -> T) -> T { | ||
|
||
lock?.lock() | ||
defer { | ||
lock?.unlock() | ||
} | ||
return closure() | ||
|
||
} | ||
// MARK: - Reset all dependencies | ||
|
||
/** | ||
|
@@ -74,9 +99,10 @@ public final class DependencyContainer { | |
*/ | ||
public func remove<T, F>(definition: DefinitionOf<T, F>, forTag tag: Tag? = nil) { | ||
let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag) | ||
definitions[key] = nil | ||
threadSafe { | ||
self.definitions[key] = nil | ||
} | ||
} | ||
|
||
// MARK: Register dependencies | ||
|
||
/** | ||
|
@@ -125,7 +151,9 @@ public final class DependencyContainer { | |
public func registerFactory<T, F>(tag tag: Tag? = nil, scope: ComponentScope, factory: F) -> DefinitionOf<T, F> { | ||
let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag) | ||
let definition = DefinitionOf<T, F>(factory: factory, scope: scope) | ||
definitions[key] = definition | ||
threadSafe { | ||
self.definitions[key] = definition | ||
} | ||
return definition | ||
} | ||
|
||
|
@@ -138,7 +166,9 @@ public final class DependencyContainer { | |
*/ | ||
public func register<T, F>(definition: DefinitionOf<T, F>, forTag tag: Tag? = nil) { | ||
let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag) | ||
definitions[key] = definition | ||
threadSafe { | ||
self.definitions[key] = definition | ||
} | ||
} | ||
|
||
// MARK: Resolve dependencies | ||
|
@@ -190,41 +220,43 @@ public final class DependencyContainer { | |
let key = DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: tag) | ||
let nilTagKey = tag.map { _ in DefinitionKey(protocolType: T.self, factoryType: F.self, associatedTag: nil) } | ||
|
||
guard let definition = (self.definitions[key] ?? self.definitions[nilTagKey]) as? DefinitionOf<T, F> else { | ||
throw DipError.DefinitionNotFound(key) | ||
guard let definition = (threadSafe { | ||
return (self.definitions[key] ?? self.definitions[nilTagKey]) | ||
}) as? DefinitionOf<T, F> else { | ||
throw DipError.DefinitionNotFound(key) | ||
} | ||
|
||
let usingKey: DefinitionKey? = definition.scope == .ObjectGraph ? key : nil | ||
return _resolve(tag, key: usingKey, definition: definition, builder: builder) | ||
} | ||
|
||
/// Actually resolve dependency | ||
private func _resolve<T, F>(tag: Tag? = nil, key: DefinitionKey?, definition: DefinitionOf<T, F>, builder: F->T) -> T { | ||
|
||
return resolvedInstances.resolve { | ||
|
||
if let previouslyResolved: T = resolvedInstances.previouslyResolved(key, definition: definition) { | ||
return previouslyResolved | ||
} | ||
else { | ||
let resolvedInstance = builder(definition.factory) | ||
return threadSafe { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it will be better to lock only whole There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, thinking about it a bit more, makes sense - less locking overhead |
||
return self.resolvedInstances.resolve { | ||
|
||
//when builder calls factory it will in turn resolve sub-dependencies (if there are any) | ||
//when it returns instance that we try to resolve here can be already resolved | ||
//so we return it, throwing away instance created by previous call to builder | ||
if let previouslyResolved: T = resolvedInstances.previouslyResolved(key, definition: definition) { | ||
if let previouslyResolved: T = self.resolvedInstances.previouslyResolved(key, definition: definition) { | ||
return previouslyResolved | ||
} | ||
|
||
resolvedInstances.storeResolvedInstance(resolvedInstance, forKey: key) | ||
definition.resolvedInstance = resolvedInstance | ||
definition.resolveDependenciesBlock?(self, resolvedInstance) | ||
|
||
return resolvedInstance | ||
else { | ||
let resolvedInstance = builder(definition.factory) | ||
|
||
//when builder calls factory it will in turn resolve sub-dependencies (if there are any) | ||
//when it returns instance that we try to resolve here can be already resolved | ||
//so we return it, throwing away instance created by previous call to builder | ||
if let previouslyResolved: T = self.resolvedInstances.previouslyResolved(key, definition: definition) { | ||
return previouslyResolved | ||
} | ||
|
||
self.resolvedInstances.storeResolvedInstance(resolvedInstance, forKey: key) | ||
definition.resolvedInstance = resolvedInstance | ||
definition.resolveDependenciesBlock?(self, resolvedInstance) | ||
|
||
return resolvedInstance | ||
} | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
// MARK: - Private | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For code style coherence, please remove leading and trailing empty lines inside both those
threadSafe
implementations.