Merge pull request #766 from JetBrains/rr/daemon-fixes

daemon fixes
This commit is contained in:
Ilya Chernikov
2015-10-14 12:21:51 +02:00
8 changed files with 34 additions and 147 deletions
@@ -25,23 +25,15 @@ import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.rmi.CompilerCallbackServicesFacade
import org.jetbrains.kotlin.rmi.LoopbackNetworkInterface
import org.jetbrains.kotlin.rmi.SOCKET_ANY_FREE_PORT
import java.rmi.server.UnicastRemoteObject
public class CompilerCallbackServicesFacadeServer(
val incrementalCompilationComponents: IncrementalCompilationComponents? = null,
val compilationCancelledStatus: CompilationCanceledStatus? = null,
port: Int = SOCKET_ANY_FREE_PORT
) : CompilerCallbackServicesFacade {
init {
UnicastRemoteObject.exportObject(this, port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
}
public fun disconnect() {
UnicastRemoteObject.unexportObject(this, true)
}
) : CompilerCallbackServicesFacade,
java.rmi.server.UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
{
override fun hasIncrementalCaches(): Boolean = incrementalCompilationComponents != null
override fun hasLookupTracker(): Boolean = incrementalCompilationComponents != null
@@ -114,14 +114,7 @@ public object KotlinCompilerClient {
public fun compile(compilerService: CompileService, targetPlatform: CompileService.TargetPlatform, args: Array<out String>, out: OutputStream): Int {
val outStrm = RemoteOutputStreamServer(out)
val servicesFacade = CompilerCallbackServicesFacadeServer()
try {
return compilerService.remoteCompile(targetPlatform, args, servicesFacade, outStrm, CompileService.OutputFormat.PLAIN, outStrm)
}
finally {
servicesFacade.disconnect()
outStrm.disconnect()
}
return compilerService.remoteCompile(targetPlatform, args, CompilerCallbackServicesFacadeServer(), outStrm, CompileService.OutputFormat.PLAIN, outStrm)
}
@@ -132,22 +125,15 @@ public object KotlinCompilerClient {
compilerOut: OutputStream,
daemonOut: OutputStream,
profiler: Profiler = DummyProfiler()
): Int {
val compilerOutStreamServer = RemoteOutputStreamServer(compilerOut)
val daemonOutStreamServer = RemoteOutputStreamServer(daemonOut)
val servicesFacade = CompilerCallbackServicesFacadeServer(incrementalCompilationComponents = callbackServices.incrementalCompilationComponents,
compilationCancelledStatus = callbackServices.compilationCanceledStatus)
try {
return profiler.withMeasure(this) {
compileService.remoteIncrementalCompile(targetPlatform, args, servicesFacade, compilerOutStreamServer, CompileService.OutputFormat.XML, daemonOutStreamServer)
}
}
finally {
servicesFacade.disconnect()
compilerOutStreamServer.disconnect()
daemonOutStreamServer.disconnect()
}
): Int = profiler.withMeasure(this) {
compileService.remoteIncrementalCompile(
targetPlatform,
args,
CompilerCallbackServicesFacadeServer(incrementalCompilationComponents = callbackServices.incrementalCompilationComponents,
compilationCancelledStatus = callbackServices.compilationCanceledStatus),
RemoteOutputStreamServer(compilerOut),
CompileService.OutputFormat.XML,
RemoteOutputStreamServer(daemonOut))
}
public val COMPILE_DAEMON_CLIENT_OPTIONS_PROPERTY: String = "kotlin.daemon.client.options"
@@ -230,8 +216,9 @@ public object KotlinCompilerClient {
println("Used memory $memAfter (${"%+d".format(memAfter - memBefore)} kb)")
}
finally {
servicesFacade.disconnect()
outStrm.disconnect()
// forcing RMI to unregister all objects and stop
java.rmi.server.UnicastRemoteObject.unexportObject(servicesFacade, true)
java.rmi.server.UnicastRemoteObject.unexportObject(outStrm, true)
}
}
}
@@ -284,7 +271,7 @@ public object KotlinCompilerClient {
}
.filterNotNull()
.toList()
return when (daemons.size()) {
return when (daemons.size) {
0 -> null
1 -> daemons.first()
else -> throw IllegalStateException("Multiple daemons serving the same compiler, reset with the cleanup required")
@@ -311,13 +298,8 @@ public object KotlinCompilerClient {
private fun startDaemon(compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, reportingTargets: DaemonReportingTargets) {
val javaExecutable = File(System.getProperty("java.home"), "bin").let {
val javaw = File(it, "javaw.exe")
// TODO: doesn't seem reliable enough, consider more checks if OS is of windows flavor, etc.
if (javaw.exists() && javaw.isFile && javaw.canExecute()) javaw else File(it, "java")
}
// TODO add os detection to specify option more precisely
val platformSpecificOptions = listOf("-Djava.awt.headless=true") // hide daemon in OS X
val javaExecutable = File(File(System.getProperty("java.home"), "bin"), "java")
val platformSpecificOptions = listOf("-Djava.awt.headless=true") // hide daemon window
val args = listOf(
javaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) +
platformSpecificOptions +
@@ -20,19 +20,12 @@ import org.jetbrains.kotlin.rmi.LoopbackNetworkInterface
import org.jetbrains.kotlin.rmi.RemoteOutputStream
import org.jetbrains.kotlin.rmi.SOCKET_ANY_FREE_PORT
import java.io.OutputStream
import java.rmi.server.UnicastRemoteObject
class RemoteOutputStreamServer(val out: OutputStream, port: Int = SOCKET_ANY_FREE_PORT) : RemoteOutputStream {
init {
UnicastRemoteObject.exportObject(this, port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
}
public fun disconnect() {
UnicastRemoteObject.unexportObject(this, true)
}
class RemoteOutputStreamServer(val out: OutputStream, port: Int = SOCKET_ANY_FREE_PORT)
: RemoteOutputStream,
java.rmi.server.UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
{
override fun close() {
out.close()
}
@@ -50,15 +50,19 @@ public object LoopbackNetworkInterface {
}
}
// base socket factories by default don't implement equals properly (see e.g. http://stackoverflow.com/questions/21555710/rmi-and-jmx-socket-factories)
// so implementing it in derived classes using the fact that they are singletons
data class ServerLoopbackSocketFactory : RMIServerSocketFactory, Serializable {
class ServerLoopbackSocketFactory : RMIServerSocketFactory, Serializable {
override fun equals(other: Any?): Boolean = other === this || super.equals(other)
@Throws(IOException::class)
override fun createServerSocket(port: Int): ServerSocket = ServerSocket(port, SERVER_SOCKET_BACKLOG_SIZE, InetAddress.getByName(loopbackInetAddressName))
}
data class ClientLoopbackSocketFactory : RMIClientSocketFactory, Serializable {
class ClientLoopbackSocketFactory : RMIClientSocketFactory, Serializable {
override fun equals(other: Any?): Boolean = other === this || super.equals(other)
@Throws(IOException::class)
override fun createSocket(host: String, port: Int): Socket = Socket(InetAddress.getByName(loopbackInetAddressName), port)
@@ -209,11 +209,4 @@ class CompileServiceImpl(
if (!alive) throw IllegalStateException("Kotlin Compiler Service is not in alive state")
body()
}
// sometimes used for debugging
fun<R> spy(msg: String, body: () -> R): R {
val res = body()
log.info(msg + " = " + res.toString())
return res
}
}
@@ -1,79 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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.jetbrains.kotlin.rmi.service
import java.io.FilePermission
import java.net.SocketPermission
import java.security.CodeSource
import java.security.Permission
import java.security.PermissionCollection
import java.security.Policy
import java.util.ArrayList
import java.util.Collections
import java.util.Enumeration
import java.util.PropertyPermission
public class DaemonPolicy(val port: Int) : Policy() {
private fun createPermissions(): PermissionCollection {
val perms = DaemonPermissionCollection()
val socketPermission = SocketPermission("localhost:$port-", "connect, accept, resolve")
val propertyPermission = PropertyPermission("localhost:$port", "read")
//val filePermission = FilePermission("<<ALL FILES>>", "read")
perms.add(socketPermission)
perms.add(propertyPermission)
//perms.add(filePermission)
return perms
}
private val perms: PermissionCollection by lazy { createPermissions() }
override fun getPermissions(codesource: CodeSource?): PermissionCollection {
return perms
}
}
class DaemonPermissionCollection : PermissionCollection() {
val perms = ArrayList<Permission>()
override fun add(p: Permission) {
perms.add(p)
}
override fun implies(p: Permission): Boolean = perms.any { implies(p) }
override fun elements(): Enumeration<Permission> = Collections.enumeration(perms)
override fun isReadOnly(): Boolean = false
//
// companion object {
//
// private val serialVersionUID = 614300921365729272L
// }
}
public fun setDaemonPpermissions(port: Int) {
Policy.setPolicy(DaemonPolicy(port))
}
@@ -200,6 +200,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() {
val clientAliveFile = createTempFile("kotlin-daemon-transitive-run-test", ".run")
val runFilesPath = File(tmpdir, getTestName(true)).absolutePath
val daemonOptions = DaemonOptions(runFilesPath = runFilesPath, clientAliveFlagPath = clientAliveFile.absolutePath)
val jar = tmpdir.absolutePath + File.separator + "hello.jar"
val args = listOf(
File(File(System.getProperty("java.home"), "bin"), "java").absolutePath,
"-D$COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY",
@@ -208,7 +209,8 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() {
KotlinCompilerClient::class.qualifiedName!!) +
daemonOptions.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) } +
compilerId.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) } +
File(getHelloAppBaseDir(), "hello.kt").absolutePath
File(getHelloAppBaseDir(), "hello.kt").absolutePath +
"-d" + jar
try {
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
var resOutput: String? = null
@@ -128,9 +128,9 @@ public object KotlinCompilerRunner {
internal class DaemonConnection(public val daemon: CompileService?)
internal object getDaemonConnection {
private var connection: DaemonConnection? = null
private @Volatile var connection: DaemonConnection? = null
operator fun invoke(environment: CompilerEnvironment, messageCollector: MessageCollector): DaemonConnection? {
@Synchronized operator fun invoke(environment: CompilerEnvironment, messageCollector: MessageCollector): DaemonConnection? {
if (connection == null) {
val libPath = CompilerRunnerUtil.getLibPath(environment.kotlinPaths, messageCollector)
val compilerId = CompilerId.makeCompilerId(File(libPath, "kotlin-compiler.jar"))