<commit_before>/*
 * Copyright 2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gradle.api.internal.tasks.compile.daemon

import org.gradle.api.internal.project.ProjectInternal
import org.gradle.util.ConcurrentSpecification
import org.gradle.util.TestUtil

import java.util.concurrent.CountDownLatch

import static org.gradle.test.fixtures.ConcurrentTestUtil.poll

class CompilerDaemonManagerTest extends ConcurrentSpecification {

    def project = TestUtil.createRootProject()

    def "creates new daemon when fork options incompatible"() {
        def forkOptions = Stub(DaemonForkOptions) { isCompatibleWith(_) >> false }
        def m = new CompilerDaemonManager(new DummyCompilerDaemonStarter())

        when:
        Set daemons = []
        daemons << m.getDaemon(project, forkOptions)
        daemons << m.getDaemon(project, forkOptions)

        then:
        daemons.size() == 2
    }

    def "reuses daemons"() {
        def forkOptions = Stub(DaemonForkOptions) { isCompatibleWith(_) >> true }
        def m = new CompilerDaemonManager(new DummyCompilerDaemonStarter())

        when:
        Set daemons = []
        daemons << m.getDaemon(project, forkOptions).setIdle(true)
        daemons << m.getDaemon(project, forkOptions)

        then:
        daemons.size() == 1
    }

    def "allows compiler daemon per thread"() {
        def forkOptions = Stub(DaemonForkOptions) { isCompatibleWith(_) >> true }
        def starter = new DummyCompilerDaemonStarter().blockCreation()
        def m = new CompilerDaemonManager(starter)

        when: //2 threads asking for daemons
        Set daemons = []
        start { daemons << m.getDaemon(project, forkOptions) }
        start { daemons << m.getDaemon(project, forkOptions) }

        then: //both threads are creating an instance of a daemon at the same time
        poll { assert starter.awaitingCreation == 2 }

        when: //complete creation of the daemons
        starter.unblockCreation()

        then:
        poll { assert daemons.size() == 2 }
    }

    def "reuses daemons in concurrent scenario"() {
        def forkOptions = Stub(DaemonForkOptions) { isCompatibleWith(_) >> true }
        def m = new CompilerDaemonManager(new DummyCompilerDaemonStarter())

        when: //2 threads asking for daemons
        Set daemons = []
        start { daemons << m.getDaemon(project, forkOptions) }
        start { daemons << m.getDaemon(project, forkOptions) }

        then: //both threads are creating an instance of a daemon at the same time
        poll { assert daemons.size() == 2 }

        when: //daemons finished their jobs
        daemons.each { it.idle = true }

        and: //and new daemon requested
        daemons << m.getDaemon(project, forkOptions)

        then: //one of the daemons is reused
        poll {
            assert daemons.size() == 2
            assert daemons.count { it.idle } == 1
        }
    }

    private static class DummyCompilerDaemonStarter extends CompilerDaemonStarter {
        def latch = new CountDownLatch(0)
        def awaitingCreation = 0

        CompilerDaemonClient startDaemon(ProjectInternal project, DaemonForkOptions forkOptions) {
            awaitingCreation++
            latch.await()
            new CompilerDaemonClient(forkOptions, null, null)
        }

        DummyCompilerDaemonStarter blockCreation() {
            latch = new CountDownLatch(1)
            this
        }

        void unblockCreation() {
            latch.countDown()
        }
    }
}
<commit_msg>Fixed a few thread safety issues in unit test.<commit_after>/*
 * Copyright 2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gradle.api.internal.tasks.compile.daemon

import org.gradle.api.internal.project.ProjectInternal
import org.gradle.util.ConcurrentSpecification
import org.gradle.util.TestUtil

import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.locks.ReentrantLock

import static org.gradle.test.fixtures.ConcurrentTestUtil.poll

class CompilerDaemonManagerTest extends ConcurrentSpecification {

    def project = TestUtil.createRootProject()

    def "creates new daemon when fork options incompatible"() {
        def forkOptions = Stub(DaemonForkOptions) { isCompatibleWith(_) >> false }
        def m = new CompilerDaemonManager(new DummyCompilerDaemonStarter())

        when:
        Set daemons = []
        daemons << m.getDaemon(project, forkOptions)
        daemons << m.getDaemon(project, forkOptions)

        then:
        daemons.size() == 2
    }

    def "reuses daemons"() {
        def forkOptions = Stub(DaemonForkOptions) { isCompatibleWith(_) >> true }
        def m = new CompilerDaemonManager(new DummyCompilerDaemonStarter())

        when:
        Set daemons = []
        daemons << m.getDaemon(project, forkOptions).setIdle(true)
        daemons << m.getDaemon(project, forkOptions)

        then:
        daemons.size() == 1
    }

    def "allows compiler daemon per thread"() {
        def forkOptions = Stub(DaemonForkOptions) { isCompatibleWith(_) >> true }
        def starter = new DummyCompilerDaemonStarter().blockCreation()
        def m = new CompilerDaemonManager(starter)

        when: //2 threads asking for daemons
        Set daemons = new CopyOnWriteArraySet()
        start { daemons << m.getDaemon(project, forkOptions) }
        start { daemons << m.getDaemon(project, forkOptions) }

        then: //both threads are creating an instance of a daemon at the same time
        poll { assert starter.awaitingCreation == 2 }

        when: //complete creation of the daemons
        starter.unblockCreation()

        then:
        poll { assert daemons.size() == 2 }
    }

    def "reuses daemons in concurrent scenario"() {
        def forkOptions = Stub(DaemonForkOptions) { isCompatibleWith(_) >> true }
        def m = new CompilerDaemonManager(new DummyCompilerDaemonStarter())

        when: //2 threads asking for daemons
        Set daemons = new CopyOnWriteArraySet()
        start { daemons << m.getDaemon(project, forkOptions) }
        start { daemons << m.getDaemon(project, forkOptions) }

        then: //both threads are creating an instance of a daemon at the same time
        poll { assert daemons.size() == 2 }

        when: //daemons finished their jobs
        daemons.each { it.idle = true }

        and: //and new daemon requested
        daemons << m.getDaemon(project, forkOptions)

        then: //one of the daemons is reused
        poll {
            assert daemons.size() == 2
            assert daemons.count { it.idle } == 1
        }
    }

    private static class DummyCompilerDaemonStarter extends CompilerDaemonStarter {
        def lock = new ReentrantLock()
        def condition = lock.newCondition()
        def boolean blocked
        def awaitingCreation = 0

        CompilerDaemonClient startDaemon(ProjectInternal project, DaemonForkOptions forkOptions) {
            lock.lock()
            try {
                awaitingCreation++
                while (blocked) {
                    condition.await()
                }
            } finally {
                lock.unlock()
            }
            new CompilerDaemonClient(forkOptions, null, null)
        }

        DummyCompilerDaemonStarter blockCreation() {
            lock.lock()
            try {
                blocked = true
            } finally {
                lock.unlock()
            }
            this
        }

        void unblockCreation() {
            lock.lock()
            try {
                blocked = false
                condition.signalAll()
            } finally {
                lock.unlock()
            }
        }
    }
}

{'fraction_non_alphanumeric': 0.08437571071184899, 'fraction_numerical': 0.004321128041846714, 'mean_word_length': 2.3983771251931993, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3529546', 'n_tokens_mistral': 2493, 'n_tokens_neox': 2392, 'n_words': 715}