process.ha (5954B)
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 = rt::rusage { ... }; 72 let st: status = 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: int => 77 assert(pid == *proc: int); 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 = rt::rusage { ... }; 87 let st: status = status { ... }; 88 match (rt::wait4(-1, &st.status, 0, &ru)) { 89 case let err: rt::errno => 90 return errors::errno(err); 91 case let pid: int => 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 = status { ... }; 102 let ru: rt::rusage = rt::rusage { ... }; 103 for (let i = 0u; true; i += 1) match (rt::wait4(-1, &st.status, 0, &ru)) { 104 case let err: rt::errno => 105 if (err == rt::ECHILD) { 106 return i; 107 } else { 108 return errors::errno(err); 109 }; 110 case let pid: int => 111 match (check(&st)) { 112 case void => void; 113 case let es: !exit_status => 114 return (pid: process, es); 115 }; 116 }; 117 }; 118 119 // Checks for process completion, returning its status information on 120 // completion, or void if it is still running. 121 export fn peek(proc: *process) (status | void | error) = { 122 let ru: rt::rusage = rt::rusage { ... }; 123 let st: status = status { ... }; 124 match (rt::wait4(*proc, &st.status, rt::WNOHANG, &ru)) { 125 case let err: rt::errno => 126 return errors::errno(err); 127 case let pid: int => 128 switch (pid) { 129 case 0 => 130 return; 131 case => 132 assert(pid == *proc: int); 133 }; 134 }; 135 rusage(&st, &ru); 136 return st; 137 }; 138 139 // Checks if any child process has completed, returning its process info and 140 // status if so. 141 export fn peekany() ((process, status) | void | error) = { 142 let ru: rt::rusage = rt::rusage { ... }; 143 let st: status = status { ... }; 144 match (rt::wait4(-1, &st.status, rt::WNOHANG, &ru)) { 145 case let err: rt::errno => 146 return errors::errno(err); 147 case let pid: int => 148 switch (pid) { 149 case 0 => 150 return; 151 case => 152 return (pid: process, st); 153 }; 154 }; 155 }; 156 157 // The exit status code of a process. 158 export type exited = int; 159 160 // The signal number which caused a process to terminate. 161 export type signaled = signal::sig; 162 163 // The exit status of a process. 164 export type exit_status = (exited | signaled); 165 166 // Returns a human friendly string describing the exit status. The string is 167 // statically allocated; use [[strings::dup]] to extend its lifetime. 168 export fn exitstr(status: exit_status) const str = { 169 static let buf: [1024]u8 = [0...]; 170 match (status) { 171 case let i: exited => 172 switch (i) { 173 case 0 => 174 return "exited normally"; 175 case => 176 return fmt::bsprintf(buf, "exited with status {}", 177 i: int); 178 }; 179 case let s: signaled => 180 return fmt::bsprintf(buf, "exited with signal {}", 181 signal::signame(s)); 182 }; 183 }; 184 185 // Returns the exit status of a completed process. 186 export fn exit(stat: *status) exit_status = { 187 if (rt::wifexited(stat.status)) { 188 return rt::wexitstatus(stat.status): exited; 189 }; 190 if (rt::wifsignaled(stat.status)) { 191 return rt::wtermsig(stat.status): signaled; 192 }; 193 abort("Unexpected exit status"); 194 }; 195 196 // Checks the exit status of a completed process, returning void if successful, 197 // or its status code as an error type if not. 198 export fn check(stat: *status) (void | !exit_status) = { 199 if (rt::wifexited(stat.status)) { 200 switch (rt::wexitstatus(stat.status)) { 201 case 0 => 202 return void; 203 case => 204 return exit(stat); 205 }; 206 }; 207 return exit(stat); 208 }; 209 210 // Terminates a process. On FreeBSD, this sends [[unix::signal::sig::TERM]] to 211 // the process. 212 export fn kill(proc: process) (void | errors::error) = { 213 return sig(proc, signal::sig::TERM); 214 }; 215 216 // Sends a signal to a child process. This function is only supported on 217 // Unix-like systems. 218 export fn sig(proc: process, sig: signal::sig) (void | errors::error) = { 219 match (rt::kill(proc, sig)) { 220 case let errno: rt::errno => 221 return errors::errno(errno); 222 case void => 223 return; 224 }; 225 };