Use sessions with connection to eliminate async shutdown problems

(cherry picked from commit 78d334b)
This commit is contained in:
Ilya Chernikov
2017-02-18 19:03:41 +03:00
parent 4202bde550
commit 00e8dfe1be
3 changed files with 117 additions and 70 deletions
@@ -30,6 +30,7 @@ import java.io.OutputStream
import java.io.PrintStream
import java.net.SocketException
import java.rmi.ConnectException
import java.rmi.ConnectIOException
import java.rmi.UnmarshalException
import java.rmi.server.UnicastRemoteObject
import java.util.concurrent.Semaphore
@@ -73,52 +74,49 @@ object KotlinCompilerClient {
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean = true
): CompileService? {
): CompileService? =
connectAndLease(compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
reportingTargets,
autostart,
leaseSession = false,
sessionAliveFlagFile = null)?.first
var attempts = 0
fun retryOrRethrow(e: Exception) {
if (attempts < DAEMON_CONNECT_CYCLE_ATTEMPTS) {
reportingTargets.report(DaemonReportCategory.INFO, "retrying($attempts) on:" + e.toString())
fun connectAndLease(compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean,
leaseSession: Boolean,
sessionAliveFlagFile: File? = null
): Pair<CompileService, Int>? = connectLoop(reportingTargets) {
val (service, newJVMOptions) = tryFindSuitableDaemonOrNewOpts(File(daemonOptions.runFilesPath), compilerId, daemonJVMOptions, { cat, msg -> reportingTargets.report(cat, msg) })
if (service != null) {
// the newJVMOptions could be checked here for additional parameters, if needed
service.registerClient(clientAliveFlagFile.absolutePath)
reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon")
if (!leaseSession) service to CompileService.NO_SESSION
else {
val sessionId = service.leaseCompileSession(sessionAliveFlagFile?.absolutePath)
if (sessionId is CompileService.CallResult.Dying)
null
else
service to sessionId.get()
}
else throw e
}
try {
while (attempts < DAEMON_CONNECT_CYCLE_ATTEMPTS) {
try {
attempts += 1
val (service, newJVMOptions) = tryFindSuitableDaemonOrNewOpts(File(daemonOptions.runFilesPath), compilerId, daemonJVMOptions, { cat, msg -> reportingTargets.report(cat, msg) })
if (service != null) {
// the newJVMOptions could be checked here for additional parameters, if needed
service.registerClient(clientAliveFlagFile.absolutePath)
reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon")
return service
}
reportingTargets.report(DaemonReportCategory.DEBUG, "no suitable daemon found")
if (autostart) {
startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets)
reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it")
}
}
catch (e: SocketException) {
retryOrRethrow(e)
}
catch (e: ConnectException) {
retryOrRethrow(e)
}
catch (e: UnmarshalException) {
retryOrRethrow(e)
}
} else {
reportingTargets.report(DaemonReportCategory.DEBUG, "no suitable daemon found")
if (autostart) {
startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets)
reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it")
}
null
}
catch (e: Throwable) {
reportingTargets.report(DaemonReportCategory.EXCEPTION, e.toString())
}
return null
}
fun shutdownCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions): Unit {
connectToCompileService(compilerId, DaemonJVMOptions(), daemonOptions, DaemonReportingTargets(out = System.out), autostart = false, checkId = false)
?.shutdown()
@@ -130,6 +128,13 @@ object KotlinCompilerClient {
}
fun leaseCompileSession(compilerService: CompileService, aliveFlagPath: String?): Int =
compilerService.leaseCompileSession(aliveFlagPath).get()
fun releaseCompileSession(compilerService: CompileService, sessionId: Int): Unit {
compilerService.releaseCompileSession(sessionId)
}
fun compile(compilerService: CompileService,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
@@ -293,6 +298,33 @@ object KotlinCompilerClient {
// --- Implementation ---------------------------------------
private inline fun <R> connectLoop(reportingTargets: DaemonReportingTargets, body: () -> R?): R? {
try {
var attempts = 0
while (attempts < DAEMON_CONNECT_CYCLE_ATTEMPTS) {
attempts += 1
val (res, err) = try {
body() to null
}
catch (e: SocketException) { null to e }
catch (e: ConnectException) { null to e }
catch (e: ConnectIOException) { null to e }
catch (e: UnmarshalException) { null to e }
when {
res != null -> return res
err != null && attempts >= DAEMON_CONNECT_CYCLE_ATTEMPTS -> throw err
err != null ->
reportingTargets.report(DaemonReportCategory.INFO, "retrying($attempts) on: " + err.toString())
}
}
}
catch (e: Throwable) {
reportingTargets.report(DaemonReportCategory.EXCEPTION, e.toString())
}
return null
}
private fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String = "daemon client") {
if (category == DaemonReportCategory.DEBUG && !verboseReporting) return
out?.println("[$source] ${category.name}: $message")