Detect new activity after delayed shutdown correctly

fixes #KT-22549, test

(cherry picked from commit 50f34db)
This commit is contained in:
Ilya Chernikov
2018-01-29 18:31:40 +01:00
parent 98b0f91d84
commit d70b9c1d76
2 changed files with 101 additions and 54 deletions
@@ -137,6 +137,8 @@ class CompileServiceImpl(
private val sessions: MutableMap<Int, ClientOrSessionProxy<Any>> = hashMapOf()
private val sessionsIdCounter = AtomicInteger(0)
val lastSessionId get() = sessionsIdCounter.get()
fun<T: Any> leaseSession(session: ClientOrSessionProxy<T>): Int = lock.write {
val newId = getValidId(sessionsIdCounter) {
it != CompileService.NO_SESSION && !sessions.containsKey(it)
@@ -182,8 +184,13 @@ class CompileServiceImpl(
val aliveClientsCount: Int get() = clientProxies.size
private val _clientsCounter = AtomicInteger(0)
val clientsCounter get() = _clientsCounter.get()
fun addClient(aliveFlagPath: String?) {
clientsLock.write {
_clientsCounter.incrementAndGet()
clientProxies.add(ClientOrSessionProxy(aliveFlagPath))
}
}
@@ -352,70 +359,72 @@ class CompileServiceImpl(
servicesFacade: CompilerServicesFacadeBase,
compilationResults: CompilationResults?
): CompileService.CallResult<Int> = ifAlive {
val messageCollector = CompileServicesFacadeMessageCollector(servicesFacade, compilationOptions)
val daemonReporter = DaemonMessageReporter(servicesFacade, compilationOptions)
val targetPlatform = compilationOptions.targetPlatform
log.info("Starting compilation with args: " + compilerArguments.joinToString(" "))
withValidClientOrSessionProxy(sessionId) {
val messageCollector = CompileServicesFacadeMessageCollector(servicesFacade, compilationOptions)
val daemonReporter = DaemonMessageReporter(servicesFacade, compilationOptions)
val targetPlatform = compilationOptions.targetPlatform
log.info("Starting compilation with args: " + compilerArguments.joinToString(" "))
@Suppress("UNCHECKED_CAST")
val compiler = when (targetPlatform) {
CompileService.TargetPlatform.JVM -> K2JVMCompiler()
CompileService.TargetPlatform.JS -> K2JSCompiler()
CompileService.TargetPlatform.METADATA -> K2MetadataCompiler()
} as CLICompiler<CommonCompilerArguments>
@Suppress("UNCHECKED_CAST")
val compiler = when (targetPlatform) {
CompileService.TargetPlatform.JVM -> K2JVMCompiler()
CompileService.TargetPlatform.JS -> K2JSCompiler()
CompileService.TargetPlatform.METADATA -> K2MetadataCompiler()
} as CLICompiler<CommonCompilerArguments>
val k2PlatformArgs = compiler.createArguments()
parseCommandLineArguments(compilerArguments.asList(), k2PlatformArgs)
val argumentParseError = validateArguments(k2PlatformArgs.errors)
if (argumentParseError != null) {
messageCollector.report(CompilerMessageSeverity.ERROR, argumentParseError)
CompileService.CallResult.Good(ExitCode.COMPILATION_ERROR.code)
}
else when (compilationOptions.compilerMode) {
CompilerMode.JPS_COMPILER -> {
val jpsServicesFacade = servicesFacade as JpsCompilerServicesFacade
withIC(enabled = servicesFacade.hasIncrementalCaches()) {
doCompile(sessionId, daemonReporter, tracer = null) { eventManger, profiler ->
val services = createCompileServices(jpsServicesFacade, eventManger, profiler)
compiler.exec(messageCollector, services, k2PlatformArgs)
}
}
val k2PlatformArgs = compiler.createArguments()
parseCommandLineArguments(compilerArguments.asList(), k2PlatformArgs)
val argumentParseError = validateArguments(k2PlatformArgs.errors)
if (argumentParseError != null) {
messageCollector.report(CompilerMessageSeverity.ERROR, argumentParseError)
CompileService.CallResult.Good(ExitCode.COMPILATION_ERROR.code)
}
CompilerMode.NON_INCREMENTAL_COMPILER -> {
doCompile(sessionId, daemonReporter, tracer = null) { _, _ ->
compiler.exec(messageCollector, Services.EMPTY, k2PlatformArgs)
}
}
CompilerMode.INCREMENTAL_COMPILER -> {
val gradleIncrementalArgs = compilationOptions as IncrementalCompilationOptions
val gradleIncrementalServicesFacade = servicesFacade as IncrementalCompilerServicesFacade
else when (compilationOptions.compilerMode) {
CompilerMode.JPS_COMPILER -> {
val jpsServicesFacade = servicesFacade as JpsCompilerServicesFacade
when (targetPlatform) {
CompileService.TargetPlatform.JVM -> {
val k2jvmArgs = k2PlatformArgs as K2JVMCompilerArguments
withIC {
doCompile(sessionId, daemonReporter, tracer = null) { _, _ ->
execIncrementalCompiler(k2jvmArgs, gradleIncrementalArgs, gradleIncrementalServicesFacade, compilationResults!!,
messageCollector, daemonReporter)
}
withIC(enabled = servicesFacade.hasIncrementalCaches()) {
doCompile(sessionId, daemonReporter, tracer = null) { eventManger, profiler ->
val services = createCompileServices(jpsServicesFacade, eventManger, profiler)
compiler.exec(messageCollector, services, k2PlatformArgs)
}
}
CompileService.TargetPlatform.JS -> {
val k2jsArgs = k2PlatformArgs as K2JSCompilerArguments
}
CompilerMode.NON_INCREMENTAL_COMPILER -> {
doCompile(sessionId, daemonReporter, tracer = null) { _, _ ->
compiler.exec(messageCollector, Services.EMPTY, k2PlatformArgs)
}
}
CompilerMode.INCREMENTAL_COMPILER -> {
val gradleIncrementalArgs = compilationOptions as IncrementalCompilationOptions
val gradleIncrementalServicesFacade = servicesFacade as IncrementalCompilerServicesFacade
withJsIC {
doCompile(sessionId, daemonReporter, tracer = null) { _, _ ->
execJsIncrementalCompiler(k2jsArgs, gradleIncrementalArgs, gradleIncrementalServicesFacade, compilationResults!!, messageCollector)
when (targetPlatform) {
CompileService.TargetPlatform.JVM -> {
val k2jvmArgs = k2PlatformArgs as K2JVMCompilerArguments
withIC {
doCompile(sessionId, daemonReporter, tracer = null) { _, _ ->
execIncrementalCompiler(k2jvmArgs, gradleIncrementalArgs, gradleIncrementalServicesFacade, compilationResults!!,
messageCollector, daemonReporter)
}
}
}
}
else -> throw IllegalStateException("Incremental compilation is not supported for target platform: $targetPlatform")
CompileService.TargetPlatform.JS -> {
val k2jsArgs = k2PlatformArgs as K2JSCompilerArguments
withJsIC {
doCompile(sessionId, daemonReporter, tracer = null) { _, _ ->
execJsIncrementalCompiler(k2jsArgs, gradleIncrementalArgs, gradleIncrementalServicesFacade, compilationResults!!, messageCollector)
}
}
}
else -> throw IllegalStateException("Incremental compilation is not supported for target platform: $targetPlatform")
}
}
else -> throw IllegalStateException("Unknown compilation mode ${compilationOptions.compilerMode}")
}
else -> throw IllegalStateException("Unknown compilation mode ${compilationOptions.compilerMode}")
}
}
@@ -801,18 +810,23 @@ class CompileServiceImpl(
private fun shutdownWithDelay() {
state.delayedShutdownQueued.set(true)
val currentClientsCount = state.clientsCounter
val currentSessionId = state.sessions.lastSessionId
val currentCompilationsCount = compilationsCounter.get()
log.info("Delayed shutdown in ${daemonOptions.shutdownDelayMilliseconds}ms")
timer.schedule(daemonOptions.shutdownDelayMilliseconds) {
state.delayedShutdownQueued.set(false)
if (currentCompilationsCount == compilationsCounter.get()) {
if (currentClientsCount == state.clientsCounter &&
currentCompilationsCount == compilationsCounter.get() &&
currentSessionId == state.sessions.lastSessionId)
{
ifAliveExclusiveUnit(minAliveness = Aliveness.LastSession) {
log.fine("Execute delayed shutdown")
shutdownNow()
}
}
else {
log.info("Cancel delayed shutdown due to new client")
log.info("Cancel delayed shutdown due to a new activity")
}
}
}
@@ -403,6 +403,39 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
}
}
fun testDaemonCancelShutdownOnANewClient() {
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1000, shutdownDelayMilliseconds = 3000, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
val clientFlag = createTempFile(getTestName(true), "-client.alive")
val clientFlag2 = createTempFile(getTestName(true), "-client.alive")
try {
withLogFile("kotlin-daemon-test") { logFile ->
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, clientFlag, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
assertNotNull("failed to connect daemon", daemon)
daemon?.leaseCompileSession(null)
clientFlag.delete()
Thread.sleep(2100) // allow deleted file detection, should be 2 * DAEMON_PERIODIC_CHECK_INTERVAL_MS + o
// TODO: consider possibility to set DAEMON_PERIODIC_CHECK_INTERVAL_MS from tests, to allow shorter sleeps
val daemon2 = KotlinCompilerClient.connectToCompileService(compilerId, clientFlag2, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
assertNotNull("failed to connect daemon", daemon2)
daemon2?.leaseCompileSession(null)
Thread.sleep(3000) // allow to trigger delayed shutdown timer
logFile.assertLogContainsSequence("No more clients left",
"Delayed shutdown in 3000ms",
"Cancel delayed shutdown due to a new activity")
}
}
finally {
clientFlag.delete()
clientFlag2.delete()
}
}
/** Testing that running daemon in the child process doesn't block on s child process.waitFor()
* that may happen on windows if simple processBuilder.start is used due to handles inheritance:
* - process A starts process B using ProcessBuilder and waits for it using process.waitFor()