hare

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

process+linux.ha (6474B)


      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 Eyal Sawady <ecs@d2evs.net>
      5 use errors;
      6 use rt;
      7 use fmt;
      8 
      9 // Stores information about a child process.
     10 export type process = int;
     11 
     12 // Returns the currently running [[process]].
     13 export fn self() process = {
     14 	return rt::getpid();
     15 };
     16 
     17 // Stores information about an exited process.
     18 export type status = struct {
     19 	status: int,
     20 	// Not all of these members are supported on all operating systems.
     21 	// Only utime and stime are guaranteed to be available.
     22 	rusage: struct {
     23 		// TODO: utime, stime
     24 		maxrss: u64,
     25 		minflt: u64,
     26 		majflt: u64,
     27 		inblock: u64,
     28 		oublock: u64,
     29 		nvcsw: u64,
     30 		nivcsw: u64,
     31 	},
     32 };
     33 
     34 fn rusage(st: *status, ru: *rt::rusage) void = {
     35 	st.rusage.maxrss = ru.ru_maxrss;
     36 	st.rusage.minflt = ru.ru_minflt;
     37 	st.rusage.majflt = ru.ru_majflt;
     38 	st.rusage.inblock = ru.ru_inblock;
     39 	st.rusage.oublock = ru.ru_oublock;
     40 	st.rusage.nvcsw = ru.ru_nvcsw;
     41 	st.rusage.nivcsw = ru.ru_nivcsw;
     42 };
     43 
     44 // Waits for a process to complete, then returns its status information.
     45 export fn wait(proc: *process) (status | error) = {
     46 	let ru: rt::rusage = rt::rusage { ... };
     47 	let st: status = status { ... };
     48 	match (rt::wait4(*proc, &st.status, 0, &ru)) {
     49 	case let err: rt::errno =>
     50 		return errors::errno(err);
     51 	case let pid: int =>
     52 		assert(pid == *proc);
     53 	};
     54 	rusage(&st, &ru);
     55 	return st;
     56 };
     57 
     58 // Waits for the first child process to complete, then returns its process info
     59 // and status
     60 export fn waitany() ((process, status) | error) = {
     61 	let ru: rt::rusage = rt::rusage { ... };
     62 	let st: status = status { ... };
     63 	match (rt::wait4(-1, &st.status, 0, &ru)) {
     64 	case let err: rt::errno =>
     65 		return errors::errno(err);
     66 	case let pid: int =>
     67 		rusage(&st, &ru);
     68 		return (pid, st);
     69 	};
     70 };
     71 
     72 // Waits for all children to terminate succesfully. If a child process exits
     73 // with a nonzero status, returns its process info and exit status immediately,
     74 // not waiting for the remaining children.
     75 export fn waitall() (uint | error | !(process, exit_status)) = {
     76 	let st: status = status { ... };
     77 	let ru: rt::rusage = rt::rusage { ... };
     78 	for (let i = 0u; true; i += 1) match (rt::wait4(-1, &st.status, 0, &ru)) {
     79 	case let err: rt::errno =>
     80 		if (err: int == rt::ECHILD) {
     81 			return i;
     82 		} else {
     83 			return errors::errno(err);
     84 		};
     85 	case let pid: int =>
     86 		match (check(&st)) {
     87 		case void => void;
     88 		case let es: !exit_status =>
     89 			return (pid, es);
     90 		};
     91 	};
     92 	abort("unreachable");
     93 };
     94 
     95 // Checks for process completion, returning its status information on
     96 // completion, or void if it is still running.
     97 export fn peek(proc: *process) (status | void | error) = {
     98 	let ru: rt::rusage = rt::rusage { ... };
     99 	let st: status = status { ... };
    100 	match (rt::wait4(*proc, &st.status, rt::WNOHANG, &ru)) {
    101 	case let err: rt::errno =>
    102 		return errors::errno(err);
    103 	case let pid: int =>
    104 		switch (pid) {
    105 		case 0 =>
    106 			return;
    107 		case =>
    108 			assert(pid == *proc);
    109 		};
    110 	};
    111 	rusage(&st, &ru);
    112 	return st;
    113 };
    114 
    115 // Waits for the first child process to complete, then returns its process info
    116 // and status
    117 export fn peekany() ((process, status) | void | error) = {
    118 	let ru: rt::rusage = rt::rusage { ... };
    119 	let st: status = status { ... };
    120 	match (rt::wait4(-1, &st.status, rt::WNOHANG, &ru)) {
    121 	case let err: rt::errno =>
    122 		return errors::errno(err);
    123 	case let pid: int =>
    124 		switch (pid) {
    125 		case 0 =>
    126 			return;
    127 		case =>
    128 			return (pid, st);
    129 		};
    130 	};
    131 };
    132 
    133 // The exit status code of a process.
    134 export type exited = int;
    135 
    136 // The signal number which caused a process to terminate.
    137 export type signaled = int;
    138 
    139 // The exit status of a process.
    140 export type exit_status = (exited | signaled);
    141 
    142 // Returns a human friendly string describing the exit status.
    143 export fn exitstr(status: exit_status) const str = {
    144 	static let buf: [1024]u8 = [0...];
    145 	match (status) {
    146 	case let i: exited =>
    147 		switch (i) {
    148 		case 0 =>
    149 			return "exited normally";
    150 		case =>
    151 			return fmt::bsprintf(buf, "exited with status {}",
    152 				i: int);
    153 		};
    154 	case let s: signaled =>
    155 		// TODO: Add signal name
    156 		return fmt::bsprintf(buf, "exited with signal {}", s: int);
    157 	};
    158 };
    159 
    160 // Returns the exit status of a completed process.
    161 export fn exit(stat: *status) exit_status = {
    162 	if (rt::wifexited(stat.status)) {
    163 		return rt::wexitstatus(stat.status): exited;
    164 	};
    165 	if (rt::wifsignaled(stat.status)) {
    166 		return rt::wtermsig(stat.status): signaled;
    167 	};
    168 	abort("Unexpected exit status");
    169 };
    170 
    171 // Checks the exit status of a completed process, returning void if successful,
    172 // or its status code as an error type if not.
    173 export fn check(stat: *status) (void | !exit_status) = {
    174 	if (rt::wifexited(stat.status)) {
    175 		switch (rt::wexitstatus(stat.status)) {
    176 		case 0 =>
    177 			return void;
    178 		case =>
    179 			return exit(stat);
    180 		};
    181 	};
    182 	return exit(stat);
    183 };
    184 
    185 // An enumeration of all known signals. Only a subset of these are defined by
    186 // POSIX, consult the specification for details:
    187 //
    188 // https://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html
    189 export type signal = enum int {
    190 	SIGHUP = rt::SIGHUP,
    191 	SIGINT = rt::SIGINT,
    192 	SIGQUIT = rt::SIGQUIT,
    193 	SIGILL = rt::SIGILL,
    194 	SIGTRAP = rt::SIGTRAP,
    195 	SIGABRT = rt::SIGABRT,
    196 	SIGBUS = rt::SIGBUS,
    197 	SIGFPE = rt::SIGFPE,
    198 	SIGKILL = rt::SIGKILL,
    199 	SIGUSR1 = rt::SIGUSR1,
    200 	SIGSEGV = rt::SIGSEGV,
    201 	SIGUSR2 = rt::SIGUSR2,
    202 	SIGPIPE = rt::SIGPIPE,
    203 	SIGALRM = rt::SIGALRM,
    204 	SIGTERM = rt::SIGTERM,
    205 	SIGSTKFLT = rt::SIGSTKFLT,
    206 	SIGCHLD = rt::SIGCHLD,
    207 	SIGCONT = rt::SIGCONT,
    208 	SIGSTOP = rt::SIGSTOP,
    209 	SIGTSTP = rt::SIGTSTP,
    210 	SIGTTIN = rt::SIGTTIN,
    211 	SIGTTOU = rt::SIGTTOU,
    212 	SIGURG = rt::SIGURG,
    213 	SIGXCPU = rt::SIGXCPU,
    214 	SIGXFSZ = rt::SIGXFSZ,
    215 	SIGVTALRM = rt::SIGVTALRM,
    216 	SIGPROF = rt::SIGPROF,
    217 	SIGWINCH = rt::SIGWINCH,
    218 	SIGIO = rt::SIGIO,
    219 	SIGPOLL = rt::SIGPOLL,
    220 	SIGPWR = rt::SIGPWR,
    221 	SIGSYS = rt::SIGSYS,
    222 };
    223 
    224 // Sends a signal to a child process. If no variadic arguments are provided, the
    225 // program is terminated in a platform-specific manner. You may provide exactly
    226 // one variadic argument, the [[signal]] you wish to send, but this is only
    227 // supported on Unix-like systems.
    228 export fn kill(proc: process, sig: signal...) (void | errors::error) = {
    229 	const sig = if (len(sig) == 0) {
    230 		yield signal::SIGTERM;
    231 	} else if (len(sig) == 1) {
    232 		yield sig[0];
    233 	} else {
    234 		abort("os::exec::signal illegally called with more than one signal");
    235 	};
    236 	match (rt::kill(proc, sig)) {
    237 	case let errno: rt::errno =>
    238 		return errors::errno(errno);
    239 	case void =>
    240 		return;
    241 	};
    242 };