diff --git a/dingo.go b/dingo.go index 0df01e5..34d5302 100644 --- a/dingo.go +++ b/dingo.go @@ -367,14 +367,8 @@ func (injector *Injector) createInstanceOfAnnotatedType(t reflect.Type, annotati } if circularTrace != nil { - for _, ct := range circularTrace { - if ct.typ == t && ct.annotation == annotation { - for _, ct := range circularTrace { - log.Println(ct.typ.PkgPath() + "#" + ct.typ.Name() + ": " + ct.annotation) - } - log.Println(t.PkgPath() + "#" + t.Name() + ": " + annotation) - panic("detected circular dependency") - } + if checkCircular(circularTrace, t, annotation) { + panic("detected circular dependency") } subCircularTrace := make([]circularTraceEntry, len(circularTrace)) copy(subCircularTrace, circularTrace) @@ -388,6 +382,19 @@ func (injector *Injector) createInstanceOfAnnotatedType(t reflect.Type, annotati return n, injector.requestInjection(n.Interface(), nil) } +func checkCircular(circularTrace []circularTraceEntry, t reflect.Type, annotation string) bool { + for _, ct := range circularTrace { + if ct.typ == t && ct.annotation == annotation { + for _, ct := range circularTrace { + log.Println(ct.typ.PkgPath() + "#" + ct.typ.Name() + ": " + ct.annotation) + } + log.Println(t.PkgPath() + "#" + t.Name() + ": " + annotation) + return true + } + } + return false +} + func reflectedError(err *error, t reflect.Type) reflect.Value { rerr := reflect.New(reflect.TypeOf(new(error)).Elem()).Elem() if err == nil || *err == nil { diff --git a/scope.go b/scope.go index ab0262e..b08d5be 100644 --- a/scope.go +++ b/scope.go @@ -1,8 +1,10 @@ package dingo import ( + "log" "reflect" "sync" + "time" ) type ( @@ -56,7 +58,29 @@ func (s *SingletonScope) ResolveType(t reflect.Type, annotation string, unscoped if l, ok := s.instanceLock[ident]; ok { // we have the instance lock s.mu.Unlock() + + ticker := time.NewTimer(10 * time.Second) + go func() { + <-ticker.C + path := t.PkgPath() + if path == "" { + if t.Kind() == reflect.Ptr { + path = t.Elem().PkgPath() + } + } + if path != "" { + path += "." + } + path += t.Name() + //checkCircular(traceCircular, t, annotation) + // trigger traceCircular + if traceCircular != nil { + unscoped(t, annotation, false) + } + log.Printf("Singleton: timed out waiting for instance lock: type: %q, annotation: %q", path, annotation) + }() l.RLock() + ticker.Stop() defer l.RUnlock() instance, _ := s.instances.Load(ident) diff --git a/scope_test.go b/scope_test.go index 31098fc..d00e781 100644 --- a/scope_test.go +++ b/scope_test.go @@ -148,3 +148,23 @@ func TestInheritedScope(t *testing.T) { secondI := i.(*inheritedScopeInjected) assert.Same(t, firstI.i, secondI.i) } + +type circSingletonA struct { + B *circSingletonB `inject:""` +} +type circSingletonB struct { + A *circSingletonA `inject:""` +} + +func TestCircularSingletonBinding(t *testing.T) { + EnableCircularTracing() + defer func() { + traceCircular = nil + }() + injector, err := NewInjector() + assert.NoError(t, err) + injector.Bind(new(circSingletonA)).In(Singleton) + assert.Panics(t, func() { + injector.GetInstance(new(circSingletonA)) + }) +}