Skip to content

MockController is not concurrency-friendly and leads to a deadlock #1882

@boris-petrov

Description

@boris-petrov

Describe the bug

I've been debugging a random failure in one of my tests. It led me to the following stack-trace of a blocked thread:

      org.spockframework.mock.runtime.MockController.handle(MockController.java)
      org.spockframework.mock.runtime.JavaMockInterceptor.intercept(JavaMockInterceptor.java:86)
      org.spockframework.mock.runtime.ByteBuddyInterceptorAdapter.interceptNonAbstract(ByteBuddyInterceptorAdapter.java:35)
      ...

In my test basically I have a mock:

def latch = new CountDownLatch(1)
Mock(Foo) {
  get(1) >> {
    assert latch.await(5, TimeUnit.SECONDS)
  }
  get(2) >> {
    latch.countDown()
  }
}

And then I concurrently call get with 1 and with 2. If the thread that calls 1 gets to the await - the test fails. MockController.handle is a synchronized method and hence the second thread can't get in it (as the first one is in that method). Not sure if that's a known issue? If so, what's the workaround?

To Reproduce

See above.

Expected behavior

No deadlock.

Actual behavior

Deadlock.

Java version

21.0.2

Buildtool version

Gradle 8.6.

What operating system are you using

Linux

Dependencies

Just Spock.

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions