Skip to content

Commit

Permalink
fix: use existing async executor
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandrosAlexiou committed Jan 24, 2025
1 parent 61619b3 commit 037c9df
Showing 1 changed file with 96 additions and 58 deletions.
154 changes: 96 additions & 58 deletions server/src/main/kotlin/org/javacs/kt/CompilerClassPath.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
package org.javacs.kt

import org.javacs.kt.classpath.ClassPathEntry
import org.javacs.kt.classpath.defaultClassPathResolver
import org.javacs.kt.compiler.Compiler
import org.javacs.kt.database.DatabaseService
import org.javacs.kt.util.AsyncExecutor
import java.io.Closeable
import java.io.File
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.util.concurrent.CompletableFuture
import org.javacs.kt.classpath.ClassPathEntry
import org.javacs.kt.classpath.ClassPathResolver
import org.javacs.kt.classpath.defaultClassPathResolver
import org.javacs.kt.compiler.Compiler
import org.javacs.kt.database.DatabaseService
import org.javacs.kt.util.AsyncExecutor

/**
* Manages the class path (compiled JARs, etc), the Java source path
* and the compiler. Note that Kotlin sources are stored in SourcePath.
* Manages the class path (compiled JARs, etc), the Java source path and the compiler. Note that
* Kotlin sources are stored in SourcePath.
*/
class CompilerClassPath(
private val config: CompilerConfiguration,
private val scriptsConfig: ScriptsConfiguration,
private val codegenConfig: CodegenConfiguration,
private val databaseService: DatabaseService
private val databaseService: DatabaseService,
) : Closeable {
val workspaceRoots = mutableSetOf<Path>()

Expand All @@ -30,14 +31,15 @@ class CompilerClassPath(
val outputDirectory: File = Files.createTempDirectory("klsBuildOutput").toFile()
val javaHome: String? = System.getProperty("java.home", null)

var compiler = Compiler(
javaSourcePath,
classPath.map { it.compiledJar }.toSet(),
buildScriptClassPath,
scriptsConfig,
codegenConfig,
outputDirectory
)
var compiler =
Compiler(
javaSourcePath,
classPath.map { it.compiledJar }.toSet(),
buildScriptClassPath,
scriptsConfig,
codegenConfig,
outputDirectory,
)
private set

private val async = AsyncExecutor()
Expand All @@ -50,65 +52,94 @@ class CompilerClassPath(
private fun refresh(
updateClassPath: Boolean = true,
updateBuildScriptClassPath: Boolean = true,
updateJavaSourcePath: Boolean = true
updateJavaSourcePath: Boolean = true,
): Boolean {
val resolver = defaultClassPathResolver(workspaceRoots, databaseService.db)
var refreshCompiler = updateJavaSourcePath
val asyncExecutor = AsyncExecutor()

val classPathFuture = if (updateClassPath) {
asyncExecutor.compute {
val newClassPath = resolver.classpathOrEmpty
if (newClassPath != classPath) {
synchronized(classPath) {
syncPaths(classPath, newClassPath, "class path") { it.compiledJar }
}
refreshCompiler = true
}

asyncExecutor.compute {
val newClassPathWithSources = resolver.classpathWithSources
synchronized(classPath) {
syncPaths(classPath, newClassPathWithSources, "class path with sources") { it.compiledJar }
}
}
val classPathFuture =
if (updateClassPath) {
updateClassPathAsync(resolver)
} else {
CompletableFuture.completedFuture(false)
}
} else {
CompletableFuture.completedFuture(null)
}

val buildScriptClassPathFuture = if (updateBuildScriptClassPath) {
asyncExecutor.compute {
LOG.info("Update build script path")
val newBuildScriptClassPath = resolver.buildScriptClasspathOrEmpty
if (newBuildScriptClassPath != buildScriptClassPath) {
syncPaths(buildScriptClassPath, newBuildScriptClassPath, "build script class path") { it }
refreshCompiler = true
}
val buildScriptClassPathFuture =
if (updateBuildScriptClassPath) {
updateBuildScriptClassPathAsync(resolver)
} else {
CompletableFuture.completedFuture(false)
}
} else {
CompletableFuture.completedFuture(null)
}

// Wait for both futures to complete
CompletableFuture.allOf(classPathFuture, buildScriptClassPathFuture).join()

// Update refreshCompiler based on the results of the futures
refreshCompiler =
refreshCompiler || classPathFuture.get() || buildScriptClassPathFuture.get()

if (refreshCompiler) {
LOG.info("Reinstantiating compiler")
compiler.close()
compiler = Compiler(
javaSourcePath,
classPath.map { it.compiledJar }.toSet(),
buildScriptClassPath,
scriptsConfig,
codegenConfig,
outputDirectory
)
compiler =
Compiler(
javaSourcePath,
classPath.map { it.compiledJar }.toSet(),
buildScriptClassPath,
scriptsConfig,
codegenConfig,
outputDirectory,
)
updateCompilerConfiguration()
}

return refreshCompiler
}

private fun updateClassPathAsync(resolver: ClassPathResolver): CompletableFuture<Boolean> {
return async.compute {
var updated = false
val newClassPath = resolver.classpathOrEmpty
if (newClassPath != classPath) {
synchronized(classPath) {
syncPaths(classPath, newClassPath, "class path") { it.compiledJar }
}
updated = true
}

val newClassPathWithSources = resolver.classpathWithSources
synchronized(classPath) {
syncPaths(classPath, newClassPathWithSources, "class path with sources") {
it.compiledJar
}
}

updated
}
}

private fun updateBuildScriptClassPathAsync(
resolver: ClassPathResolver
): CompletableFuture<Boolean> {
return async.compute {
var updated = false
LOG.info("Update build script path")
val newBuildScriptClassPath = resolver.buildScriptClasspathOrEmpty
if (newBuildScriptClassPath != buildScriptClassPath) {
synchronized(buildScriptClassPath) {
syncPaths(
buildScriptClassPath,
newBuildScriptClassPath,
"build script class path",
) {
it
}
}
updated = true
}
updated
}
}

/** Synchronizes the given two path sets and logs the differences. */
private fun <T> syncPaths(dest: MutableSet<T>, new: Set<T>, name: String, toPath: (T) -> Path) {
Expand Down Expand Up @@ -162,15 +193,22 @@ class CompilerClassPath(
val buildScript = isBuildScript(file)
val javaSource = isJavaSource(file)
if (buildScript || javaSource) {
return refresh(updateClassPath = buildScript, updateBuildScriptClassPath = false, updateJavaSourcePath = javaSource)
return refresh(
updateClassPath = buildScript,
updateBuildScriptClassPath = false,
updateJavaSourcePath = javaSource,
)
} else {
return false
}
}

private fun isJavaSource(file: Path): Boolean = file.fileName.toString().endsWith(".java")

private fun isBuildScript(file: Path): Boolean = file.fileName.toString().let { it == "pom.xml" || it == "build.gradle" || it == "build.gradle.kts" }
private fun isBuildScript(file: Path): Boolean =
file.fileName.toString().let {
it == "pom.xml" || it == "build.gradle" || it == "build.gradle.kts"
}

private fun findJavaSourceFiles(root: Path): Set<Path> {
val sourceMatcher = FileSystems.getDefault().getPathMatcher("glob:*.java")
Expand Down

0 comments on commit 037c9df

Please sign in to comment.