Skip to content

Commit d07ce8c

Browse files
committed
feat: support std error
fix: fix concurrent map writes BREAKING CHANGE: change the error with std error which might break the origin err handler.
1 parent fb9a084 commit d07ce8c

File tree

4 files changed

+115
-20
lines changed

4 files changed

+115
-20
lines changed

Diff for: error.go

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package inject
2+
3+
import "errors"
4+
5+
var (
6+
ErrValueNotFound = errors.New("value not found")
7+
ErrValueCanNotSet = errors.New("value can not set")
8+
)

Diff for: error_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package inject
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"reflect"
7+
"testing"
8+
)
9+
10+
func TestError(t *testing.T) {
11+
err := fmt.Errorf("%w: %v", ErrValueNotFound, reflect.TypeOf(""))
12+
expect(t, errors.Is(err, ErrValueNotFound), true)
13+
err = fmt.Errorf("%w: %v", ErrValueCanNotSet, reflect.TypeOf(""))
14+
expect(t, errors.Is(err, ErrValueCanNotSet), true)
15+
}

Diff for: inject.go

+17-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package inject
55
import (
66
"fmt"
77
"reflect"
8+
"sync"
89
)
910

1011
// Injector represents an interface for mapping and injecting dependencies into
@@ -79,10 +80,11 @@ var _ Injector = (*injector)(nil)
7980
type injector struct {
8081
values map[reflect.Type]reflect.Value
8182
parent Injector
83+
mu sync.RWMutex
8284
}
8385

