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