Skip to content

Process[IO] that spawns child processes might not respect cancellation #3693

Description

@fredshonorio

The jvm implementation of Process[IO] in linux might not respect cancellation if the main process does not itself kill the child processes.

A simple example, this should not respect the cancel and take +10 seconds to finish:

//> using scala "2.13.16"
//> using dep "co.fs2::fs2-io:3.12.2"
import cats.effect.{IO, IOApp}
import fs2.io.process.ProcessBuilder
import scala.concurrent.duration._
object Main extends IOApp.Simple {
  def run: IO[Unit] =
    ProcessBuilder("bash", List("-c", "sleep 10 & sleep 10")).spawn[IO].use { proc =>
      proc.stdout.merge(proc.stderr).compile.drain.start.flatTap(_ => IO.sleep(200.millis)).flatMap(_.cancel)
    }
}

This one will stop immediately:

//> using scala "2.13.16"
//> using dep "co.fs2::fs2-io:3.12.2"
import cats.effect.{IO, IOApp}
import fs2.io.process.ProcessBuilder
import scala.concurrent.duration._
object Main extends IOApp.Simple {
  def run: IO[Unit] =
    ProcessBuilder("sleep", List("10")).spawn[IO].use { proc =>
      proc.stdout.merge(proc.stderr).compile.drain.start.flatMap(_.cancel)
    }
}

⚠️ what follows includes LLM "facts", words are mine ⚠️

To fix the real case where this happened to me, I ended up using a jdk Process and calling destroyForcibly on all descendants. This apparently is subject to a race condition where processes could be spawned while the loop is happening.
It seems the correct approach (in linux) would be to use process groups, then fs2 would simply kill the whole group. This is apparently not supported by the jvm, so it would require jni.

At the very least I think this needs a mention in the docs, I will PR that soon unless someone proposes a solution.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions