-
Notifications
You must be signed in to change notification settings - Fork 31
Create a runPipe that sets up a process pipeline from multiple Configurations #147
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
base: main
Are you sure you want to change the base?
Conversation
Sources/Subprocess/API.swift
Outdated
|
||
return try result.get() | ||
} else { | ||
Task { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be best to use a task group here, rather than unstructured tasks. That way the subtasks will inherit cancellation & priority properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've converted this to use a task group, which collects the results, and failures.
Sources/Subprocess/API.swift
Outdated
} | ||
|
||
if idx == pipeline.count - 1 { | ||
let result = await Task { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The task here looks redundant; this:
let result = await Task { try await (...) }.result
return try result.get()
is equivalent to:
return try await (...)
Sources/Subprocess/API.swift
Outdated
input: Input = .none, | ||
output: Output, | ||
error: Error = .discarded | ||
) async throws -> CollectedResult<Output, Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do shells like bash start every process in a pipeline in parallel regardless of how many there are? Should there be a maxConcurrency parameter to limit it? People probably won't set up 1000+-process pipelines in practice, but maybe there are actually some use cases for that?
Another approach could be an optional parameter like an execution pool, defined like:
protocol ExecutionPool {
func execute<ReturnValue>(_ work: () async throws -> sending ReturnValue) async throws -> ReturnValue
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed this to a variadic function, which should help to reduce the probability of very large process bombs.
Change runPipe into another variant of run using variadic parameters for the configuration
Would a feature like this need a higher level DSL to be able to express all the things you might want to be able to address? Redirecting stderr to stout, discarding intermediate stderr, "pipefail" type behavior where the first failing command exits the pipeline? etc... I think a single runPipe method with a single set of defined semantics might be too limiting to cover common use cases. |
Pipefail is a good point, and something that I can add as a parameter. In terms of individually controlling, or redirecting elements in the pipeline I think that would be difficult to achieve with a single function call. I think that the trouble is that the existing Configuration structure isn't really a configuration in the sense that we're talking about here because it doesn't do anything to specify input/output/error. If it did, then this could become multiple run function calls, and the type checker could verify that the next in the pipe has a standard out fd (redirected, or not) that can be the next function's input. I'll see if I can get an experiment going that yields some ergonomic results. |
The runPipe() function helps to create easy multi-process pipelines in a
similar method as the run() function that accepts a Configuration and
returns a CollectedResult.
TODO: