Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

/development/std/process/pipe_darwin.nct

pipe_darwin.nct

//! Owning Darwin pipe endpoints.
//!
//! This source is the sole owner of close-on-exec pipe creation, descriptor normalization, and
//! endpoint closure. Consumers receive one owner rather than unrelated raw descriptor integers.

see ./index.nct

use /internal/os/darwin.SyscallPairResult
use /internal/os/darwin.SyscallResult
use /internal/os/darwin

#target: "arm64-darwin"
struct OwnedPipe {
    read_fd: usize
    write_fd: usize
    read_open: bool
    write_open: bool
}

#target: "arm64-darwin"
enum PipeAttempt {
    ready(pipe: OwnedPipe)
    failed(errno: i32)
}

#target: "arm64-darwin"
enum DescriptorInstallAttempt {
    ready
    failed(errno: i32)
    invalid
}

#target: "arm64-darwin"
noalloc func duplicate_cloexec(fd: usize): SyscallResult {
    var result = darwin.syscall3(
        darwin.SYS_FCNTL,
        fd,
        darwin.F_DUPFD_CLOEXEC,
        darwin.FIRST_PRIVATE_FD,
    )
    while result.errno == darwin.ERRNO_INTERRUPTED {
        result = darwin.syscall3(
            darwin.SYS_FCNTL,
            fd,
            darwin.F_DUPFD_CLOEXEC,
            darwin.FIRST_PRIVATE_FD,
        )
    }
    return result
}

#target: "arm64-darwin"
noalloc func close_raw_fd(fd: usize): void {
    // A failed close is never retried. Darwin may already have consumed the descriptor, and a
    // retry could close an unrelated descriptor allocated in the meantime.
    let _ = darwin.syscall1(darwin.SYS_CLOSE, fd)
    return
}

#target: "arm64-darwin"
noalloc func create_cloexec_pipe(): PipeAttempt {
    let raw: SyscallPairResult = darwin.syscall_pair0(darwin.SYS_PIPE)
    if raw.errno != 0 { return PipeAttempt.failed(raw.errno) }

    let normalized_read = duplicate_cloexec(raw.first)
    if normalized_read.errno != 0 {
        close_raw_fd(raw.first)
        close_raw_fd(raw.second)
        return PipeAttempt.failed(normalized_read.errno)
    }

    let normalized_write = duplicate_cloexec(raw.second)
    if normalized_write.errno != 0 {
        close_raw_fd(normalized_read.value)
        close_raw_fd(raw.first)
        close_raw_fd(raw.second)
        return PipeAttempt.failed(normalized_write.errno)
    }

    close_raw_fd(raw.first)
    close_raw_fd(raw.second)
    return PipeAttempt.ready(OwnedPipe {
        read_fd: normalized_read.value,
        write_fd: normalized_write.value,
        read_open: true,
        write_open: true,
    })
}

#target: "arm64-darwin"
noalloc func pipe_read_fd(pipe: &OwnedPipe): usize {
    return pipe.read_fd
}

#target: "arm64-darwin"
noalloc func pipe_write_fd(pipe: &OwnedPipe): usize {
    return pipe.write_fd
}

#target: "arm64-darwin"
noalloc func pipe_read_is_open(pipe: &OwnedPipe): bool {
    return pipe.read_open
}

#target: "arm64-darwin"
noalloc func close_pipe_read(pipe: &+OwnedPipe): void {
    if pipe.read_open {
        close_raw_fd(pipe.read_fd)
        pipe.read_open = false
    }
    return
}

#target: "arm64-darwin"
noalloc func close_pipe_write(pipe: &+OwnedPipe): void {
    if pipe.write_open {
        close_raw_fd(pipe.write_fd)
        pipe.write_open = false
    }
    return
}

#target: "arm64-darwin"
noalloc func install_descriptor(source_fd: usize, target_fd: usize): DescriptorInstallAttempt {
    var result = darwin.syscall2(darwin.SYS_DUP2, source_fd, target_fd)
    while result.errno == darwin.ERRNO_INTERRUPTED {
        result = darwin.syscall2(darwin.SYS_DUP2, source_fd, target_fd)
    }
    if result.errno != 0 { return DescriptorInstallAttempt.failed(result.errno) }
    if result.value != target_fd { return DescriptorInstallAttempt.invalid }
    return DescriptorInstallAttempt.ready
}

noalloc drop OwnedPipe(&+self) {
    close_pipe_read(self)
    close_pipe_write(self)
    return
}