hare

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

process+linux.ha (5662B)


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