Lightweight way to call a swift function as a child process?

i have a tool written in Swift that occasionally traps on precondition failure. changing the tool so that it never traps is not realistic, and is also not desirable. therefore, i would like to run the tool as a child process to allow for cleanup and recovery if the tool crashes.

the tool has a complex interface, so i do not want to write a command line argument parser for it. is there a simpler way to run a Swift function in a child process?

1 Like

This is probably not a good answer, but it would be fun to make a distributed actor provider that did that. The reason my mind went there is because all the structure is there to handle the argument/result serialization and asynchrony.

More practically, however, @grynspan’s experiments with “death tests” might have a way forward. I haven’t looked at the implementation there, though. Add experimental exit test support. by grynspan · Pull Request #307 · apple/swift-testing · GitHub

4 Likes

My experimental implementation just uses Process, so that's probably not what I'd call "lightweight." But I am eager to explore how distributed actors could be used instead for the same purpose.

@ktoso knows far more about what's possible with them than I do.

1 Like

I'd love to try that. A first naive question - does it work on iOS? IIRC before we were limited to one process per app (excluding extensions for things like QuickLook previewing, audio units, sharing, etc).

I was thinking you’d still implement it on top of Process, at least for now, so no.

If the subtask doesn't need to also run as native code, and can run within the limits of the platform, another thing to try might be to build it as wasm and run it in a wasm host. wasm might be an interesting way of building the "in process distributed actor host" concept Jordan alluded to.

4 Likes

Looks are are in need of some new "Actor Application extension" then...

FYI I've actually merged that PR and started a thread in the forums for anybody who wants to try it out and discuss.

1 Like

My first thought was: that’s just a fork. A quick search on ‘fork without exec’ shows that you have to be very careful with it though, but perhaps it’s usable in your situation.

A quick search on ‘fork without exec’ shows that you have to
be very careful with it though

Indeed. IMO you you can’t reliably combine fork without exec* and Swift. This has come up a few times on DevForums, like here and here.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

3 Likes

Any chance that implementation can become its own library? I really need something like that for a project I'm currently working on. Right now I'm using Process which accepts data as a blob of serialised stuff. This is much less ergonomic than using something like your implementation and obviously more bug prone

Any chance that implementation can become its own library?

Why, yes!

The Foundation folks are in the process (hey hey) of creating a replacement for Process. See [Pitch] Swift Subprocess. I don’t think there’s any ETA for that yet, but it’s definitely on the cards.

In the meantime, there are a number of alternative Process wrappers from other third-party developers. I don’t keep track of them — due to the nature of my work, I’m not able to use third-party libraries — so I can’t give you a specific recommendation. Perhaps someone else will chime in…

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like

Subprocess still can't run a swift function inside it though. I'm working on a project where it would be highly beneficial to do just that. For now, I'm using a multicall binary and a bincode-encoded blob of data to pass it all the needed information. This works fine, but feels pretty junky. So an ability to run a function in a subprocess would be incredibly nice to have. See: procspawn — Rust data encoding library // Lib.rs as an example

1 Like

What you're describing sounds very similar to a distributed actor system that is being discussed here -- Runtime extensibility via distributed actors - #2 by ktoso

We'd have a binary that we launch and enter the "child process mode" and communicate with it. You could have a distributed actor encapsulate your (distributed) function and this way invoke it in the child process. It doesn't have to be the same binary, but we could think of such way -- I had old old old prototypes with just that in the distributed actors library from way before we even had actors in the language, for inspiration. We could do this much nicer today with (distributed) actors being part of the language though :slight_smile:

I invite you to join the discussion in the Runtime extensibility via distributed actors thread!

The implementation in swift-testing's proof-of-concept exit tests feature is probably not possible to factor into a library as-is. It depends on some pretty deep/arcane knowledge of the Swift runtime's emitted metadata. @ktoso's distributed actors work is probably closer to a general-purpose solution.

1 Like