Skip to content

Commit

Permalink
timeout on singleton deadlock, closes #10
Browse files Browse the repository at this point in the history
  • Loading branch information
bastianccm authored and tessig committed Mar 23, 2023
1 parent 34f5abd commit 03f6ec1
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 8 deletions.
23 changes: 15 additions & 8 deletions dingo.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,14 +369,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)
Expand All @@ -390,6 +384,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 {
Expand Down
24 changes: 24 additions & 0 deletions scope.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package dingo

import (
"log"
"reflect"
"sync"
"time"
)

type (
Expand Down Expand Up @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions scope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
})
}

0 comments on commit 03f6ec1

Please sign in to comment.