hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

process+linux.ha (5548B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use errors;
      5 use fmt;
      6 use rt;
      7 use time;
      8 use unix::signal;
      9 
     10 // Stores information about a child process.
     11 export type process = int;
     12 
     13 // Returns the currently running [[process]].
     14 export fn self() process = {
     15 	return rt::getpid();
     16 };
     17 
     18 // Stores information about an exited process.
     19 export type status = struct {
     20 	status: int,
     21 	// Not all of these members are supported on all operating systems.
     22 	// Only utime and stime are guaranteed to be available.
     23 	rusage: struct {
     24 		utime: time::instant,
     25 		stime: time::instant,
     26 		maxrss: u64,
     27 		minflt: u64,
     28 		majflt: u64,
     29 		inblock: u64,
     30 		oublock: u64,
     31 		nvcsw: u64,
     32 		nivcsw: u64,
     33 	},
     34 };
     35 
     36 fn rusage(st: *status, ru: *rt::rusage) void = {
     37 	st.rusage.utime = time::instant {
     38 		sec = ru.ru_utime.tv_sec,
     39 		nsec = ru.ru_utime.tv_usec * time::MICROSECOND: i64,
     40 	};
     41 	st.rusage.stime = time::instant {
     42 		sec = ru.ru_stime.tv_sec,
     43 		nsec = ru.ru_stime.tv_usec * time::MICROSECOND: i64,
     44 	};
     45 	st.rusage.maxrss = ru.ru_maxrss;
     46 	st.rusage.minflt = ru.ru_minflt;
     47 	st.rusage.majflt = ru.ru_majflt;
     48 	st.rusage.inblock = ru.ru_inblock;
     49 	st.rusage.oublock = ru.ru_oublock;
     50 	st.rusage.nvcsw = ru.ru_nvcsw;
     51 	st.rusage.nivcsw = ru.ru_nivcsw;
     52 };
     53 
     54 // Waits for a process to complete, then returns its status information.
     55 export fn wait(proc: *process) (status | error) = {
     56 	let ru: rt::rusage = rt::rusage { ... };
     57 	let st: status = status { ... };
     58 	match (rt::wait4(*proc, &st.status, 0, &ru)) {
     59 	case let err: rt::errno =>
     60 		return errors::errno(err);
     61 	case let pid: int =>
     62 		assert(pid == *proc);
     63 	};
     64 	rusage(&st, &ru);
     65 	return st;
     66 };
     67 
     68 // Waits for the first child process to complete, then returns its process info
     69 // and status
     70 export fn waitany() ((process, status) | error) = {
     71 	let ru: rt::rusage = rt::rusage { ... };
     72 	let st: status = status { ... };
     73 	match (rt::wait4(-1, &st.status, 0, &ru)) {
     74 	case let err: rt::errno =>
     75 		return errors::errno(err);
     76 	case let pid: int =>
     77 		rusage(&st, &ru);
     78 		return (pid, st);
     79 	};
     80 };
     81 
     82 // Waits for all children to terminate succesfully. If a child process exits
     83 // with a nonzero status, returns its process info and exit status immediately,
     84 // not waiting for the remaining children.
     85 export fn waitall() (uint | error | !(process, exit_status)) = {
     86 	let st: status = status { ... };
     87 	let ru: rt::rusage = rt::rusage { ... };
     88 	for (let i = 0u; true; i += 1) match (rt::wait4(-1, &st.status, 0, &ru)) {
     89 	case let err: rt::errno =>
     90 		if (err == rt::ECHILD) {
     91 			return i;
     92 		};
     93 		return errors::errno(err);
     94 	case let pid: int =>
     95 		match (check(&st)) {
     96 		case void => void;
     97 		case let es: !exit_status =>
     98 			return (pid, es);
     99 		};
    100 	};
    101 };
    102 
    103 // Checks for process completion, returning its status information on
    104 // completion, or void if it is still running.
    105 export fn peek(proc: *process) (status | void | error) = {
    106 	let ru: rt::rusage = rt::rusage { ... };
    107 	let st: status = status { ... };
    108 	match (rt::wait4(*proc, &st.status, rt::WNOHANG, &ru)) {
    109 	case let err: rt::errno =>
    110 		return errors::errno(err);
    111 	case let pid: int =>
    112 		switch (pid) {
    113 		case 0 =>
    114 			return;
    115 		case =>
    116 			assert(pid == *proc);
    117 		};
    118 	};
    119 	rusage(&st, &ru);
    120 	return st;
    121 };
    122 
    123 // Checks if any child process has completed, returning its process info and
    124 // status if so.
    125 export fn peekany() ((process, status) | void | error) = {
    126 	let ru: rt::rusage = rt::rusage { ... };
    127 	let st: status = status { ... };
    128 	match (rt::wait4(-1, &st.status, rt::WNOHANG, &ru)) {
    129 	case let err: rt::errno =>
    130 		return errors::errno(err);
    131 	case let pid: int =>
    132 		switch (pid) {
    133 		case 0 =>
    134 			return;
    135 		case =>
    136 			return (pid, st);
    137 		};
    138 	};
    139 };
    140 
    141 // The exit status code of a process.
    142 export type exited = int;
    143 
    144 // The signal number which caused a process to terminate.
    145 export type signaled = signal::sig;
    146 
    147 // The exit status of a process.
    148 export type exit_status = (exited | signaled);
    149 
    150 // Returns a human friendly string describing the exit status. The string is
    151 // statically allocated; use [[strings::dup]] to extend its lifetime.
    152 export fn exitstr(status: exit_status) const str = {
    153 	static let buf: [1024]u8 = [0...];
    154 	match (status) {
    155 	case let i: exited =>
    156 		switch (i) {
    157 		case 0 =>
    158 			return "exited normally";
    159 		case =>
    160 			return fmt::bsprintf(buf, "exited with status {}",
    161 				i: int);
    162 		};
    163 	case let s: signaled =>
    164 		return fmt::bsprintf(buf, "exited with signal {}",
    165 			signal::signame(s));
    166 	};
    167 };
    168 
    169 // Returns the exit status of a completed process.
    170 export fn exit(stat: *status) exit_status = {
    171 	if (rt::wifexited(stat.status)) {
    172 		return rt::wexitstatus(stat.status): exited;
    173 	};
    174 	if (rt::wifsignaled(stat.status)) {
    175 		return rt::wtermsig(stat.status): signaled;
    176 	};
    177 	abort("Unexpected exit status");
    178 };
    179 
    180 // Checks the exit status of a completed process, returning void if successful,
    181 // or its status code as an error type if not.
    182 export fn check(stat: *status) (void | !exit_status) = {
    183 	if (rt::wifexited(stat.status)) {
    184 		switch (rt::wexitstatus(stat.status)) {
    185 		case 0 =>
    186 			return void;
    187 		case =>
    188 			return exit(stat);
    189 		};
    190 	};
    191 	return exit(stat);
    192 };
    193 
    194 // Terminates a process. On Linux, this sends [[unix::signal::sig::TERM]] to the
    195 // process.
    196 export fn kill(proc: process) (void | errors::error) = {
    197 	return sig(proc, signal::sig::TERM);
    198 };
    199 
    200 // Sends a signal to a child process. This function is only supported on
    201 // Unix-like systems.
    202 export fn sig(proc: process, sig: signal::sig) (void | errors::error) = {
    203 	match (rt::kill(proc, sig)) {
    204 	case let errno: rt::errno =>
    205 		return errors::errno(errno);
    206 	case void =>
    207 		return;
    208 	};
    209 };