hare

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

process.ha (5885B)


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