diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index ec980257735..51cb414c2fc 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -33,7 +33,6 @@ import org.jetbrains.kotlin.config.addKotlinSourceRoots import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.TargetManager import org.jetbrains.kotlin.utils.KotlinPaths -import java.io.File import kotlin.reflect.KFunction class K2Native : CLICompiler() { diff --git a/dependencies/build.gradle b/dependencies/build.gradle index 4d0cb3120ea..073ba9306d5 100644 --- a/dependencies/build.gradle +++ b/dependencies/build.gradle @@ -21,7 +21,6 @@ import groovy.transform.stc.FirstParam import groovy.transform.stc.FromString import org.jetbrains.kotlin.konan.target.* import org.jetbrains.kotlin.konan.util.DependencyProcessor - import static org.jetbrains.kotlin.konan.target.KonanTarget.* import org.jetbrains.kotlin.konan.properties.KonanProperties import org.jetbrains.kotlin.konan.properties.* diff --git a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt index 5e703a65d10..f59d4369f5b 100644 --- a/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt +++ b/shared/src/main/kotlin/org/jetbrains/kotlin/konan/util/DependencyDownloader.kt @@ -6,6 +6,10 @@ import java.net.URL import java.net.URLConnection import java.nio.file.Files import java.nio.file.StandardCopyOption +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.thread +import kotlin.concurrent.withLock class DependencyDownloader( var maxAttempts: Int = DEFAULT_MAX_ATTEMPTS, @@ -21,29 +25,77 @@ class DependencyDownloader( RETURN_EXISTING } + class DownloadingProgress(@Volatile var currentBytes: Long, val totalBytes: Long) { + @Volatile var done: Boolean = false + private set + @Volatile var exception: Throwable? = null + private set + + private val lock = ReentrantLock() + private val condition = lock.newCondition() + + val doneCorrectly: Boolean + get() = done && exception != null + + fun update(readBytes: Int) { currentBytes += readBytes } + + fun done() = lock.withLock { + done = true + condition.signalAll() + } + + fun done(e: Throwable) = lock.withLock { + done = true + exception = e + condition.signalAll() + } + + fun trackProgress(interval: Long, + intervalTimeUnit: TimeUnit = TimeUnit.MILLISECONDS, + action: (progress: DownloadingProgress) -> Unit) = + lock.withLock { + action(this) + while (!done) { + condition.await(interval, intervalTimeUnit) + action(this) + } + } + } + private fun doDownload(originalUrl: URL, connection: URLConnection, tmpFile: File, currentBytes: Long, totalBytes: Long, append: Boolean) { - @Suppress("NAME_SHADOWING") - var currentBytes = currentBytes - connection.getInputStream().use { from -> - FileOutputStream(tmpFile, append).use { to -> - val buffer = ByteArray(DEFAULT_BUFFER_SIZE) - var read = from.read(buffer) - while (read != -1) { - to.write(buffer, 0, read) - currentBytes += read - updateProgressMsg(originalUrl.toString(), currentBytes, totalBytes) - read = from.read(buffer) - } - if (currentBytes != totalBytes) { - throw EOFException("The stream closed before end of downloading.") + val progress = DownloadingProgress(currentBytes, totalBytes) + + // TODO: Implement multi-thread downloading + use some sort of thread pool. + thread { + Thread.setDefaultUncaughtExceptionHandler { _, throwable -> progress.done(throwable) } + connection.getInputStream().use { from -> + FileOutputStream(tmpFile, append).use { to -> + val buffer = ByteArray(DEFAULT_BUFFER_SIZE) + var read = from.read(buffer) + while (read != -1) { + to.write(buffer, 0, read) + progress.update(read) + read = from.read(buffer) + } + if (currentBytes != totalBytes) { + throw EOFException("The stream closed before end of downloading.") + } + progress.done() } } } + + progress.trackProgress(1000) { + updateProgressMsg(originalUrl.toString(), it.currentBytes, it.totalBytes) + } + if (!progress.doneCorrectly) { + throw progress.exception!! + } } private fun tryHttpDownload(originalUrl: URL, connection: HttpURLConnection, tmpFile: File) { @@ -82,7 +134,7 @@ class DependencyDownloader( /** Performs an attempt to download a specified file into the specified location */ private fun tryDownload(url: URL, tmpFile: File) { - var connection = url.openConnection() + val connection = url.openConnection() if (connection is HttpURLConnection) { tryHttpDownload(url, connection, tmpFile) } else { @@ -152,8 +204,6 @@ class DependencyDownloader( print("\rDownloading dependency: $url (${currentBytes.humanReadable}/${totalBytes.humanReadable}). ") } - - companion object { const val DEFAULT_MAX_ATTEMPTS = 10 const val DEFAULT_ATTEMPT_INTERVAL_MS = 3000L