Processes and threads
Processes and threads
import std::process;
import std::threads;Spawning a process
[public] fn spawn(program: string, args: Array<string>) => Process?
[public] fn spawn_in(program: string, args: Array<string>, cwd: string) => Process?
[public] fn run(program: string, args: Array<string>) => Output?run waits and returns Output { exit_code, stdout, stderr }, draining stderr
on its own thread so a full pipe cannot deadlock. spawn gives you the running
child:
[public] fn pid() => number
[public] fn write(text: string) => number // negative on error
[public] fn write_line(text: string) => number // negative on error
[public] fn close_input()
[public] fn read_line() => string?
[public] fn read_error_line() => string?
[public] fn read(count: number) => string?
[public] fn on_output(callback: closure(string) => void)
[public] fn on_error(callback: closure(string) => void)
[public] fn wait() => number
[public] fn is_running() => bool
[public] fn try_exit_code() => number?
[public] fn kill()
[public] fn free()read_line, read, and wait block. on_output and on_error spawn a
background thread and return immediately, which is how you stream a long-running
child. read(count) reads a fixed byte count for framed protocols.
free() closes the pipes but does not stop the child: kill() first if it is
still running.
Threads
[public] fn sleep(seconds: number)
[public] fn sleep_ms(ms: number)
[public] fn new_thread(work: closure() => void)
[public] fn make_future<T>(work: closure() => T) => Future<T>
[public] fn scope(body: closure(Scope) => void)new_thread is the workhorse. Capture what the thread needs:
threads::new_thread(closure[application, child]() => void {
let code: number = child.wait()
application.emit("done", `{"code":${code}}`)
})GC participation is automatic: the runtime attaches and detaches each worker, and every blocking call in the standard library is bracketed so a collection can run while a thread waits.
Mutex
let guarded: threads::Mutex<Map<string, number>> =
new threads::Mutex<Map<string, number>>(new Map<string, number>())
let map: Map<string, number> = guarded.lock()
let present: bool = map.contains(key)
guarded.release()A mutex is not scoped. lock() returns the guarded value and release() has
to run on every path out, including early returns. There is no guard object and
no try_lock. Hold it across the read, then release before doing real work.
Channels, futures, cancellation
Channel<T> with a buffer size of zero is a rendezvous; a positive size queues.
recv() returns None when the channel is closed and drained, while
try_recv() returns None for empty or closed, which are not
distinguishable.
Future<T> from make_future has await(), await_timeout(ms),
is_complete(), and cancel(). await returns None when cancelled.
CancelToken is the safe way to stop work: check is_cancelled() in the loop.
Thread.kill() exists but may leave a lock held or an allocation half-finished.
Mutex, Channel, Future, CancelToken, and Rng each own memory outside
the managed heap and must be released with free() when done.