hare

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

process+freebsd.ha (5899B)


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