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

Thread safe lazy loading with double check lock optimization #29

Merged
merged 7 commits into from Jun 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
166 changes: 85 additions & 81 deletions Sample/Pods/Pods.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

74 changes: 26 additions & 48 deletions Sources/WeaverDI/Builder.swift
Expand Up @@ -7,62 +7,40 @@

import Foundation

/// A representation of any builder.
protocol AnyBuilder {

var scope: Scope { get }
}

// MARK: - Builder

final class Builder {

let scope: Scope
private let body: Any
/// A `Builder` is an object responsible for lazily building the instance of a service.
/// It is fully thread-safe.
/// - Parameters
/// - I: Instance type.
/// - P: Parameters type. Usually a tuple containing multiple parameters (eg. `(p1: Int, p2: String, ...)`)
final class Builder<I, P>: AnyBuilder {

private var instance: Instance?
private let locker = NSLock()
typealias Body = (() -> P) -> I

init(scope: Scope, body: Any) {
let scope: Scope

private var instance: Instance

/// Inits a builder.
/// - Parameters
/// - scope: Service's scope used to determine which building & storing strategy to use.
/// - body: Block responsible of calling the service's initializer (eg. `init(p1: Int, p2: String, ...)`).
init(scope: Scope, body: @escaping Body) {
self.scope = scope
self.body = body
instance = Instance(scope: scope, body: body)
}

func functor<P, I>() -> (() -> P) -> I {

guard let body = body as? (() -> P) -> I else {
fatalError("Type \(((() -> P) -> I).self) doesn't correspond to body type: \(self.body.self)")
}

guard !scope.isTransient else {
return body
}

/// Makes the builder's body, which can then get called to build the service's instance, and store it.
func make() -> Body {
return { (parameters: () -> P) -> I in
self.locker.lock()
defer { self.locker.unlock() }

if let instance = self.instance?.value as? I {
return instance
}

let instance = body(parameters)
self.instance = Instance(value: instance as AnyObject, scope: self.scope)
return instance
return self.instance.getInstance(parameters: parameters)
}
}
}

// MARK: - Instance

private final class Instance {
private weak var weakValue: AnyObject?
private var strongValue: AnyObject?

init(value: AnyObject, scope: Scope) {

if scope.isWeak {
weakValue = value
} else {
strongValue = value
}
}

var value: AnyObject? {
return weakValue ?? strongValue ?? nil
}
}
24 changes: 16 additions & 8 deletions Sources/WeaverDI/BuilderStore.swift
Expand Up @@ -11,9 +11,9 @@ import Foundation

protocol BuilderStoring: AnyObject {

func get(for key: BuilderKey, isCalledFromAChild: Bool) -> Builder?
func get<I, P>(for key: BuilderKey, isCalledFromAChild: Bool) -> Builder<I, P>?

func set<P, I>(scope: Scope, key: BuilderKey, builder: @escaping (() -> P) -> I)
func set<I, P>(_ buidler: Builder<I, P>, for key: BuilderKey)

var parent: BuilderStoring? { get set }
}
Expand All @@ -22,24 +22,32 @@ protocol BuilderStoring: AnyObject {

extension BuilderStoring {

func get(for key: BuilderKey) -> Builder? {
func get<I, P>(for key: BuilderKey) -> Builder<I, P>? {
return get(for: key, isCalledFromAChild: false)
}

func set<I, P>(scope: Scope, for key: BuilderKey, body: @escaping Builder<I, P>.Body) {
set(Builder(scope: scope, body: body), for: key)
}
}

// MARK: - Store

final class BuilderStore: BuilderStoring {

private var builders: [BuilderKey: Builder] = [:]
private var builders: [BuilderKey: AnyBuilder] = [:]

weak var parent: BuilderStoring? = nil

func get(for key: BuilderKey, isCalledFromAChild: Bool) -> Builder? {
func get<I, P>(for key: BuilderKey, isCalledFromAChild: Bool) -> Builder<I, P>? {

guard let builder = builders[key] else {
guard let foundBuilder = builders[key] else {
return parent?.get(for: key, isCalledFromAChild: true)
}

guard let builder = foundBuilder as? Builder<I, P> else {
fatalError("Found a builder (\(foundBuilder.self)) with an incorrect type \(Builder<I, P>.self).")
}

if (isCalledFromAChild && builder.scope.allowsAccessFromChildren) || !isCalledFromAChild {
return builder
Expand All @@ -48,7 +56,7 @@ final class BuilderStore: BuilderStoring {
return parent?.get(for: key, isCalledFromAChild: true)
}

func set<P, I>(scope: Scope, key: BuilderKey, builder: @escaping (() -> P) -> I) {
builders[key] = Builder(scope: scope, body: builder)
func set<I, P>(_ builder: Builder<I, P>, for key: BuilderKey) {
builders[key] = builder
}
}
30 changes: 15 additions & 15 deletions Sources/WeaverDI/DependencyContainer.swift
Expand Up @@ -45,51 +45,51 @@ extension DependencyContainer: DependencyResolver {
public func resolve<S>(_ serviceType: S.Type, name: String? = nil) -> S {
let key = BuilderKey(for: serviceType, name: name)

guard let builder = builders.get(for: key) else {
guard let builder: Builder<S, DependencyContainer> = builders.get(for: key) else {
fatalError("\(DependencyContainer.self): Could not resolve \(key).")
}

return builder.functor()({ self })
return builder.make()({ self })
}

public func resolve<S, P1>(_ serviceType: S.Type, name: String? = nil, parameter: P1) -> S {
let key = BuilderKey(for: serviceType, name: name, parameterType: P1.self)

guard let builder = builders.get(for: key) else {
guard let builder: Builder<S, (DependencyContainer, P1)> = builders.get(for: key) else {
fatalError("\(DependencyContainer.self): Could not resolve \(key).")
}

return builder.functor()({ (self, parameter) })
return builder.make()({ (self, parameter) })
}

public func resolve<S, P1, P2>(_ serviceType: S.Type, name: String? = nil, parameters p1: P1, _ p2: P2) -> S {
let key = BuilderKey(for: serviceType, name: name, parameterTypes: P1.self, P2.self)

guard let builder = builders.get(for: key) else {
guard let builder: Builder<S, (DependencyContainer, P1, P2)> = builders.get(for: key) else {
fatalError("\(DependencyContainer.self): Could not resolve \(key).")
}

return builder.functor()({ (self, p1, p2) })
return builder.make()({ (self, p1, p2) })
}

public func resolve<S, P1, P2, P3>(_ serviceType: S.Type, name: String? = nil, parameters p1: P1, _ p2: P2, _ p3: P3) -> S {
let key = BuilderKey(for: serviceType, name: name, parameterTypes: P1.self, P2.self, P3.self)

guard let builder = builders.get(for: key) else {
guard let builder: Builder<S, (DependencyContainer, P1, P2, P3)> = builders.get(for: key) else {
fatalError("\(DependencyContainer.self): Could not resolve \(key).")
}

return builder.functor()({ (self, p1, p2, p3) })
return builder.make()({ (self, p1, p2, p3) })
}

public func resolve<S, P1, P2, P3, P4>(_ serviceType: S.Type, name: String? = nil, parameters p1: P1, _ p2: P2, _ p3: P3, _ p4: P4) -> S {
let key = BuilderKey(for: serviceType, name: name, parameterTypes: P1.self, P2.self, P3.self, P4.self)

guard let builder = builders.get(for: key) else {
guard let builder: Builder<S, (DependencyContainer, P1, P2, P3, P4)> = builders.get(for: key) else {
fatalError("\(DependencyContainer.self): Could not resolve \(key).")
}

return builder.functor()({ (self, p1, p2, p3, p4) })
return builder.make()({ (self, p1, p2, p3, p4) })
}
}

Expand All @@ -108,39 +108,39 @@ extension DependencyContainer {
public func register<S>(_ serviceType: S.Type, scope: Scope, name: String? = nil, builder: @escaping (DependencyContainer) -> S) {
let key = BuilderKey(for: serviceType, name: name)

builders.set(scope: scope, key: key) { (parameter: () -> (DependencyContainer)) -> S in
builders.set(scope: scope, for: key) { (parameter: () -> (DependencyContainer)) -> S in
return builder(parameter())
}
}

public func register<S, P1>(_ serviceType: S.Type, scope: Scope, name: String? = nil, builder: @escaping (DependencyContainer, P1) -> S) {
let key = BuilderKey(for: serviceType, name: name, parameterType: P1.self)

builders.set(scope: scope, key: key) { (parameters: () -> (DependencyContainer, P1)) -> S in
builders.set(scope: scope, for: key) { (parameters: () -> (DependencyContainer, P1)) -> S in
return builder(parameters().0, parameters().1)
}
}

public func register<S, P1, P2>(_ serviceType: S.Type, scope: Scope, name: String? = nil, builder: @escaping (DependencyContainer, P1, P2) -> S) {
let key = BuilderKey(for: serviceType, name: name, parameterTypes: P1.self, P2.self)

builders.set(scope: scope, key: key) { (parameters: () -> (DependencyContainer, P1, P2)) -> S in
builders.set(scope: scope, for: key) { (parameters: () -> (DependencyContainer, P1, P2)) -> S in
return builder(parameters().0, parameters().1, parameters().2)
}
}

public func register<S, P1, P2, P3>(_ serviceType: S.Type, scope: Scope, name: String? = nil, builder: @escaping (DependencyContainer, P1, P2, P3) -> S) {
let key = BuilderKey(for: serviceType, name: name, parameterTypes: P1.self, P2.self, P3.self)

builders.set(scope: scope, key: key) { (parameters: () -> (DependencyContainer, P1, P2, P3)) -> S in
builders.set(scope: scope, for: key) { (parameters: () -> (DependencyContainer, P1, P2, P3)) -> S in
return builder(parameters().0, parameters().1, parameters().2, parameters().3)
}
}

public func register<S, P1, P2, P3, P4>(_ serviceType: S.Type, scope: Scope, name: String? = nil, builder: @escaping (DependencyContainer, P1, P2, P3, P4) -> S) {
let key = BuilderKey(for: serviceType, name: name, parameterTypes: P1.self, P2.self, P3.self, P4.self)

builders.set(scope: scope, key: key) { (parameters: () -> (DependencyContainer, P1, P2, P3, P4)) -> S in
builders.set(scope: scope, for: key) { (parameters: () -> (DependencyContainer, P1, P2, P3, P4)) -> S in
return builder(parameters().0, parameters().1, parameters().2, parameters().3, parameters().4)
}
}
Expand Down