8486
// InterfaceOf dereferences a pointer to an Interface type. It panics if value
85-
// is not an pointer to an interface.
87+
// is not a pointer to an interface.
8688
func InterfaceOf(value interface{}) reflect.Type {
8789
t := reflect.TypeOf(value)
8890
for t.Kind() == reflect.Ptr {
@@ -127,7 +129,7 @@ func (inj *injector) fastInvoke(f FastInvoker, t reflect.Type, numIn int) ([]ref
127129
argType = t.In(i)
128130
val = inj.Value(argType)
129131
if !val.IsValid() {
130-
return nil, fmt.Errorf("value not found for type %v", argType)
132+
return nil, fmt.Errorf("%w: %v", ErrValueNotFound, argType)
131133
}
132134

133135
in[i] = val.Interface()
@@ -146,7 +148,7 @@ func (inj *injector) callInvoke(f interface{}, t reflect.Type, numIn int) ([]ref
146148
argType = t.In(i)
147149
val = inj.Value(argType)
148150
if !val.IsValid() {
149-
return nil, fmt.Errorf("value not found for type %v", argType)
151+
return nil, fmt.Errorf("%w: %v", ErrValueNotFound, argType)
150152
}
151153

152154
in[i] = val
@@ -176,7 +178,7 @@ func (inj *injector) Apply(val interface{}) error {
176178
ft := f.Type()
177179
v := inj.Value(ft)
178180
if !v.IsValid() {
179-
return fmt.Errorf("value not found for type %v", ft)
181+
return fmt.Errorf("%w: %v", ErrValueNotFound, ft)
180182
}
181183

182184
f.Set(v)
@@ -187,24 +189,32 @@ func (inj *injector) Apply(val interface{}) error {
187189
}
188190

189191
func (inj *injector) Map(values ...interface{}) TypeMapper {
192+
inj.mu.Lock()
190193
for _, val := range values {
191194
inj.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
192195
}
196+
inj.mu.Unlock()
193197
return inj
194198
}
195199

196200
func (inj *injector) MapTo(val, ifacePtr interface{}) TypeMapper {
201+
inj.mu.Lock()
197202
inj.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
203+
inj.mu.Unlock()
198204
return inj
199205
}
200206

201207
func (inj *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper {
208+
inj.mu.Lock()
202209
inj.values[typ] = val
210+
inj.mu.Unlock()
203211
return inj
204212
}
205213

206214
func (inj *injector) Value(t reflect.Type) reflect.Value {
215+
inj.mu.RLock()
207216
val := inj.values[t]
217+
inj.mu.RUnlock()
208218

209219
if val.IsValid() {
210220
return val
@@ -233,16 +243,15 @@ func (inj *injector) Load(val interface{}) error {
233243
valType := reflect.TypeOf(val)
234244
value := inj.Value(valType)
235245
if !value.IsValid() {
236-
237-
return fmt.Errorf("value not found for type %v", valType)
246+
return fmt.Errorf("%w: %v", ErrValueNotFound, valType)
238247
}
239248
v := reflect.ValueOf(val)
240249
if v.Kind() != reflect.Ptr {
241-
return fmt.Errorf("value not a pointer for type %v", valType)
250+
return fmt.Errorf("%w: %v", ErrValueCanNotSet, valType)
242251
}
243252
v = v.Elem()
244253
if !v.CanSet() {
245-
return fmt.Errorf("value not settable for type %v", valType)
254+
return fmt.Errorf("%w: %v", ErrValueCanNotSet, valType)
246255
}
247256
v.Set(value.Elem())
248257
return nil

Diff for: inject_test.go

+75-12
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package inject
33
import (
44
"fmt"
55
"reflect"
6+
"sync"
67
"testing"
8+
"unsafe"
79
)
810

911
type specialString interface{}
@@ -23,9 +25,9 @@ func (g *greeter) String() string {
2325
}
2426

2527
/* Test Helpers */
26-
func expect(t *testing.T, a interface{}, b interface{}) {
27-
if a != b {
28-
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
28+
func expect(t testing.TB, actual interface{}, expect interface{}) {
29+
if actual != expect {
30+
t.Errorf("Expected %v (type %v) - Got %v (type %v)", expect, reflect.TypeOf(expect), actual, reflect.TypeOf(actual))
2931
}
3032
}
3133

@@ -41,6 +43,11 @@ func (myFastInvoker) Invoke([]interface{}) ([]reflect.Value, error) {
4143
return nil, nil
4244
}
4345

46+
func TestInjectorSize(t *testing.T) {
47+
// prevent unnecessary memory usage increases
48+
expect(t, unsafe.Alignof(injector{}), uintptr(8))
49+
}
50+
4451
func BenchmarkNew(b *testing.B) {
4552
b.ReportAllocs()
4653
var j Injector
@@ -140,6 +147,24 @@ func TestInjector_InterfaceOf(t *testing.T) {
140147
InterfaceOf((*testing.T)(nil))
141148
}
142149

150+
func TestInjector_Map(t *testing.T) {
151+
inj := New()
152+
153+
g := &greeter{"Jeremy"}
154+
inj.Map(g)
155+
156+
expect(t, inj.Value(InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true)
157+
}
158+
159+
func BenchmarkInjector_Map(b *testing.B) {
160+
b.ReportAllocs()
161+
inj := New()
162+
b.ResetTimer()
163+
for i := 0; i < b.N; i++ {
164+
inj.Map("Jeremy")
165+
}
166+
}
167+
143168
func TestInjector_Set(t *testing.T) {
144169
inj := New()
145170

@@ -204,15 +229,6 @@ func TestInjector_SetParent(t *testing.T) {
204229
expect(t, inj2.Value(InterfaceOf((*specialString)(nil))).IsValid(), true)
205230
}
206231

207-
func TestInjector_Implementors(t *testing.T) {
208-
inj := New()
209-
210-
g := &greeter{"Jeremy"}
211-
inj.Map(g)
212-
213-
expect(t, inj.Value(InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true)
214-
}
215-
216232
func TestIsFastInvoker(t *testing.T) {
217233
expect(t, IsFastInvoker(myFastInvoker(nil)), true)
218234
}
@@ -249,3 +265,50 @@ func BenchmarkInjector_FastInvoke(b *testing.B) {
249265
_, _ = inj.Invoke(fn)
250266
}
251267
}
268+
269+
func TestConcurrent(t *testing.T) {
270+
t.Parallel()
271+
inj := New()
272+
t.Run("Map", func(t *testing.T) {
273+
var trigger, wg sync.WaitGroup
274+
trigger.Add(1)
275+
for i := 0; i < 1000; i++ {
276+
wg.Add(1)
277+
go func() {
278+
trigger.Wait()
279+
inj.Map("")
280+
wg.Done()
281+
}()
282+
}
283+
trigger.Done()
284+
wg.Wait()
285+
})
286+
t.Run("MapTo", func(t *testing.T) {
287+
var trigger, wg sync.WaitGroup
288+
trigger.Add(1)
289+
for i := 0; i < 1000; i++ {
290+
wg.Add(1)
291+
go func() {
292+
trigger.Wait()
293+
inj.MapTo("", (*Injector)(nil))
294+
wg.Done()
295+
}()
296+
}
297+
trigger.Done()
298+
wg.Wait()
299+
})
300+
t.Run("Set", func(t *testing.T) {
301+
var trigger, wg sync.WaitGroup
302+
trigger.Add(1)
303+
for i := 0; i < 1000; i++ {
304+
wg.Add(1)
305+
go func() {
306+
trigger.Wait()
307+
inj.Set(reflect.TypeOf(""), reflect.ValueOf(""))
308+
wg.Done()
309+
}()
310+
}
311+
trigger.Done()
312+
wg.Wait()
313+
})
314+
}

0 commit comments

Comments
 (0)