os: add Process (#6786)

pull/6815/head
Delyan Angelov 2020-11-16 18:32:50 +02:00 committed by GitHub
parent 8e473181ed
commit d633261a99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 701 additions and 135 deletions

View File

@ -0,0 +1,81 @@
module main
import os
import time
import os.cmdline
enum Target {
both
stderr
stdout
alternate
}
fn s2target(s string) Target {
return match s {
'both' { Target.both }
'stderr' { Target.stderr }
'alternate' { Target.alternate }
else { Target.stdout }
}
}
struct Context {
mut:
timeout_ms int
period_ms int
exitcode int
target Target
omode Target
is_verbose bool
}
fn (mut ctx Context) println(s string) {
if ctx.target == .alternate {
ctx.omode = if ctx.omode == .stderr { Target.stdout } else { Target.stderr }
}
if ctx.target in [.both, .stdout] || ctx.omode == .stdout {
println('stdout, $s')
}
if ctx.target in [.both, .stderr] || ctx.omode == .stderr {
eprintln('stderr, $s')
}
}
fn do_timeout(c &Context) {
mut ctx := c
time.sleep_ms(ctx.timeout_ms)
exit(ctx.exitcode)
}
fn main() {
mut ctx := Context{}
args := os.args[1..]
if '-h' in args || '--help' in args {
println("Usage:
test_os_process [-v] [-h] [-target stderr/stdout/both/alternate] [-exitcode 0] [-timeout_ms 1000] [-period_ms 100]
Prints lines periodically (-period_ms), to stdout/stderr (-target).
After a while (-timeout_ms), exit with (-exitcode).
This program is useful for platform independent testing
of child process/standart input/output control.
It is used in V\'s `os` module tests.
")
}
ctx.is_verbose = '-v' in args
ctx.target = s2target(cmdline.option(args, '-target', 'both'))
ctx.exitcode = cmdline.option(args, '-exitcode', '0').int()
ctx.timeout_ms = cmdline.option(args, '-timeout_ms', '1000').int()
ctx.period_ms = cmdline.option(args, '-period_ms', '100').int()
if ctx.target == .alternate {
ctx.omode = .stdout
}
if ctx.is_verbose {
eprintln('> args: $args | context: $ctx')
}
go do_timeout(&ctx)
for i := 1; true; i++ {
ctx.println('$i')
time.sleep_ms(ctx.period_ms)
}
time.sleep(100000)
}

View File

@ -4,388 +4,307 @@ module builtin
fn C.memcpy(byteptr, byteptr, int) voidptr fn C.memcpy(byteptr, byteptr, int) voidptr
fn C.memcmp(byteptr, byteptr, int) int fn C.memcmp(byteptr, byteptr, int) int
fn C.memmove(byteptr, byteptr, int) voidptr
fn C.calloc(int) byteptr
fn C.malloc(int) byteptr
fn C.realloc(a byteptr, b int) byteptr
fn C.free(ptr voidptr)
fn C.exit(code int)
fn C.memmove(byteptr, byteptr, int) voidptr
fn C.calloc(int) byteptr
fn C.malloc(int) byteptr
fn C.realloc(a byteptr, b int) byteptr
fn C.free(ptr voidptr)
fn C.exit(code int)
fn C.qsort(voidptr, int, int, qsort_callback_func) fn C.qsort(voidptr, int, int, qsort_callback_func)
fn C.sprintf(a ...voidptr) int fn C.sprintf(a ...voidptr) int
fn C.strlen(s charptr) int fn C.strlen(s charptr) int
fn C.sscanf(byteptr, byteptr,...byteptr) int fn C.sscanf(byteptr, byteptr, ...byteptr) int
fn C.isdigit(s byteptr) bool fn C.isdigit(s byteptr) bool
// stdio.h // stdio.h
fn C.popen(c charptr, t charptr) voidptr fn C.popen(c charptr, t charptr) voidptr
// <execinfo.h> // <execinfo.h>
fn C.backtrace(a &voidptr, size int) int fn C.backtrace(a &voidptr, size int) int
fn C.backtrace_symbols(a &voidptr, size int) &charptr
fn C.backtrace_symbols(a &voidptr, size int) &charptr
fn C.backtrace_symbols_fd(a &voidptr, size int, fd int) fn C.backtrace_symbols_fd(a &voidptr, size int, fd int)
// <libproc.h> // <libproc.h>
pub fn proc_pidpath(int, voidptr, int) int pub fn proc_pidpath(int, voidptr, int) int
fn C.realpath(charptr, charptr) &char fn C.realpath(charptr, charptr) &char
fn C.chmod(byteptr, int) int fn C.chmod(byteptr, int) int
fn C.printf(byteptr, ...byteptr) int fn C.printf(byteptr, ...byteptr) int
fn C.puts(byteptr) int fn C.puts(byteptr) int
fn C.fputs(byteptr) int fn C.fputs(byteptr) int
fn C.fflush(byteptr) int fn C.fflush(byteptr) int
// TODO define args in these functions // TODO define args in these functions
fn C.fseek() int fn C.fseek() int
fn C.fopen() voidptr fn C.fopen() voidptr
fn C.fileno(voidptr) int fn C.fileno(voidptr) int
fn C.fwrite() int fn C.fwrite() int
fn C.fclose() int fn C.fclose() int
fn C.pclose() int fn C.pclose() int
// process execution, os.process:
fn C.getpid() int
fn C.system() int fn C.system() int
fn C.posix_spawn(&int, charptr, voidptr, voidptr, &charptr, voidptr) int
fn C.waitpid(int, voidptr, int) int fn C.posix_spawn(child_pid &int, path charptr, file_actions voidptr, attrp voidptr, argv &charptr, envp &charptr) int
fn C.posix_spawnp(child_pid &int, exefile charptr, file_actions voidptr, attrp voidptr, argv &charptr, envp &charptr) int
fn C.execve(cmd_path charptr, args voidptr, envs voidptr) int
fn C.fork() int
fn C.wait(status &int) int
fn C.waitpid(pid int, status &int, options int) int
fn C.kill(pid int, sig int) int
fn C.setenv(charptr) int fn C.setenv(charptr) int
fn C.unsetenv(charptr) int fn C.unsetenv(charptr) int
fn C.access() int fn C.access() int
fn C.remove() int fn C.remove() int
fn C.rmdir() int fn C.rmdir() int
fn C.chdir() int fn C.chdir() int
fn C.fread() int fn C.fread() int
fn C.rewind() int fn C.rewind() int
fn C.stat(charptr) int fn C.stat(charptr) int
fn C.lstat() int fn C.lstat() int
fn C.rename() int fn C.rename() int
fn C.fgets() int fn C.fgets() int
fn C.memset() int fn C.memset() int
fn C.sigemptyset() int fn C.sigemptyset() int
fn C.getcwd() int fn C.getcwd() int
fn C.signal(signal int, handlercb voidptr) voidptr
fn C.signal() int
fn C.mktime() int fn C.mktime() int
fn C.gettimeofday() int fn C.gettimeofday() int
[trusted] [trusted]
fn C.sleep(int) int fn C.sleep(int) int
fn C.usleep() int fn C.usleep() int
fn C.opendir() voidptr fn C.opendir() voidptr
fn C.closedir() int fn C.closedir() int
fn C.mkdir() int fn C.mkdir() int
fn C.srand() int fn C.srand() int
fn C.atof() int fn C.atof() int
fn C.tolower() int fn C.tolower() int
fn C.toupper() int fn C.toupper() int
[trusted] [trusted]
fn C.getchar() int fn C.getchar() int
[trusted] [trusted]
fn C.strerror(int) charptr fn C.strerror(int) charptr
fn C.snprintf() int fn C.snprintf() int
fn C.fprintf(byteptr, ...byteptr) fn C.fprintf(byteptr, ...byteptr)
fn C.WIFEXITED() bool fn C.WIFEXITED() bool
fn C.WEXITSTATUS() int fn C.WEXITSTATUS() int
fn C.WIFSIGNALED() bool fn C.WIFSIGNALED() bool
fn C.WTERMSIG() int fn C.WTERMSIG() int
fn C.isatty() int fn C.isatty() int
fn C.syscall() int fn C.syscall() int
fn C.sysctl() int fn C.sysctl() int
fn C._fileno(int) int fn C._fileno(int) int
fn C._get_osfhandle(fd int) C.intptr_t fn C._get_osfhandle(fd int) C.intptr_t
fn C.GetModuleFileName() int fn C.GetModuleFileName() int
fn C.GetModuleFileNameW(hModule voidptr, lpFilename &u16, nSize u32) u32 fn C.GetModuleFileNameW(hModule voidptr, lpFilename &u16, nSize u32) u32
fn C.CreateFile() voidptr fn C.CreateFile() voidptr
fn C.CreateFileW(lpFilename &u16, dwDesiredAccess u32, dwShareMode u32, lpSecurityAttributes &u16, dwCreationDisposition u32, dwFlagsAndAttributes u32, hTemplateFile voidptr) u32
fn C.CreateFileW(lpFilename &u16, dwDesiredAccess u32, dwShareMode u32, lpSecurityAttributes &u16, dwCreationDisposition u32, dwFlagsAndAttributes u32, hTemplateFile voidptr) u32
fn C.GetFinalPathNameByHandleW(hFile voidptr, lpFilePath &u16, nSize u32, dwFlags u32) int fn C.GetFinalPathNameByHandleW(hFile voidptr, lpFilePath &u16, nSize u32, dwFlags u32) int
fn C.CreatePipe(hReadPipe &voidptr, hWritePipe &voidptr, lpPipeAttributes voidptr, nSize u32) bool fn C.CreatePipe(hReadPipe &voidptr, hWritePipe &voidptr, lpPipeAttributes voidptr, nSize u32) bool
fn C.SetHandleInformation(hObject voidptr, dwMask u32, dw_flags u32) bool fn C.SetHandleInformation(hObject voidptr, dwMask u32, dw_flags u32) bool
fn C.ExpandEnvironmentStringsW(lpSrc &u16, lpDst &u16, nSize u32) u32 fn C.ExpandEnvironmentStringsW(lpSrc &u16, lpDst &u16, nSize u32) u32
fn C.SendMessageTimeout() u32 fn C.SendMessageTimeout() u32
fn C.SendMessageTimeoutW(hWnd voidptr, Msg u32, wParam &u16, lParam &u32, fuFlags u32, uTimeout u32, lpdwResult &u64) u32
fn C.SendMessageTimeoutW(hWnd voidptr, Msg u32, wParam &u16, lParam &u32, fuFlags u32, uTimeout u32, lpdwResult &u64) u32
fn C.CreateProcessW(lpApplicationName &u16, lpCommandLine &u16, lpProcessAttributes voidptr, lpThreadAttributes voidptr, bInheritHandles bool, dwCreationFlags u32, lpEnvironment voidptr, lpCurrentDirectory &u16, lpStartupInfo voidptr, lpProcessInformation voidptr) bool fn C.CreateProcessW(lpApplicationName &u16, lpCommandLine &u16, lpProcessAttributes voidptr, lpThreadAttributes voidptr, bInheritHandles bool, dwCreationFlags u32, lpEnvironment voidptr, lpCurrentDirectory &u16, lpStartupInfo voidptr, lpProcessInformation voidptr) bool
fn C.ReadFile(hFile voidptr, lpBuffer voidptr, nNumberOfBytesToRead u32, lpNumberOfBytesRead C.LPDWORD, lpOverlapped voidptr) bool fn C.ReadFile(hFile voidptr, lpBuffer voidptr, nNumberOfBytesToRead u32, lpNumberOfBytesRead C.LPDWORD, lpOverlapped voidptr) bool
fn C.GetFileAttributesW(lpFileName byteptr) u32 fn C.GetFileAttributesW(lpFileName byteptr) u32
fn C.RegQueryValueEx() voidptr fn C.RegQueryValueEx() voidptr
fn C.RegQueryValueExW(hKey voidptr, lpValueName &u16, lp_reserved &u32, lpType &u32, lpData byteptr, lpcbData &u32) int fn C.RegQueryValueExW(hKey voidptr, lpValueName &u16, lp_reserved &u32, lpType &u32, lpData byteptr, lpcbData &u32) int
fn C.RegOpenKeyEx() voidptr fn C.RegOpenKeyEx() voidptr
fn C.RegOpenKeyExW(hKey voidptr, lpSubKey &u16, ulOptions u32, samDesired u32, phkResult voidptr) int fn C.RegOpenKeyExW(hKey voidptr, lpSubKey &u16, ulOptions u32, samDesired u32, phkResult voidptr) int
fn C.RegSetValueEx() voidptr fn C.RegSetValueEx() voidptr
fn C.RegSetValueExW(hKey voidptr, lpValueName &u16, Reserved u32, dwType u32, lpData byteptr, lpcbData u32) int
fn C.RegSetValueExW(hKey voidptr, lpValueName &u16, Reserved u32, dwType u32, lpData byteptr, lpcbData u32) int
fn C.RegCloseKey() fn C.RegCloseKey()
fn C.RemoveDirectory() int fn C.RemoveDirectory() int
// fn C.GetStdHandle() voidptr
//fn C.GetStdHandle() voidptr
fn C.GetStdHandle(u32) voidptr fn C.GetStdHandle(u32) voidptr
// fn C.SetConsoleMode()
//fn C.SetConsoleMode()
fn C.SetConsoleMode(voidptr, u32) fn C.SetConsoleMode(voidptr, u32)
// fn C.GetConsoleMode() int
//fn C.GetConsoleMode() int
fn C.GetConsoleMode(voidptr, &u32) int fn C.GetConsoleMode(voidptr, &u32) int
fn C.GetCurrentProcessId() int
fn C.wprintf() fn C.wprintf()
// fn C.setbuf()
//fn C.setbuf()
fn C.setbuf(voidptr, charptr) fn C.setbuf(voidptr, charptr)
fn C.SymCleanup() fn C.SymCleanup()
fn C.MultiByteToWideChar() int fn C.MultiByteToWideChar() int
fn C.wcslen() int fn C.wcslen() int
fn C.WideCharToMultiByte() int fn C.WideCharToMultiByte() int
fn C._wstat() fn C._wstat()
fn C._wrename() fn C._wrename()
fn C._wfopen() voidptr fn C._wfopen() voidptr
fn C._wpopen() voidptr fn C._wpopen() voidptr
fn C._pclose() int fn C._pclose() int
fn C._wsystem() int fn C._wsystem() int
fn C._wgetenv() voidptr fn C._wgetenv() voidptr
fn C._putenv() int fn C._putenv() int
fn C._waccess() int fn C._waccess() int
fn C._wremove() int fn C._wremove() int
fn C.ReadConsole() voidptr fn C.ReadConsole() voidptr
fn C.WriteConsole() voidptr fn C.WriteConsole() voidptr
fn C.WriteFile() voidptr fn C.WriteFile() voidptr
fn C._wchdir() fn C._wchdir()
fn C._wgetcwd() int fn C._wgetcwd() int
fn C._fullpath() int fn C._fullpath() int
fn C.GetCommandLine() voidptr fn C.GetCommandLine() voidptr
fn C.LocalFree() fn C.LocalFree()
fn C.FindFirstFileW() voidptr fn C.FindFirstFileW() voidptr
fn C.FindFirstFile() voidptr fn C.FindFirstFile() voidptr
fn C.FindNextFile() int fn C.FindNextFile() int
fn C.FindClose() fn C.FindClose()
fn C.MAKELANGID() int fn C.MAKELANGID() int
fn C.FormatMessage() voidptr fn C.FormatMessage() voidptr
fn C.CloseHandle(voidptr) int fn C.CloseHandle(voidptr) int
fn C.GetExitCodeProcess() fn C.GetExitCodeProcess()
fn C.GetTickCount() i64 fn C.GetTickCount() i64
fn C.Sleep() fn C.Sleep()
fn C.WSAStartup(u16, &voidptr) int fn C.WSAStartup(u16, &voidptr) int
fn C.WSAGetLastError() int fn C.WSAGetLastError() int
fn C.closesocket(int) int fn C.closesocket(int) int
fn C.vschannel_init(&C.TlsContext) fn C.vschannel_init(&C.TlsContext)
fn C.request(&C.TlsContext, int, &u16, byteptr, &byteptr) fn C.request(&C.TlsContext, int, &u16, byteptr, &byteptr)
fn C.vschannel_cleanup(&C.TlsContext) fn C.vschannel_cleanup(&C.TlsContext)
fn C.URLDownloadToFile(int, &u16, &u16, int, int) fn C.URLDownloadToFile(int, &u16, &u16, int, int)
fn C.GetLastError() u32 fn C.GetLastError() u32
fn C.CreateDirectory(byteptr, int) bool fn C.CreateDirectory(byteptr, int) bool
// win crypto // win crypto
@ -393,60 +312,102 @@ fn C.BCryptGenRandom(int, voidptr, int, int) int
// win synchronization // win synchronization
fn C.CreateMutex(int, bool, byteptr) voidptr fn C.CreateMutex(int, bool, byteptr) voidptr
fn C.WaitForSingleObject(voidptr, int) int fn C.WaitForSingleObject(voidptr, int) int
fn C.ReleaseMutex(voidptr) bool fn C.ReleaseMutex(voidptr) bool
fn C.CreateEvent(int, bool, bool, byteptr) voidptr fn C.CreateEvent(int, bool, bool, byteptr) voidptr
fn C.SetEvent(voidptr) int fn C.SetEvent(voidptr) int
fn C.CreateSemaphore(voidptr, int, int, voidptr) voidptr fn C.CreateSemaphore(voidptr, int, int, voidptr) voidptr
fn C.ReleaseSemaphore(voidptr, int, voidptr) voidptr fn C.ReleaseSemaphore(voidptr, int, voidptr) voidptr
fn C.InitializeSRWLock(voidptr) fn C.InitializeSRWLock(voidptr)
fn C.AcquireSRWLockShared(voidptr) fn C.AcquireSRWLockShared(voidptr)
fn C.AcquireSRWLockExclusive(voidptr) fn C.AcquireSRWLockExclusive(voidptr)
fn C.ReleaseSRWLockShared(voidptr) fn C.ReleaseSRWLockShared(voidptr)
fn C.ReleaseSRWLockExclusive(voidptr) fn C.ReleaseSRWLockExclusive(voidptr)
// pthread.h // pthread.h
fn C.pthread_mutex_init(voidptr, voidptr) int fn C.pthread_mutex_init(voidptr, voidptr) int
fn C.pthread_mutex_lock(voidptr) int fn C.pthread_mutex_lock(voidptr) int
fn C.pthread_mutex_unlock(voidptr) int fn C.pthread_mutex_unlock(voidptr) int
fn C.pthread_mutex_destroy(voidptr) int fn C.pthread_mutex_destroy(voidptr) int
fn C.pthread_rwlockattr_init(voidptr) int fn C.pthread_rwlockattr_init(voidptr) int
fn C.pthread_rwlockattr_setkind_np(voidptr, int) int fn C.pthread_rwlockattr_setkind_np(voidptr, int) int
fn C.pthread_rwlockattr_setpshared(voidptr, int) int fn C.pthread_rwlockattr_setpshared(voidptr, int) int
fn C.pthread_rwlock_init(voidptr, voidptr) int fn C.pthread_rwlock_init(voidptr, voidptr) int
fn C.pthread_rwlock_rdlock(voidptr) int fn C.pthread_rwlock_rdlock(voidptr) int
fn C.pthread_rwlock_wrlock(voidptr) int fn C.pthread_rwlock_wrlock(voidptr) int
fn C.pthread_rwlock_unlock(voidptr) int fn C.pthread_rwlock_unlock(voidptr) int
fn C.pthread_condattr_init(voidptr) int fn C.pthread_condattr_init(voidptr) int
fn C.pthread_condattr_setpshared(voidptr, int) int fn C.pthread_condattr_setpshared(voidptr, int) int
fn C.pthread_condattr_destroy(voidptr) int fn C.pthread_condattr_destroy(voidptr) int
fn C.pthread_cond_init(voidptr, voidptr) int fn C.pthread_cond_init(voidptr, voidptr) int
fn C.pthread_cond_signal(voidptr) int fn C.pthread_cond_signal(voidptr) int
fn C.pthread_cond_wait(voidptr, voidptr) int fn C.pthread_cond_wait(voidptr, voidptr) int
fn C.pthread_cond_timedwait(voidptr, voidptr, voidptr) int fn C.pthread_cond_timedwait(voidptr, voidptr, voidptr) int
fn C.pthread_cond_destroy(voidptr) int fn C.pthread_cond_destroy(voidptr) int
fn C.sem_init(voidptr, int, u32) int fn C.sem_init(voidptr, int, u32) int
fn C.sem_post(voidptr) int fn C.sem_post(voidptr) int
fn C.sem_wait(voidptr) int fn C.sem_wait(voidptr) int
fn C.sem_trywait(voidptr) int fn C.sem_trywait(voidptr) int
fn C.sem_timedwait(voidptr, voidptr) int fn C.sem_timedwait(voidptr, voidptr) int
fn C.sem_destroy(voidptr) int fn C.sem_destroy(voidptr) int
// MacOS semaphore functions // MacOS semaphore functions
fn C.dispatch_semaphore_create(i64) voidptr fn C.dispatch_semaphore_create(i64) voidptr
fn C.dispatch_semaphore_signal(voidptr) i64 fn C.dispatch_semaphore_signal(voidptr) i64
fn C.dispatch_semaphore_wait(voidptr, u64) i64 fn C.dispatch_semaphore_wait(voidptr, u64) i64
fn C.dispatch_time(u64, i64) u64 fn C.dispatch_time(u64, i64) u64
fn C.dispatch_release(voidptr) fn C.dispatch_release(voidptr)
// file descriptor based reading/writing
fn C.read(fd int, buf voidptr, count size_t) int fn C.read(fd int, buf voidptr, count size_t) int
fn C.write(fd int, buf voidptr, count size_t) int fn C.write(fd int, buf voidptr, count size_t) int
fn C.close(fd int) int fn C.close(fd int) int
// pipes
fn C.pipe(pipefds &int) int
fn C.dup2(oldfd int, newfd int) int
// used by gl, stbi, freetype // used by gl, stbi, freetype
fn C.glTexImage2D() fn C.glTexImage2D()

44
vlib/os/fd.v 100644
View File

@ -0,0 +1,44 @@
module os
// file descriptor based operations:
pub fn fd_close(fd int) int {
return C.close(fd)
}
pub fn fd_write(fd int, s string) {
mut sp := s.str
mut remaining := s.len
for remaining > 0 {
written := C.write(fd, sp, remaining)
if written < 0 {
return
}
remaining = remaining - written
sp = unsafe {sp + written}
}
}
pub fn fd_slurp(fd int) []string {
mut res := []string{}
for {
s, b := fd_read(fd, 4096)
if b <= 0 {
break
}
res << s
}
return res
}
pub fn fd_read(fd int, maxbytes int) (string, int) {
mut buf := malloc(maxbytes)
nbytes := C.read(fd, buf, maxbytes)
if nbytes < 0 {
free(buf)
return '', nbytes
}
unsafe {
buf[nbytes] = 0
}
return tos(buf, nbytes), nbytes
}

View File

@ -8,8 +8,6 @@ struct C.dirent {
fn C.readdir(voidptr) &C.dirent fn C.readdir(voidptr) &C.dirent
fn C.getpid() int
fn C.readlink() int fn C.readlink() int
fn C.getline(voidptr, voidptr, voidptr) int fn C.getline(voidptr, voidptr, voidptr) int
@ -24,10 +22,6 @@ fn C.fdopen(int, string) voidptr
fn C.CopyFile(&u32, &u32, int) int fn C.CopyFile(&u32, &u32, int) int
fn C.fork() int
fn C.wait() int
// fn C.proc_pidpath(int, byteptr, int) int // fn C.proc_pidpath(int, byteptr, int) int
struct C.stat { struct C.stat {
st_size int st_size int

240
vlib/os/process.v 100644
View File

@ -0,0 +1,240 @@
module os
// ProcessState.not_started - the process has not yet started
// ProcessState.running - the process is currently running
// ProcessState.stopped - the process was running, but was stopped temporarily
// ProcessState.exited - the process has finished/exited
// ProcessState.aborted - the process was terminated by a signal
pub enum ProcessState {
not_started
running
stopped
exited
aborted
}
[ref_only]
pub struct Process {
pub:
filename string // the process's command file path
pub mut:
pid int // the PID of the process
code int = -1
// the exit code of the process, != -1 *only* when status is .exited *and* the process was not aborted
status ProcessState = .not_started
// the current status of the process
err string // if the process fails, contains the reason why
args []string // the arguments that the command takes
env_is_custom bool // true, when the environment was customized with .set_environment
env []string // the environment with which the process was started
use_stdio_ctl bool // when true, then you can use p.stdin_write(), p.stdout_slurp() and p.stderr_slurp()
stdio_fd [3]int
}
// new_process - create a new process descriptor
// NB: new does NOT start the new process.
// That is done because you may want to customize it first,
// by calling different set_ methods on it.
// In order to start it, call p.run() or p.wait()
pub fn new_process(filename string) &Process {
return &Process{
filename: filename
}
}
// set_args - set the arguments for the new process
pub fn (mut p Process) set_args(pargs []string) &Process {
if p.status != .not_started {
return p
}
p.args = pargs
return p
}
// set_environment - set a custom environment variable mapping for the new process
pub fn (mut p Process) set_environment(envs map[string]string) &Process {
if p.status != .not_started {
return p
}
p.env_is_custom = true
p.env = []string{}
for k, v in envs {
p.env << '$k=$v'
}
return p
}
// run - starts the new process
pub fn (mut p Process) run() &Process {
if p.status != .not_started {
return p
}
p._spawn()
return p
}
// signal_kill - kills the process, after that it is no longer running
pub fn (mut p Process) signal_kill() &Process {
if p.status !in [.running, .stopped] {
return p
}
p._signal_kill()
p.status = .aborted
return p
}
// signal_stop - stops the process, you can resume it with p.signal_continue()
pub fn (mut p Process) signal_stop() &Process {
if p.status != .running {
return p
}
p._signal_stop()
p.status = .stopped
return p
}
// signal_continue - tell a stopped process to continue/resume its work
pub fn (mut p Process) signal_continue() &Process {
if p.status != .stopped {
return p
}
p._signal_continue()
p.status = .running
return p
}
// wait - wait for a process to finish.
// NB: You have to call p.wait(), otherwise a finished process
// would get to a zombie state, and its resources will not get
// released fully, until its parent process exits.
// NB: This call will block the calling process until the child
// process is finished.
pub fn (mut p Process) wait() &Process {
if p.status == .not_started {
p._spawn()
}
if p.status !in [.running, .stopped] {
return p
}
p._wait()
return p
}
//
// _spawn - should not be called directly, but only by p.run()/p.wait() .
// It encapsulates the fork/execve mechanism that allows the
// asynchronous starting of the new child process.
fn (mut p Process) _spawn() int {
if !p.env_is_custom {
p.env = []string{}
current_environment := environ()
for k, v in current_environment {
p.env << '$k=$v'
}
}
mut pid := 0
$if windows {
pid = p.win_spawn_process()
} $else {
pid = p.unix_spawn_process()
}
p.pid = pid
p.status = .running
return 0
}
// is_alive - query whether the process p.pid is still alive
pub fn (mut p Process) is_alive() bool {
if p.status in [.running, .stopped] {
return p._is_alive()
}
return false
}
//
pub fn (mut p Process) set_redirect_stdio() &Process {
p.use_stdio_ctl = true
return p
}
pub fn (mut p Process) stdin_write(s string) {
p._check_redirection_call('stdin_write')
fd_write(p.stdio_fd[0], s)
}
pub fn (mut p Process) stdout_slurp() string {
p._check_redirection_call('stdout_slurp')
return fd_slurp(p.stdio_fd[1]).join('')
}
pub fn (mut p Process) stderr_slurp() string {
p._check_redirection_call('stderr_slurp')
return fd_slurp(p.stdio_fd[2]).join('')
}
pub fn (mut p Process) stdout_read() string {
p._check_redirection_call('stdout_read')
s, _ := fd_read(p.stdio_fd[1], 4096)
return s
}
pub fn (mut p Process) stderr_read() string {
p._check_redirection_call('stderr_read')
s, _ := fd_read(p.stdio_fd[2], 4096)
return s
}
// _check_redirection_call - should be called just by stdxxx methods
fn (mut p Process) _check_redirection_call(fn_name string) {
if !p.use_stdio_ctl {
panic('Call p.set_redirect_stdio() before calling p.$fn_name')
}
if p.status == .not_started {
panic('Call p.${fn_name}() after you have called p.run()')
}
}
// _signal_stop - should not be called directly, except by p.signal_stop
fn (mut p Process) _signal_stop() {
$if windows {
p.win_stop_process()
} $else {
p.unix_stop_process()
}
}
// _signal_continue - should not be called directly, just by p.signal_continue
fn (mut p Process) _signal_continue() {
$if windows {
p.win_resume_process()
} $else {
p.unix_resume_process()
}
}
// _signal_kill - should not be called directly, except by p.signal_kill
fn (mut p Process) _signal_kill() {
$if windows {
p.win_kill_process()
} $else {
p.unix_kill_process()
}
}
// _wait - should not be called directly, except by p.wait()
fn (mut p Process) _wait() {
$if windows {
p.win_wait()
} $else {
p.unix_wait()
}
}
// _is_alive - should not be called directly, except by p.is_alive()
fn (mut p Process) _is_alive() bool {
$if windows {
return p.win_is_alive()
} $else {
return p.unix_is_alive()
}
}

View File

@ -0,0 +1,134 @@
module os
fn (mut p Process) unix_spawn_process() int {
mut pipeset := [6]int{}
if p.use_stdio_ctl {
C.pipe(&pipeset[0]) // pipe read end 0 <- 1 pipe write end
C.pipe(&pipeset[2]) // pipe read end 2 <- 3 pipe write end
C.pipe(&pipeset[4]) // pipe read end 4 <- 5 pipe write end
}
pid := fork()
if pid != 0 {
// This is the parent process after the fork.
// NB: pid contains the process ID of the child process
if p.use_stdio_ctl {
p.stdio_fd[0] = pipeset[1] // store the write end of child's in
p.stdio_fd[1] = pipeset[2] // store the read end of child's out
p.stdio_fd[2] = pipeset[4] // store the read end of child's err
// close the rest of the pipe fds, the parent does not need them
fd_close(pipeset[0])
fd_close(pipeset[3])
fd_close(pipeset[5])
}
return pid
}
//
// Here, we are in the child process.
// It still shares file descriptors with the parent process,
// but it is otherwise independant and can do stuff *without*
// affecting the parent process.
if p.use_stdio_ctl {
// Redirect the child standart in/out/err to the pipes that
// were created in the parent.
// Close the parent's pipe fds, the child do not need them:
fd_close(pipeset[1])
fd_close(pipeset[2])
fd_close(pipeset[4])
// redirect the pipe fds to the child's in/out/err fds:
C.dup2(pipeset[0], 0)
C.dup2(pipeset[3], 1)
C.dup2(pipeset[5], 2)
// close the pipe fdsx after the redirection
fd_close(pipeset[0])
fd_close(pipeset[3])
fd_close(pipeset[5])
}
mut cargv := []charptr{}
mut cenvs := []charptr{}
cargv << p.filename.str
for i in 0 .. p.args.len {
cargv << p.args[i].str
}
for i in 0 .. p.env.len {
cenvs << p.env[i].str
}
cargv << charptr(0)
cenvs << charptr(0)
C.execve(p.filename.str, cargv.data, cenvs.data)
// NB: normally execve does not return at all.
// If it returns, then something went wrong...
eprintln(posix_get_error_msg(C.errno))
exit(1)
return 0
}
fn (mut p Process) unix_stop_process() {
C.kill(p.pid, C.SIGSTOP)
}
fn (mut p Process) unix_resume_process() {
C.kill(p.pid, C.SIGCONT)
}
fn (mut p Process) unix_kill_process() {
C.kill(p.pid, C.SIGKILL)
}
fn (mut p Process) unix_wait() {
cstatus := 0
ret := C.waitpid(p.pid, &cstatus, 0)
if ret == -1 {
p.err = posix_get_error_msg(C.errno)
return
}
pret, is_signaled := posix_wait4_to_exit_status(cstatus)
if is_signaled {
p.status = .aborted
p.err = 'Terminated by signal ${ret:2d} (${sigint_to_signal_name(pret)})'
} else {
p.status = .exited
}
p.code = pret
}
fn (mut p Process) unix_is_alive() bool {
cstatus := 0
ret := C.waitpid(p.pid, &cstatus, C.WNOHANG)
if ret == -1 {
p.err = posix_get_error_msg(C.errno)
return false
}
if ret == 0 {
return true
}
pret, is_signaled := posix_wait4_to_exit_status(cstatus)
if is_signaled {
p.status = .aborted
p.err = 'Terminated by signal ${ret:2d} (${sigint_to_signal_name(pret)})'
} else {
p.status = .exited
}
p.code = pret
return false
}
// these are here to make v_win.c/v.c generation work in all cases:
fn (mut p Process) win_spawn_process() int {
return 0
}
fn (mut p Process) win_stop_process() {
}
fn (mut p Process) win_resume_process() {
}
fn (mut p Process) win_kill_process() {
}
fn (mut p Process) win_wait() {
}
fn (mut p Process) win_is_alive() bool {
return false
}

View File

@ -0,0 +1,63 @@
import os
import time
fn test_getpid() {
pid := os.getpid()
eprintln('current pid: $pid')
assert pid != 0
}
fn test_run() {
if os.user_os() == 'windows' {
return
}
//
mut p := os.new_process('/bin/sleep')
p.set_args(['0.2'])
p.run()
assert p.status == .running
assert p.pid > 0
assert p.pid != os.getpid()
mut i := 0
for {
if !p.is_alive() {
break
}
os.system('ps -opid= -oppid= -ouser= -onice= -of= -ovsz= -orss= -otime= -oargs= -p $p.pid')
time.sleep_ms(50)
i++
}
p.wait()
assert p.code == 0
assert p.status == .exited
//
eprintln('polling iterations: $i')
assert i > 1
assert i < 20
}
fn test_wait() {
if os.user_os() == 'windows' {
return
}
mut p := os.new_process('/bin/date')
p.wait()
assert p.pid != os.getpid()
assert p.code == 0
assert p.status == .exited
}
fn test_slurping_output() {
if os.user_os() == 'windows' {
return
}
mut p := os.new_process('/bin/date')
p.set_redirect_stdio()
p.wait()
assert p.code == 0
assert p.status == .exited
output := p.stdout_slurp().trim_space()
errors := p.stderr_slurp().trim_space()
eprintln('p output: "$output"')
eprintln('p errors: "$errors"')
}

View File

@ -0,0 +1,51 @@
module os
fn (mut p Process) win_spawn_process() int {
eprintln('TODO implement waiting for a process on windows')
return 12345
}
fn (mut p Process) win_stop_process() {
eprintln('TODO implement stopping a process on windows')
}
fn (mut p Process) win_resume_process() {
eprintln('TODO implement resuming a process on windows')
}
fn (mut p Process) win_kill_process() {
eprintln('TODO implement killing a process on windows')
}
fn (mut p Process) win_wait() {
eprintln('TODO implement waiting for a process on windows')
p.status = .exited
p.code = 0
}
fn (mut p Process) win_is_alive() bool {
eprintln('TODO implement checking whether the process is still alive on windows')
return false
}
//
// these are here to make v_win.c/v.c generation work in all cases:
fn (mut p Process) unix_spawn_process() int {
return 0
}
fn (mut p Process) unix_stop_process() {
}
fn (mut p Process) unix_resume_process() {
}
fn (mut p Process) unix_kill_process() {
}
fn (mut p Process) unix_wait() {
}
fn (mut p Process) unix_is_alive() bool {
return false
}

View File

@ -38,8 +38,6 @@ fn C.tcsetattr() int
fn C.raise() fn C.raise()
fn C.kill(int, int) int
fn C.getppid() int fn C.getppid() int
// Enable the raw mode of the terminal // Enable the raw mode of the terminal