Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update kmp-process dependency #453

Merged
merged 5 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ gradle-kmp-configuration = "0.2.2"
gradle-kotlin = "1.9.24"
gradle-publish-maven = "0.28.0"

kmp-process = "0.1.0-beta01"
kmp-process = "0.1.0-beta02"
kmp-tor-core = "2.0.0"
kmp-tor-resource = "408.10.0-SNAPSHOT"
kotlinx-coroutines = "1.8.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package io.matthewnelson.kmp.tor.runtime.internal

import io.matthewnelson.kmp.file.File
import io.matthewnelson.kmp.tor.runtime.TorRuntime
import io.matthewnelson.kmp.tor.runtime.internal.process.TorDaemon
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.delay
import kotlin.time.Duration
Expand All @@ -31,10 +30,6 @@ internal expect inline fun TorRuntime.Environment.newRuntimeDispatcher(): Corout
@Throws(Throwable::class)
internal expect fun File.setDirectoryPermissions()

// TODO: https://github.com/05nelsonm/kmp-process/issues/108
@Throws(Throwable::class)
internal expect fun TorDaemon.kill(pid: Int)

// No matter the Delay implementation (Coroutines Test library)
// Will delay the specified duration using a TimeSource.
internal suspend fun timedDelay(duration: Duration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ internal class StartupFeedParser(private val lineLimit: Int = 50, private val ex

if (_lines >= lineLimit) {
if (!_isReady && _error == null) {
// Feed is not closed, nor has tor has output
// Feed is not closed, nor has tor output
// control connection info within the first
// {lineLimit} lines... something is wrong.
@Suppress("ThrowableNotThrown")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import io.matthewnelson.immutable.collections.toImmutableList
import io.matthewnelson.kmp.file.*
import io.matthewnelson.kmp.process.OutputFeed
import io.matthewnelson.kmp.process.Process
import io.matthewnelson.kmp.process.ProcessException.Companion.CTX_FEED_STDERR
import io.matthewnelson.kmp.process.ProcessException.Companion.CTX_FEED_STDOUT
import io.matthewnelson.kmp.process.Signal
import io.matthewnelson.kmp.process.Stdio
import io.matthewnelson.kmp.tor.core.api.ResourceInstaller
Expand All @@ -35,12 +37,12 @@ import io.matthewnelson.kmp.tor.runtime.RuntimeEvent.Notifier.Companion.stdout
import io.matthewnelson.kmp.tor.runtime.RuntimeEvent.Notifier.Companion.w
import io.matthewnelson.kmp.tor.runtime.core.Executable
import io.matthewnelson.kmp.tor.runtime.core.TorConfig
import io.matthewnelson.kmp.tor.runtime.core.UncaughtException
import io.matthewnelson.kmp.tor.runtime.core.address.IPSocketAddress
import io.matthewnelson.kmp.tor.runtime.core.address.IPSocketAddress.Companion.toIPSocketAddressOrNull
import io.matthewnelson.kmp.tor.runtime.core.apply
import io.matthewnelson.kmp.tor.runtime.core.ctrl.TorCmd
import io.matthewnelson.kmp.tor.runtime.ctrl.TorCtrl
import io.matthewnelson.kmp.tor.runtime.internal.*
import io.matthewnelson.kmp.tor.runtime.internal.InstanceKeeper
import io.matthewnelson.kmp.tor.runtime.internal.TorConfigGenerator
import io.matthewnelson.kmp.tor.runtime.internal.process.TorDaemon.StartArgs.Companion.createStartArgs
Expand Down Expand Up @@ -187,6 +189,40 @@ internal class TorDaemon private constructor(
Process.Builder(command = paths.tor.path)
.args(cmdLine)
.environment { putAll(generator.environment.processEnv) }
.onError { e ->
if (e.cause is UncaughtException) {
// OutputFeed line was dispatched to event observers
// and one threw an exception. That exception was
// caught by the UncaughtException.Handler and piped
// to RuntimeEvent.ERROR observers. If the ERROR
// observers threw (or none were subscribed), the
// UncaughtException was thrown. All that occurred
// within the OutputFeed.onOutput lambda, finally making
// its way back here. Throw it to shut things down (and
// potentially crash the app).
throw e.cause
}

val threw = try {
NOTIFIER.e(e)
null
} catch (t: UncaughtException) {
// ERROR observer chose to throw exception
t
}

if (threw == null) return@onError

when (e.context) {
CTX_FEED_STDERR,
CTX_FEED_STDOUT -> throw threw
else -> {
// If the origin of the exception was **not** from
// an OutputFeed, ignore the ERROR observer's wish
// and do not crash things.
}
}
}
.stdin(Stdio.Null)
.stdout(Stdio.Pipe)
.stderr(Stdio.Pipe)
Expand All @@ -200,13 +236,7 @@ internal class TorDaemon private constructor(
NOTIFIER.lce(Lifecycle.Event.OnStart(this@TorDaemon))
NOTIFIER.i(this@TorDaemon, process.toString())

val startupFeed = StartupFeedParser(exitCodeOrNull = {
try {
process.exitCode()
} catch (_: IllegalStateException) {
null
}
})
val startupFeed = StartupFeedParser(exitCodeOrNull = process::exitCodeOrNull)

run {
var notify: Executable.Once?
Expand Down Expand Up @@ -246,26 +276,7 @@ internal class TorDaemon private constructor(
state.stopMark = TimeSource.Monotonic.markNow()

try {
// TODO: https://github.com/05nelsonm/kmp-process/issues/108
// Node.js is awful, especially on Windows...
with(process) {
try {
destroy()
} catch (_: Throwable) {
NOTIFIER.w(this@TorDaemon, "Process.destroy threw exception, attempting to kill.")
}

if (!isAlive) return@with
val pid = pid()
if (pid < 1) return@with

try {
kill(pid)
} catch (t: Throwable) {
NOTIFIER.e(t)
}
}

process.destroy()
NOTIFIER.lce(Lifecycle.Event.OnStop(this@TorDaemon))
} finally {
manager.update(TorState.Daemon.Off)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class ServiceFactoryUnitTest {
@Test
fun givenBinder_whenBindAndOtherInstanceIsNotDestroyed_thenDestroysPriorInstance() = runTest {
val factory = TorRuntime.Builder(env("sf_multi_bind_destroy")) {}
.ensureStoppedOnTestCompletion() as TestFactory
.ensureStoppedOnTestCompletion(errorObserver = false) as TestFactory

// If tor failed to start for some reason with the second bind
// b/c we did not use enqueue via the factory ourselves, then
Expand All @@ -122,7 +122,7 @@ class ServiceFactoryUnitTest {
@Test
fun givenNoStart_whenBindWithoutEnqueue_thenAutoEnqueuesStartJob() = runTest {
val factory = TorRuntime.Builder(env("sf_enqueue_start")) {}
.ensureStoppedOnTestCompletion() as TestFactory
.ensureStoppedOnTestCompletion(errorObserver = false) as TestFactory

// If tor failed to start for some reason with the second bind
// b/c we did not use enqueue via the factory ourselves, then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ import io.matthewnelson.kmp.file.resolve
import io.matthewnelson.kmp.tor.core.api.ResourceInstaller
import io.matthewnelson.kmp.tor.core.api.ResourceInstaller.Paths
import io.matthewnelson.kmp.tor.resource.tor.TorResources
import io.matthewnelson.kmp.tor.runtime.Action
import io.matthewnelson.kmp.tor.runtime.FileID
import io.matthewnelson.kmp.tor.runtime.*
import io.matthewnelson.kmp.tor.runtime.FileID.Companion.fidEllipses
import io.matthewnelson.kmp.tor.runtime.Lifecycle
import io.matthewnelson.kmp.tor.runtime.TorRuntime
import io.matthewnelson.kmp.tor.runtime.core.OnEvent
import io.matthewnelson.kmp.tor.runtime.core.ThisBlock
import io.matthewnelson.kmp.tor.runtime.core.TorConfig
import io.matthewnelson.kmp.tor.runtime.core.UncaughtException
import io.matthewnelson.kmp.tor.runtime.core.key.X25519
import io.matthewnelson.kmp.tor.runtime.core.key.X25519.PrivateKey.Companion.toX25519PrivateKey
import io.matthewnelson.kmp.tor.runtime.core.key.X25519.PublicKey.Companion.toX25519PublicKey
Expand All @@ -56,11 +55,21 @@ object TestUtils {
block = block
).also { it.debug = true }

suspend fun <T: Action.Processor> T.ensureStoppedOnTestCompletion(): T {
suspend fun <T: Action.Processor> T.ensureStoppedOnTestCompletion(
errorObserver: Boolean = true,
): T {
currentCoroutineContext().job.invokeOnCompletion {
enqueue(Action.StopDaemon, {}, {})
}

if (errorObserver && this is TorRuntime) {
val tag = environment().staticTag()
subscribe(RuntimeEvent.ERROR.observer(tag, OnEvent.Executor.Immediate) { t ->
if (t is UncaughtException) throw t
t.printStackTrace()
})
}

withContext(Dispatchers.Default) { delay(100.milliseconds) }

return this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,8 @@
package io.matthewnelson.kmp.tor.runtime.internal

import io.matthewnelson.kmp.tor.runtime.TorRuntime
import io.matthewnelson.kmp.tor.runtime.internal.process.TorDaemon
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers

@Suppress("NOTHING_TO_INLINE")
internal actual inline fun TorRuntime.Environment.newRuntimeDispatcher(): CoroutineDispatcher = Dispatchers.Main

// @Throws(Throwable::class)
@Suppress("ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT")
internal actual fun TorDaemon.kill(pid: Int) {
process_kill(pid, "SIGKILL")
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import io.matthewnelson.kmp.tor.core.resource.OSHost
import io.matthewnelson.kmp.tor.core.resource.OSInfo
import io.matthewnelson.kmp.tor.runtime.FileID.Companion.fidEllipses
import io.matthewnelson.kmp.tor.runtime.TorRuntime
import io.matthewnelson.kmp.tor.runtime.internal.process.TorDaemon
import kotlinx.coroutines.*
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicLong
Expand All @@ -43,9 +42,6 @@ internal actual inline fun TorRuntime.Environment.newRuntimeDispatcher(): Corout
return executor.asCoroutineDispatcher()
}

@Throws(Throwable::class)
internal actual fun TorDaemon.kill(pid: Int) {}

@Throws(Throwable::class)
internal actual fun File.setDirectoryPermissions() {
@OptIn(InternalKmpTorApi::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,15 @@

package io.matthewnelson.kmp.tor.runtime.internal

import io.matthewnelson.kmp.file.errnoToIOException
import io.matthewnelson.kmp.tor.runtime.FileID.Companion.fidEllipses
import io.matthewnelson.kmp.tor.runtime.TorRuntime
import io.matthewnelson.kmp.tor.runtime.internal.process.TorDaemon
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.newSingleThreadContext
import platform.posix.SIGKILL
import platform.posix.errno

@Suppress("NOTHING_TO_INLINE")
internal actual inline fun TorRuntime.Environment.newRuntimeDispatcher(): CoroutineDispatcher {
@OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class)
return newSingleThreadContext("Tor[$fidEllipses]")
}

@Throws(Throwable::class)
internal actual fun TorDaemon.kill(pid: Int) {
if (platform.posix.kill(pid, SIGKILL) == -1) {
@OptIn(ExperimentalForeignApi::class)
throw errnoToIOException(errno)
}
}
Loading