uber-go / mock

GoMock is a mocking framework for the Go programming language.
Apache License 2.0
1.82k stars 104 forks source link

deadlock on unexpected call with mocked argument implementing `fmt.Stringer` #116

Closed jpicht closed 5 months ago

jpicht commented 8 months ago

Actual behavior Deadlock on unexpected call to function where one or more arguments are a generated mock that implements fmt.Stringer.

Expected behavior No deadlock on an unexpected call

To Reproduce Steps to reproduce the behavior

  1. put the files below into a directory
  2. mockgen -package=deadlock -source=interfaces.go -destination=mocks.go
  3. go test -v .

interfaces.go

package deadlock

import "fmt"

type Outer interface {
    Consume(Inner)
}

type Inner interface {
    fmt.Stringer
}

deadlock_test.go

package deadlock

import (
    "testing"

    "go.uber.org/mock/gomock"
)

func TestDeadlock(t *testing.T) {
    ctrl := gomock.NewController(t)
    inner := NewMockInner(ctrl)
    outer := NewMockOuter(ctrl)
    outer.Consume(inner)
}

Additional Information

Triage Notes for the Maintainers

  1. The call to ctrl.T.Fatalf in controller.go:209 is holding ctrl.mu
  2. Fatalf will call the String() method on the mock (also unexpectedly)
  3. this will reach controller.go:200 and deadlock

backtrace

goroutine 18 [sync.Mutex.Lock]:
sync.runtime_SemacquireMutex(0x0?, 0x65?, 0x4be6c0?)
        /usr/local/go/src/runtime/sema.go:77 +0x25
sync.(*Mutex).lockSlow(0xc000196400)
        /usr/local/go/src/sync/mutex.go:171 +0x15d
sync.(*Mutex).Lock(...)
        /usr/local/go/src/sync/mutex.go:90
go.uber.org/mock/gomock.(*Controller).Call.func1(0xc0001963f0, {0x517220?, 0xc000184200}, {0x536f30, 0x6}, {0x0, 0x0, 0x0})
        .../go/pkg/mod/go.uber.org/mock@v0.3.0/gomock/controller.go:200 +0xd1                    
go.uber.org/mock/gomock.(*Controller).Call(0xc0001963f0, {0x517220, 0xc000184200}, {0x536f30, 0x6}, {0x0, 0x0, 0x0})
        .../go/pkg/mod/go.uber.org/mock@v0.3.0/gomock/controller.go:225 +0xa7                    
.../deadlock.(*MockInner).String(0xc000184200)                        
        /home/.../deadlock/mocks.go:78 +0x53                                  
fmt.(*pp).handleMethods(0xc00019a680, 0x184220?)
        /usr/local/go/src/fmt/print.go:673 +0x2b2
fmt.(*pp).printValue(0xc00019a680, {0x513ea0?, 0xc000184220?, 0x49e866?}, 0x76, 0x1)
        /usr/local/go/src/fmt/print.go:770 +0xca
fmt.(*pp).printValue(0xc00019a680, {0x50dd20?, 0xc0001a8090?, 0x5398db?}, 0x76, 0x0)
        /usr/local/go/src/fmt/print.go:912 +0x1605
fmt.(*pp).printArg(0xc00019a680, {0x50dd20?, 0xc0001a8090}, 0x76)
        /usr/local/go/src/fmt/print.go:759 +0x6a5
fmt.(*pp).doPrintf(0xc00019a680, {0x540a4e, 0x2e}, {0xc000180140?, 0x5, 0x5})
        /usr/local/go/src/fmt/print.go:1077 +0x39e
fmt.Sprintf({0x540a4e, 0x2e}, {0xc000180140, 0x5, 0x5})
        /usr/local/go/src/fmt/print.go:239 +0x53
testing.(*common).Fatalf(0xc00019e680, {0x540a4e?, 0x40ac65?}, {0xc000180140?, 0x50efa0?, 0x61ef01?})
        /usr/local/go/src/testing/testing.go:1082 +0x3f
go.uber.org/mock/gomock.(*Controller).Call.func1(0xc0001963f0, {0x5172a0?, 0xc000184210}, {0x537171, 0x7}, {0xc000184220, 0x1, 0x1})
        .../go/pkg/mod/go.uber.org/mock@v0.3.0/gomock/controller.go:209 +0x356                    
go.uber.org/mock/gomock.(*Controller).Call(0xc0001963f0, {0x5172a0, 0xc000184210}, {0x537171, 0x7}, {0xc000184220, 0x1, 0x1})
        .../go/pkg/mod/go.uber.org/mock@v0.3.0/gomock/controller.go:225 +0xa7                    
.../deadlock.(*MockOuter).Consume(0xc000184210, {0x5670e0?, 0xc000184200})                        
        /home/.../deadlock/mocks.go:43 +0xab                                  
.../deadlock.TestDeadlock(0x0?)                        
        /home/.../deadlock_test.go:13 +0x129                                           
testing.tRunner(0xc00019e680, 0x542e38)
        /usr/local/go/src/testing/testing.go:1595 +0xff
created by testing.(*T).Run in goroutine 1
        /usr/local/go/src/testing/testing.go:1648 +0x3ad
FAIL    .../deadlock    1.006s
FAIL