-
Notifications
You must be signed in to change notification settings - Fork 59
/
containerSlayer.go
145 lines (119 loc) · 3.67 KB
/
containerSlayer.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
package di
import (
"fmt"
"sync/atomic"
)
// DeleteWithSubContainers takes all the objects saved in this Container
// and calls the Close function of their Definition on them.
// It will also call DeleteWithSubContainers on each child and remove its reference in the parent Container.
// After deletion, the Container can no longer be used.
// The sub-containers are deleted even if they are still used in other goroutines.
// It can cause errors. You may want to use the Delete method instead.
func (ctn Container) DeleteWithSubContainers() error {
return deleteContainerCore(ctn.core)
}
// Delete works like DeleteWithSubContainers if the Container does not have any child.
// But if the Container has sub-containers, it will not be deleted right away.
// The deletion only occurs when all the sub-containers have been deleted manually.
// So you have to call Delete or DeleteWithSubContainers on all the sub-containers.
func (ctn Container) Delete() error {
ctn.core.m.Lock()
if len(ctn.core.children) > 0 {
ctn.core.deleteIfNoChild = true
ctn.core.m.Unlock()
return nil
}
ctn.core.m.Unlock()
return deleteContainerCore(ctn.core)
}
// Clean deletes the sub-container created by UnscopedSafeGet, UnscopedGet or UnscopedFill.
func (ctn Container) Clean() error {
ctn.core.m.Lock()
unscopedChild := ctn.core.unscopedChild
ctn.core.unscopedChild = nil
ctn.core.m.Unlock()
if unscopedChild != nil {
return deleteContainerCore(unscopedChild)
}
return nil
}
// IsClosed returns true if the Container has been deleted.
func (ctn Container) IsClosed() bool {
ctn.core.m.RLock()
closed := ctn.core.closed
ctn.core.m.RUnlock()
return closed
}
func deleteContainerCore(core *containerCore) error {
core.m.Lock()
clone := &containerCore{
parent: core.parent,
children: core.children,
unscopedChild: core.unscopedChild,
indexesByName: core.indexesByName,
indexesByType: core.indexesByType,
definitions: core.definitions,
objects: core.objects,
unshared: core.unshared,
unsharedIndex: core.unsharedIndex,
dependencies: core.dependencies,
}
core.closed = true
core.m.Unlock()
// Stop returning the already built objects.
for i := 0; i < len(core.isBuilt); i++ {
atomic.StoreInt32(&core.isBuilt[i], 0)
}
// Delete clone.
errBuilder := &multiErrBuilder{}
for child := range clone.children {
errBuilder.Add(deleteContainerCore(child))
}
if clone.unscopedChild != nil {
errBuilder.Add(deleteContainerCore(clone.unscopedChild))
}
if clone.parent != nil {
// Remove from parent.
clone.parent.m.Lock()
delete(clone.parent.children, core)
if !clone.parent.deleteIfNoChild || len(clone.parent.children) > 0 {
clone.parent.m.Unlock()
} else {
clone.parent.m.Unlock()
errBuilder.Add(deleteContainerCore(clone.parent))
}
}
// Close objects in the right order.
indexes, err := clone.dependencies.TopologicalOrdering()
errBuilder.Add(err)
for _, index := range indexes {
if index >= 0 {
errBuilder.Add(closeObject(
clone.objects[index],
clone.definitions[index].Close,
clone.definitions[index].Name,
))
} else {
errBuilder.Add(closeObject(
clone.unshared[-index-1],
clone.definitions[clone.unsharedIndex[-index-1]].Close,
clone.definitions[clone.unsharedIndex[-index-1]].Name,
))
}
}
return errBuilder.Build()
}
func closeObject(obj interface{}, closeFunc func(interface{}) error, defName string) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("could not close `%s`, Close function panicked: %+v", defName, r)
}
}()
if closeFunc != nil {
err = closeFunc(obj)
}
if err != nil {
return fmt.Errorf("could not close `%s`: %+v", defName, err)
}
return err
}