-
Notifications
You must be signed in to change notification settings - Fork 59
/
Copy pathcontainerGetterSafe.go
176 lines (150 loc) · 5.02 KB
/
containerGetterSafe.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package di
import (
"fmt"
"reflect"
"sync/atomic"
)
// SafeGet retrieves an object from the Container.
// The object has to belong to the Container or one of its parents.
// If the object does not already exist, it is created and saved in the Container.
// If the object can not be created, it returns an error.
//
// There are different ways to retrieve an object.
// - From its name: ctn.SafeGet("object-name")
// - From its definition: ctn.SafeGet(objectDef) or ctn.SafeGet(objectDefPtr) - only with the EnhancedBuilder
// - From its index: ctn.SafeGet(objectDef.Index()) - only with the EnhancedBuilder
// - From its type: ctn.SafeGet(reflect.typeOf(MyObject{})) - only if objectDef.Is includes the given type
// In case there are more than one definition matching the given type,
// the chosen one is the last definition inserted in the builder.
func (ctn Container) SafeGet(in interface{}) (interface{}, error) {
var index int
switch v := in.(type) {
case int:
index = v
case Def:
index = v.Index()
case *Def:
index = v.Index()
case string:
var ok bool
index, ok = ctn.core.indexesByName[v]
if !ok {
return nil, fmt.Errorf("could not get `%s` because the definition does not exist", v)
}
case reflect.Type:
indexes := ctn.core.indexesByType[v]
if len(indexes) == 0 {
return nil, fmt.Errorf("could not get type `%s` because it is not defined", v)
}
index = indexes[len(indexes)-1]
}
if index < 0 || index >= len(ctn.core.definitionScopeLevels) {
return nil, fmt.Errorf("could not get index `%d` because it does not exist", index)
}
// Finding the right core.
inputCore := ctn.core
core := ctn.core
if core.definitionScopeLevels[index] != core.scopeLevel {
for core.definitionScopeLevels[index] != core.scopeLevel {
core = core.parent
if core == nil {
return nil, fmt.Errorf(
"could not get `%s` because it requires `%s` scope which does not match this container scope or any of its parents scope",
inputCore.definitions[index].Name,
inputCore.definitions[index].Scope,
)
}
}
}
if atomic.LoadInt32(&core.isBuilt[index]) == 1 {
return core.objects[index], nil // Try to fetch an already built object as quickly as possible.
}
if inputCore != core {
ctn.core = core
ctn.builtList = make([]int, 0, 10) // Reset the builtList if the scope changed.
}
// Retrieve the definition.
def := core.definitions[index]
// Cycle detection.
if len(ctn.builtList) > 0 {
for _, builtIndex := range ctn.builtList {
if builtIndex == index {
return nil, formatCycleError(ctn, def)
}
}
}
// Handle unshared objects.
if def.Unshared {
obj, err := buildObject(def.Build, ctn, index, def.Name)
if err != nil {
return nil, fmt.Errorf("could not build `%s`: %+v", def.Name, err)
}
if def.Close == nil {
return obj, nil
}
core.m.Lock()
if core.closed {
core.m.Unlock()
err := closeObject(obj, def.Close, def.Name)
return nil, formatBuiltOnClosedContainerError(def, err)
}
core.unshared = append(core.unshared, obj)
core.unsharedIndex = append(core.unsharedIndex, index)
if len(ctn.builtList) == 0 {
core.dependencies.AddVertex(-len(core.unshared))
} else {
core.dependencies.AddEdge(ctn.builtList[len(ctn.builtList)-1], -len(core.unshared))
}
core.m.Unlock()
return obj, nil
}
// Handle shared objects.
core.m.Lock()
if core.closed {
core.m.Unlock()
return nil, fmt.Errorf(
"could not get `%s` because the container has been deleted", def.Name,
)
}
if atomic.LoadInt32(&core.isBuilt[index]) == 1 { // Check again if the object was created, with the lock this time.
core.m.Unlock()
return core.objects[index], nil
}
if building := core.building[index]; building != nil {
core.m.Unlock()
<-(*building) // Wait for the object to be created by another call to SafeGet.
return ctn.SafeGet(index) // Can not get the object without calling SafeGet again as its creation may have failed.
}
building := make(buildingChan)
core.building[index] = &building // Mark the object as building.
core.m.Unlock() // And release the lock as it can take a while to create the object.
// Building the shared object.
obj, err := buildObject(def.Build, ctn, index, def.Name)
core.m.Lock()
if err != nil {
// The object could not be created. Remove the building channel from the container
// and close it to allow the object to be created again.
core.building[index] = nil
core.m.Unlock()
close(building)
return nil, err
}
if core.closed {
// The container has been deleted while the object was being built.
// The newly created object needs to be closed, and it will not be returned.
core.m.Unlock()
close(building)
err = closeObject(obj, def.Close, def.Name)
return nil, formatBuiltOnClosedContainerError(def, err)
}
if len(ctn.builtList) == 0 {
core.dependencies.AddVertex(index)
} else {
core.dependencies.AddEdge(ctn.builtList[len(ctn.builtList)-1], index)
}
core.objects[index] = obj
atomic.StoreInt32(&core.isBuilt[index], 1)
core.m.Unlock()
close(building)
return obj, nil
}