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

Fix bad windows libuv 1.48.0 implementation for Node.js #111

Merged
merged 1 commit into from
Jun 17, 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
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ package io.matthewnelson.kmp.process.internal
/** [docs](https://nodejs.org/api/process.html#processpid) */
@JsName("pid")
internal external val process_pid: Int

/** [docs](https://nodejs.org/api/process.html#processversions) */
@JsName("versions")
internal external val process_versions: dynamic
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ internal open external class stream_Readable {
eventName: String,
listener: Function<R>,
): stream_Readable

internal fun destroy()
}

/** [docs](https://nodejs.org/api/stream.html#class-streamwritable) */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package io.matthewnelson.kmp.process.internal

import io.matthewnelson.kmp.file.File
import io.matthewnelson.kmp.file.errorCodeOrNull
import io.matthewnelson.kmp.process.*

internal class NodeJsProcess internal constructor(
Expand All @@ -39,33 +40,90 @@ internal class NodeJsProcess internal constructor(
INIT,
) {

private var _exitCode: Int? = null

override fun destroy(): Process {
val wasDestroyed = !isDestroyed
isDestroyed = true

if (!jsProcess.killed && isAlive) {
// TODO: check result. check error
jsProcess.kill(destroySignal.name)
@Suppress("UNUSED_VARIABLE")
val error: Throwable? = if (!jsProcess.killed && isAlive) {
try {
jsProcess.kill(destroySignal.name)
null
} catch (t: Throwable) {
val code = t.errorCodeOrNull
var destroySelfIfAlive = false

// https://github.com/05nelsonm/kmp-process/issues/108
run {
if (!IsWindows) return@run
if (code != "EPERM") return@run

val (major, minor, patch) = try {
val split = (process_versions["uv"] as String).split('.')
Triple(split[0].toInt(), split[1].toInt(), split[2].toInt())
} catch (_: Throwable) {
return@run
}

// libuv 1.48.0 bad windows implementation
if (major == 1 && minor == 48 && patch == 0) {
destroySelfIfAlive = true
}
}

if (code == "ESRCH") {
destroySelfIfAlive = true
}

var error: Throwable? = t

if (destroySelfIfAlive && isAlive) {
// Still registering as "alive" w/o exit code, but no process
// found with our PID. Unable to kill b/c no process, it's just
// gone... Self-assign exit code and move on.
_exitCode = destroySignal.code
jsProcess.stdin?.end()
jsProcess.stdout?.destroy()
jsProcess.stderr?.destroy()
error = null
}

error
}
} else {
null
}

if (wasDestroyed) jsProcess.unref()

// TODO: Handle errors Issue #109

return this
}

// @Throws(IllegalStateException::class)
override fun exitCode(): Int {
jsProcess.exitCode?.toInt()?.let { return it }
_exitCode?.let { return it }

jsProcess.exitCode?.toInt()?.let {
_exitCode = it
return it
}

jsProcess.signalCode?.let { signal ->
return try {
val code = try {
Signal.valueOf(signal)
} catch (_: IllegalArgumentException) {
destroySignal
}.code

_exitCode = code
return code
}

throw IllegalStateException("Process hasn't exited")
return _exitCode ?: throw IllegalStateException("Process hasn't exited")
}

override fun pid(): Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,36 @@ internal constructor(
val hasBeenDestroyed = isDestroyed
isDestroyed = true

if (isAlive) {
val s = when (destroySignal) {
@Suppress("UNUSED_VARIABLE")
val killErrno = if (isAlive) {
val sig = when (destroySignal) {
Signal.SIGTERM -> SIGTERM
Signal.SIGKILL -> SIGKILL
}

kill(pid, s)
var errno = if (kill(pid, sig) == -1) {
errno
} else {
null
}

// Always call isAlive whether there was an error
// or not to ensure _exitCode is set.
if (!isAlive) {
errno = null
}

isAlive
errno
} else {
null
}

try {
@Suppress("UNUSED_VARIABLE")
val closeError = try {
handle.close()
} catch (_: IOException) {
// TODO: exception handler
null
} catch (t: IOException) {
t
}

if (!hasBeenDestroyed) {
Expand All @@ -109,6 +124,8 @@ internal constructor(
?.result
}

// TODO: Handle errors Issue #109

this
}

Expand Down
Loading