A "zombie" `faked` process never exits when running `fakeroot` from a `Process` only in an async context on linux

I have a program that launches a Process that runs the fakeroot command in order to safely check for package updates without requiring root. However, when I made it into a systemd service, I noticed that it was spawning a lot of faked processes and never cleaning them up. So i began investigating on why this happened. Here is the minimal code that reproduces the issue:

import Foundation

Task.detached {
    let task: Process = Process()
    task.executableURL = URL(fileURLWithPath: "/usr/bin/fakeroot")
    task.arguments = ["echo", "hello"]
    try task.run()
    task.waitUntilExit()
    exit(0)
}

RunLoop.main.run()

Simply compile and run this code and then check running processes using a tool like htop. You'll see a new faked process running, even after the swift program exited. (You can kill the faked process with signal 9, btw) I have not tested if this behavior is reproducable on macOS, i have only tested on linux.

However, this similar code works correctly and there is no "zombie" faked process left running:

import Foundation

let task: Process = Process()
task.executableURL = URL(fileURLWithPath: "/usr/bin/fakeroot")
task.arguments = ["echo", "hello"]
try task.run()
task.waitUntilExit()

Looking into how fakeroot and faked operate, it seems that when the program spawned by fakeroot exits, it sends a SIGTERM signal to faked, which tells it to exit. This is documented in faked.c. Inspecting the faked process using kill -d we can see that the signal was successfully sent by fakeroot, but is blocked by faked when launched from an async context. In fact it seems to block a lot of signals:

Pending (process): TERM
Blocked: HUP INT QUIT ABRT USR1 USR2 ALRM TERM STKFLT CONT TSTP TTIN TTOU URG XCPU XFSZ VTALRM WINCH IO PWR RT0 RT1 RT2 RT3 RT4 RT5 RT6 RT7 RT8 RT9 RT10 RT11 RT12 RT13 RT14 RT15 RT16 RT17 RT18 RT19 RT20 RT21 RT22 RT23 RT24 RT25 RT26 RT27 RT28 RT29 RT30
Caught: HUP INT QUIT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD TTIN TTOU URG XCPU XFSZ VTALRM PROF IO PWR SYS RT0 RT1 RT2 RT3 RT4 RT5 RT6 RT7 RT8 RT9 RT10 RT11 RT12 RT13 RT14 RT15 RT16 RT17 RT18 RT19 RT20 RT21 RT22 RT23 RT24 RT25 RT26 RT27 RT28 RT29 RT30

This is not the case when launched from a sync context, which I managed to record here by making fakeroot run sleep 1000:

Caught: HUP INT QUIT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD TTIN TTOU URG XCPU XFSZ VTALRM PROF IO PWR SYS RT0 RT1 RT2 RT3 RT4 RT5 RT6 RT7 RT8 RT9 RT10 RT11 RT12 RT13 RT14 RT15 RT16 RT17 RT18 RT19 RT20 RT21 RT22 RT23 RT24 RT25 RT26 RT27 RT28 RT29 RT30

This leads me to believe that libdispatch is configuring threads to block those signals. In fact, this code sample also blocks signals:

import Foundation

let timer = DispatchSource.makeTimerSource()
timer.schedule(deadline: .now())

timer.setEventHandler {
    let task: Process = Process()
    task.executableURL = URL(fileURLWithPath: "/usr/bin/fakeroot")
    task.arguments = ["echo", "hello"]
    try! task.run()
    task.waitUntilExit()
    exit(0)
}
timer.activate()

RunLoop.main.run()

So my question is: is there a way to configure libdispatch to not block signals? or is there a better way to handle this?