-
Notifications
You must be signed in to change notification settings - Fork 0
/
initializer.go
130 lines (110 loc) · 2.4 KB
/
initializer.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
package dscope
import (
"reflect"
"sync"
"sync/atomic"
"github.com/reusee/e5"
)
type _Initializer struct {
Def any
err error
Values []reflect.Value
ID int64
mu sync.Mutex
done uint32
ReducerKind reducerKind
}
type reducerKind uint8
const (
notReducer reducerKind = iota
isReducer
isCustomReducer
)
func newInitializer(def any, reducerKind reducerKind) *_Initializer {
return &_Initializer{
ID: atomic.AddInt64(&nextInitializerID, 1),
Def: def,
ReducerKind: reducerKind,
}
}
var nextInitializerID int64 = 42
func (i *_Initializer) get(scope Scope, id _TypeID) (ret []reflect.Value, err error) {
if atomic.LoadUint32(&i.done) == 1 {
// init done
return i.Values, i.err
}
return i.getSlow(scope.appendPath(id))
}
func (i *_Initializer) getSlow(scope Scope) (ret []reflect.Value, err error) {
// detect dependency loop
for p := scope.path.Prev; p != nil; p = p.Prev {
if p.TypeID != scope.path.TypeID {
continue
}
return nil, we.With(
e5.Info("found dependency loop when calling %T", i.Def),
e5.Info("path: %+v", scope.path),
)(
ErrDependencyLoop,
)
}
i.mu.Lock()
defer i.mu.Unlock()
if i.done == 1 {
// init done
return i.Values, i.err
}
defer func() {
i.Values = ret
i.err = err
atomic.StoreUint32(&i.done, 1)
}()
// reducer
if i.ReducerKind != notReducer {
typ := i.Def.(reflect.Type)
typeID := getTypeID(typ)
values, ok := scope.values.Load(typeID)
if !ok { // NOCOVER
panic("impossible")
}
vs := make([]reflect.Value, len(values))
for i, value := range values {
var values []reflect.Value
values, err = value.initializer.get(scope, typeID)
if err != nil { // NOCOVER
return
}
vs[i] = values[value.typeInfo.Position]
}
switch i.ReducerKind {
case isReducer:
ret = []reflect.Value{
Reduce(vs),
}
case isCustomReducer:
ret = []reflect.Value{
vs[0].Interface().(CustomReducer).Reduce(scope, vs),
}
}
return ret, nil
}
// non-reducer
defValue := reflect.ValueOf(i.Def)
defKind := defValue.Kind()
switch defKind {
case reflect.Func:
var result CallResult
result = scope.Call(i.Def)
ret = result.Values
case reflect.Ptr:
ret = []reflect.Value{defValue.Elem()}
}
return ret, nil
}
func (s *_Initializer) reset() *_Initializer {
return &_Initializer{
ID: s.ID,
Def: s.Def,
ReducerKind: s.ReducerKind,
}
}