Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

invocation from timer triggered go routine will report data race #173

Open
zdevwu opened this issue Apr 18, 2024 · 2 comments
Open

invocation from timer triggered go routine will report data race #173

zdevwu opened this issue Apr 18, 2024 · 2 comments

Comments

@zdevwu
Copy link

zdevwu commented Apr 18, 2024

There is a strange behaviour for the Call when triggered in go routines, a data race will be detected from timer triggered go routine but not in channel triggered go routine, see the bellow test cases:

func TestChannelTriggeredGoRoutineInvocation(t *testing.T) {
	ctrl := gomock.NewController(t)
	m := mock.NewMockMath(ctrl)

	called := make(chan struct{})
	releaseChannel := make(chan struct{})
	go func() {
		<-releaseChannel
		go func() {
			m.Sum(1, 1)
			called <- struct{}{}
		}()
	}()

	m.EXPECT().
		Sum(gomock.Any(), gomock.Any()).
		Return(2)
	close(releaseChannel)
	<-called
}

func TestTimerTriggeredGoRoutineInvocation(t *testing.T) {
	t.Skip("this test should not fail but it does")
	ctrl := gomock.NewController(t)
	m := mock.NewMockMath(ctrl)

	called := make(chan struct{})
	go func() {
		<-time.After(time.Millisecond * 300)
		go func() {
			m.Sum(1, 1)
			called <- struct{}{}
		}()
	}()

	m.EXPECT().
		Sum(gomock.Any(), gomock.Any()).
		Return(2)
	<-called
}
==================
WARNING: DATA RACE
Read at 0x00c000138160 by goroutine 18:
  go.uber.org/mock/gomock.(*Call).call()
      /home/zdevwu/projects/mock/gomock/call.go:432 +0x7ba
  go.uber.org/mock/gomock.(*Controller).Call.func1()
      /home/zdevwu/projects/mock/gomock/controller.go:224 +0x767
  go.uber.org/mock/gomock.(*Controller).Call()
      /home/zdevwu/projects/mock/gomock/controller.go:229 +0xc6
  go.uber.org/mock/sample/concurrent/mock.(*MockMath).Sum()
      /home/zdevwu/projects/mock/sample/concurrent/mock/concurrent_mock.go:44 +0x176
  go.uber.org/mock/sample/concurrent.TestTimerTriggeredGoRoutineInvocation.func1.1()
      /home/zdevwu/projects/mock/sample/concurrent/concurrent_test.go:119 +0x3b

Previous write at 0x00c000138160 by goroutine 16:
  go.uber.org/mock/gomock.(*Call).addAction()
      /home/zdevwu/projects/mock/gomock/call.go:497 +0xe8c
  go.uber.org/mock/gomock.(*Call).Return()
      /home/zdevwu/projects/mock/gomock/call.go:220 +0x3e9
  go.uber.org/mock/sample/concurrent.TestTimerTriggeredGoRoutineInvocation()
      /home/zdevwu/projects/mock/sample/concurrent/concurrent_test.go:126 +0x2d8
  testing.tRunner()
      /snap/go/10585/src/testing/testing.go:1689 +0x21e
  testing.(*T).Run.gowrap1()
      /snap/go/10585/src/testing/testing.go:1742 +0x44

Goroutine 18 (running) created at:
  go.uber.org/mock/sample/concurrent.TestTimerTriggeredGoRoutineInvocation.func1()
      /home/zdevwu/projects/mock/sample/concurrent/concurrent_test.go:118 +0x104

Goroutine 16 (running) created at:
  testing.(*T).Run()
      /snap/go/10585/src/testing/testing.go:1742 +0x825
  testing.runTests.func1()
      /snap/go/10585/src/testing/testing.go:2161 +0x85
  testing.tRunner()
      /snap/go/10585/src/testing/testing.go:1689 +0x21e
  testing.runTests()
      /snap/go/10585/src/testing/testing.go:2159 +0x8be
  testing.(*M).Run()
      /snap/go/10585/src/testing/testing.go:2027 +0xf17
  main.main()
      _testmain.go:55 +0x2bd
==================
==================
WARNING: DATA RACE
Read at 0x00c0001161c8 by goroutine 18:
  go.uber.org/mock/gomock.(*Controller).Call()
      /home/zdevwu/projects/mock/gomock/controller.go:232 +0xeb
  go.uber.org/mock/sample/concurrent/mock.(*MockMath).Sum()
      /home/zdevwu/projects/mock/sample/concurrent/mock/concurrent_mock.go:44 +0x176
  go.uber.org/mock/sample/concurrent.TestTimerTriggeredGoRoutineInvocation.func1.1()
      /home/zdevwu/projects/mock/sample/concurrent/concurrent_test.go:119 +0x3b

Previous write at 0x00c0001161c8 by goroutine 16:
  go.uber.org/mock/gomock.(*Call).addAction()
      /home/zdevwu/projects/mock/gomock/call.go:497 +0xe30
  go.uber.org/mock/gomock.(*Call).Return()
      /home/zdevwu/projects/mock/gomock/call.go:220 +0x3e9
  go.uber.org/mock/sample/concurrent.TestTimerTriggeredGoRoutineInvocation()
      /home/zdevwu/projects/mock/sample/concurrent/concurrent_test.go:126 +0x2d8
  testing.tRunner()
      /snap/go/10585/src/testing/testing.go:1689 +0x21e
  testing.(*T).Run.gowrap1()
      /snap/go/10585/src/testing/testing.go:1742 +0x44

Goroutine 18 (running) created at:
  go.uber.org/mock/sample/concurrent.TestTimerTriggeredGoRoutineInvocation.func1()
      /home/zdevwu/projects/mock/sample/concurrent/concurrent_test.go:118 +0x104

Goroutine 16 (running) created at:
  testing.(*T).Run()
      /snap/go/10585/src/testing/testing.go:1742 +0x825
  testing.runTests.func1()
      /snap/go/10585/src/testing/testing.go:2161 +0x85
  testing.tRunner()
      /snap/go/10585/src/testing/testing.go:1689 +0x21e
  testing.runTests()
      /snap/go/10585/src/testing/testing.go:2159 +0x8be
  testing.(*M).Run()
      /snap/go/10585/src/testing/testing.go:2027 +0xf17
  main.main()
      _testmain.go:55 +0x2bd
==================
    concurrent_test.go:128: race should be detected
    testing.go:1398: race detected during execution of test
--- FAIL: TestTimerTriggeredInvocation (0.31s)

The fork to demo the issue here:

zdevwu@0a962b4

  • gomock mode (reflect or source): source
  • gomock version or git ref: c9ae04c
  • golang version: go1.22.2

Triage Notes for the Maintainers

@zdevwu zdevwu changed the title invocation from timer triggered go routing will report data race invocation from timer triggered go routine will report data race Apr 18, 2024
@r-hang
Copy link
Contributor

r-hang commented Apr 23, 2024

Hey @zdevwu, are you expecting the mock generated methods to be concurrency safe? If so, they're not expected to be.

With respect to the two cases you gave above, the first case does not race as he first goroutine only gets unblocked after the mock expectation is declared which serializes the the Sum expectation setting and the mock call to Sum.

@zdevwu
Copy link
Author

zdevwu commented Apr 30, 2024

Thanks for getting back @r-hang, can you elaborate a bit on the second case please? Although not explicitly sending the signal by closing the releaseChannel to continue to launch the go routine, the timer in second case makes sure the mock happens before the nested go routine that is calling Sum start running, both cases the mock happens before the call, but race is only detected in the second case with timer released go routine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants