Under heavy development. This site describes the intended 1.0 language. Current features and performance vary. Development status
Sn50SnThe SnAPI programming language
Development · Evolving reference

RunProcessWithStreamsAsync

Consumes command and stream owners for bounded blocking execution. Keeps every owner in the task frame through dispatch, native operation and cleanup. Returns streams on failure so captured output remains inspectable. No stream aliases are formed before dispatch suspension completes.

Sn is under heavy development. This reference includes the intended 1.0 design; features may be incomplete in the current compiler. Read the status →

Module std.process

import std.process;

Public declaration contracts and authored examples. A page can document several related symbols. Implementation bodies are excluded.

RunProcessAsync

pub async fn RunProcessAsync<I, O, E>(mut Command: ProcessCommand, mut Input: I, mut Output: O, mut Errors: E, Limit: Timeout, Token: CancellationToken) -> Task<ProcessStreamCompletion<I, O, E>>
    where I: Movable, Reader where O: Movable, Writer where E: Movable, Writer

Consumes command and stream owners for bounded blocking execution. Keeps every owner in the task frame through dispatch, native operation and cleanup. Returns streams on failure so captured output remains inspectable. No stream aliases are formed before dispatch suspension completes. I: Movable input Reader owned through completion. O: Movable stdout Writer owned through completion. E: Movable stderr Writer owned through completion. Command: Consumed explicit native launch inputs. Input: Consumed stdin owner; zero successful read means EOF. Output: Consumed stdout owner returned with captured state. Errors: Consumed stderr owner returned independently. Limit: Native execution allowance, separate from queue admission time. Token: Owned cooperative cancellation observation. Return: Unique task owning the outcome and all final stream owners. Warning: Adapters may block on the admitted worker; cancellation cannot interrupt adapter bodies. Compiler thread-transfer checks still apply.

Example

module example.async_process_streams;
from std.process import Arguments, Environment, ProgramInputs, ProcessCommand, RunProcessAsync, ProcessStreamCompletion, ProcessError, ProcessStreamError;
from std.fs import Path;
from std.text import NativeString;
from std.prelude import Result;
from std.io import IoError;
from std.time import Timeout, Seconds;
from std.task import Task, CancellationSource;
struct InputStream {
    pub Count: usize;
    pub init() { this.Count = 0; }
    pub fn Read(Output: mut byte[]) -> Result<usize, IoError> {
        if this.Count == 3 || Output.Length == 0 { return .Ok { Value = 0 }; }
        Output[0] = (0x61_u8 + (this.Count as u8)) as byte;
        this.Count += 1; return .Ok { Value = 1 };
    }
}
struct OutputStream {
    pub Bytes: byte[8]; pub Count: usize; pub Fail: bool;
    pub init() { this.Bytes = default(byte[8]); this.Count = 0; this.Fail = false; }
    pub fn Write(Input: readonly byte[]) -> Result<usize, IoError> {
        if Input.Length == 0 { return .Ok { Value = 0 }; }
        if this.Fail && this.Count == 1 { return .Err { Error = .Failure { Reason = .Interrupted, Transferred = 0 } }; }
        if this.Count == 8 { return .Err { Error = .OutputFailed }; }
        this.Bytes[this.Count] = Input[0]; this.Count += 1; return .Ok { Value = 1 };
    }
    pub fn Flush() -> Result<void, IoError> { return .Ok; }
}
fn CreateCommand() -> Result<ProcessCommand, ProcessError> {
    var Executable = {
        // Scoped writable carriers end immediately after explicit payload transfer.
        var ExecutableCreation = Path.TryCreate("/bin/sh");
        switch ExecutableCreation {
            case .Ok { Value = Value: mut Path& } => break move(Value);
            case .Err => return .Err { Error = ProcessError { Reason = .OutOfMemory, NativeCode = 0 } };
        }
    };
    var Directory = {
        var DirectoryCreation = Path.TryCreate("/");
        switch DirectoryCreation {
            case .Ok { Value = Value: mut Path& } => break move(Value);
            case .Err => return .Err { Error = ProcessError { Reason = .OutOfMemory, NativeCode = 0 } };
        }
    };
    var Values = Arguments();
    let CommandArguments: str[3] = ["/bin/sh", "-c", "printf err >&2; /bin/cat; exit 7"];
    for Text in CommandArguments {
        var Argument = {
            var ValueCreation = NativeString.TryCreate(Text);
            switch ValueCreation {
                case .Ok { Value = Value: mut NativeString& } => break move(Value);
                case .Err => return .Err { Error = ProcessError { Reason = .OutOfMemory, NativeCode = 0 } };
            }
        };
        if Values.TryPush(move(Argument)) is .Err { Error = _ } { return .Err { Error = ProcessError { Reason = .OutOfMemory, NativeCode = 0 } }; }
    }
    return .Ok { Value = ProcessCommand(move(Executable), move(Directory), ProgramInputs(move(Values), Environment())) };
}
async fn Main() -> Task<i32> {
    var Command = {
        var CommandCreation = CreateCommand();
        switch CommandCreation {
            case .Ok { Value = Value: mut ProcessCommand& } => break move(Value);
            case .Err => return 1;
        }
    };
    let Limit = switch Timeout.TryFromDuration(Seconds.Create(2)) { case .Ok { Value = Value } => break Value; case .Err => return 2; };
    var Completed = await RunProcessAsync(move(Command), InputStream(), OutputStream(), OutputStream(), Limit);
    let Readonly: readonly ProcessStreamCompletion<InputStream, OutputStream, OutputStream>& = Completed;
    switch Readonly.Outcome {
        case .Ok { Value = PatternPayload5143 } => {
            switch PatternPayload5143 {
                case .Exited { Code = PatternPayload5144 } if (PatternPayload5144 == 7) => {}
                default => return 3;
            }
        }
        default => return 3;
    }
    if Readonly.Input.Count != 3 || Readonly.Output.Count != 3 || Readonly.Errors.Count != 3 { return 4; }
    if Readonly.Output.Bytes[0] != 0x61_byte || Readonly.Errors.Bytes[0] != 0x65_byte { return 5; }
    Completed.Output.Count = 0;
    var Retained = move(Completed);
    if Retained.Output.Count != 0 || Retained.Input.Count != 3 { return 6; }
    var NextCommand = {
        var NextCommandCreation = CreateCommand();
        switch NextCommandCreation {
            case .Ok { Value = Value: mut ProcessCommand& } => break move(Value);
            case .Err => return 7;
        }
    };
    var Cancellation = {
        var CancellationCreation = CancellationSource.TryCreate();
        switch CancellationCreation {
            case .Ok { Value = Value: mut CancellationSource& } => break move(Value);
            case .Err => return 8;
        }
    };
    if !Cancellation.RequestCancellation() { return 9; }
    var Cancelled = await RunProcessAsync(move(NextCommand), InputStream(), OutputStream(), OutputStream(), Limit, Cancellation.Token);
    switch Cancelled.Outcome {
        case .Err { Error = PatternPayload5145 } => {
            switch PatternPayload5145 {
                case .Process { Error = Error } => { if Error.Reason != .Cancelled { return 10; } }
                default => return 11;
            }
        }
        default => return 11;
    }
    if Cancelled.Input.Count != 0 || Cancelled.Output.Count != 0 || Cancelled.Errors.Count != 0 { return 12; }
    var FailedCommand = {
        var FailedCommandCreation = CreateCommand();
        switch FailedCommandCreation {
            case .Ok { Value = Value: mut ProcessCommand& } => break move(Value);
            case .Err => return 13;
        }
    };
    var FailingOutput = OutputStream(); FailingOutput.Fail = true;
    var Failed = await RunProcessAsync(move(FailedCommand), InputStream(), move(FailingOutput), OutputStream(), Limit);
    switch Failed.Outcome {
        case .Err { Error = PatternPayload5146 } => {
            switch PatternPayload5146 {
                case .Output { Error = PatternPayload5147 } => {
                    switch PatternPayload5147 {
                        case .Failure { Reason = PatternPayload5148, Transferred = Count } if (PatternPayload5148 is .Interrupted) => { if Count > 1 { return 14; } }
                        default => return 15;
                    }
                }
                default => return 15;
            }
        }
        default => return 15;
    }
    if Failed.Output.Count != 1 || Failed.Output.Bytes[0] != 0x61_byte { return 16; }
    Retained = move(Failed);
    if Retained.Output.Count != 1 { return 17; }
    return 0;
}

RunProcessAsync

pub fn RunProcessAsync<I, O, E>(mut Command: ProcessCommand, mut Input: I, mut Output: O, mut Errors: E) -> Task<ProcessStreamCompletion<I, O, E>>
    where I: Movable, Reader where O: Movable, Writer where E: Movable, Writer

Supplies the omitted arguments to RunProcessAsync. Note: Defaults: Limit = .Endless, Token = CancellationToken(). I: Same generic argument and constraints as the full operation. O: Same generic argument and constraints as the full operation. E: Same generic argument and constraints as the full operation. Command: Same input, ownership and lifetime contract as the full operation. Input: Same input, ownership and lifetime contract as the full operation. Output: Same input, ownership and lifetime contract as the full operation. Errors: Same input, ownership and lifetime contract as the full operation. Return: Exact full-operation result; no additional async frame is created.

RunProcessAsync

pub fn RunProcessAsync<I, O, E>(mut Command: ProcessCommand, mut Input: I, mut Output: O, mut Errors: E, mut Limit: Timeout) -> Task<ProcessStreamCompletion<I, O, E>>
    where I: Movable, Reader where O: Movable, Writer where E: Movable, Writer

Supplies the omitted arguments to RunProcessAsync. Note: Defaults: Token = CancellationToken(). I: Same generic argument and constraints as the full operation. O: Same generic argument and constraints as the full operation. E: Same generic argument and constraints as the full operation. Command: Same input, ownership and lifetime contract as the full operation. Input: Same input, ownership and lifetime contract as the full operation. Output: Same input, ownership and lifetime contract as the full operation. Errors: Same input, ownership and lifetime contract as the full operation. Limit: Same input, ownership and lifetime contract as the full operation. Return: Exact full-operation result; no additional async frame is created.

Documentation snapshot: October 24, 2026 About versions ↗
Searching Development · language & standard library