Skip to content

Concurrent interactions deadlock because of a synchronized method MockController#handle #583

@ptrts

Description

@ptrts

The version is 1.0-groovy-2.4

import spock.lang.Specification

import java.util.concurrent.CountDownLatch
import java.util.concurrent.Semaphore

class CodeRunnerSpec extends Specification {

    class CodeRunner {

        Runnable code

        Runnable fallback

        Semaphore semaphore = new Semaphore(1)

        void log(String message) {
            println Thread.currentThread().getName() + ' ' + message
        }

        /**
         * Runs the runnable in a new (!) thread. 
         * If the runnable is already running, runs fallback in the current (!) thread
         */
        void runCode() {

            log '=== runCode(): begin ===' 
            log 'trying to acquire the semaphore'

            if (semaphore.tryAcquire()) {

                log 'starting the code in a new thread'

                Thread.start {

                    log 'we are in the thread, starting the code'

                    code.run()

                    log 'code has finished working, releasing the semaphore'

                    semaphore.release()
                }

                log 'the thread has been started'

            } else {

                log 'the semaphore is busy, running the fallback'

                fallback.run()

                log 'the fallback has finished'
            }

            log '=== runCode(): end ==='
        }
    }

    def "Should run the fallback the code is already running"() {

        given:

        CountDownLatch latch = new CountDownLatch(1)

        CodeRunner codeRunner = new CodeRunner();

        codeRunner.code = Stub(Runnable) {
            run() >> {
                codeRunner.log 'waiting...'
                latch.await()
                codeRunner.log 'finished waiting, exiting'
            }
        }

        codeRunner.fallback = Mock(Runnable)

        when:

        // the closure will start in a new (!) thread and start to wait on the latch
        codeRunner.runCode()

        Thread.sleep(10)

        // the closure is already running, so the fallback should be run in the current (!) thread
        codeRunner.runCode()

        ///////////////////////////////////////////////////////////////////////////////////
        // it won't get to here, as it will be waiting on entering a synchronized method:
        //
        //          org.spockframework.mock.runtime.MockController#handle
        //
        ///////////////////////////////////////////////////////////////////////////////////

        then:

        1 * codeRunner.fallback.run()

        cleanup:

        latch.countDown()
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    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