select() for processes; copyDir() for os.

This commit is contained in:
dom96 2011-05-07 23:17:10 +01:00
commit 0db6f3c00f
4 changed files with 129 additions and 20 deletions

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -43,7 +43,7 @@ proc execProcess*(command: string,
options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): string {.
rtl, extern: "nosp$1".}
## A convience procedure that executes ``command`` with ``startProcess``
## A convenience procedure that executes ``command`` with ``startProcess``
## and returns its output as a string.
proc executeProcess*(command: string,
@ -101,6 +101,9 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
proc waitForExit*(p: PProcess): int {.rtl, extern: "nosp$1".}
## waits for the process to finish and returns `p`'s error code.
proc peekExitCode*(p: PProcess): int
## return -1 if the process is still running. Otherwise the process' exit code
proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
## returns ``p``'s input stream for writing to
@ -196,6 +199,12 @@ proc execProcesses*(cmds: openArray[string],
var p = startProcessAux(cmds[i], options=options)
result = max(waitForExit(p), result)
proc select*(readfds: var seq[PProcess], timeout = 500): int
## select with a sensible Nimrod interface. `timeout` is in miliseconds.
## Specify -1 for no timeout. Returns the number of file handles that are
## ready. The processes that are ready to be read from are removed from
## `readfds`.
when not defined(useNimRtl):
proc execProcess(command: string,
options: set[TProcessOption] = {poStdErrToStdOut,
@ -366,6 +375,14 @@ when defined(Windows) and not defined(useNimRtl):
result = res
discard CloseHandle(p.FProcessHandle)
proc peekExitCode(p: PProcess): int =
var b = waitForSingleObject(p.FProcessHandle, 50) == WAIT_TIMEOUT
if b: result = -1
else:
var res: int32
discard GetExitCodeProcess(p.FProcessHandle, res)
return res
proc inputStream(p: PProcess): PStream =
result = newFileHandleStream(p.inputHandle)
@ -398,6 +415,24 @@ when defined(Windows) and not defined(useNimRtl):
result = -1
discard CloseHandle(Process)
proc select(readfds: var seq[PProcess], timeout = 500): int =
assert readfds.len <= MAXIMUM_WAIT_OBJECTS
var rfds: TWOHandleArray
for i in 0..readfds.len()-1:
rfds[i] = readfds[i].FProcessHandle
var ret = waitForMultipleObjects(readfds.len,
addr(rfds), 0'i32, timeout)
case ret
of WAIT_TIMEOUT:
return 0
of WAIT_FAILED:
OSError()
else:
var i = ret - WAIT_OBJECT_0
readfds.del(i)
return 1
elif not defined(useNimRtl):
const
readIdx = 0
@ -435,7 +470,7 @@ elif not defined(useNimRtl):
var
p_stdin, p_stdout, p_stderr: array [0..1, cint]
new(result)
result.exitCode = 3 # for ``waitForExit``
result.exitCode = -3 # for ``waitForExit``
if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32:
OSError("failed to create a pipe")
var Pid = fork()
@ -504,13 +539,19 @@ elif not defined(useNimRtl):
#if waitPid(p.id, p.exitCode, 0) == int(p.id):
# ``waitPid`` fails if the process is not running anymore. But then
# ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is
# initialized with 3, wrong success exit codes are prevented.
# initialized with -3, wrong success exit codes are prevented.
var oldExitCode = p.exitCode
if waitPid(p.id, p.exitCode, 0) < 0:
# failed, so restore old exitCode
p.exitCode = oldExitCode
result = int(p.exitCode)
proc peekExitCode(p: PProcess): int =
var b = waitPid(p.id, p.exitCode, WNOHANG) == int(p.id)
if b: result = -1
elif p.exitCode == -3: result = -1
else: result = p.exitCode
proc inputStream(p: PProcess): PStream =
var f: TFile
if not open(f, p.inputHandle, fmWrite): OSError()
@ -531,6 +572,39 @@ elif not defined(useNimRtl):
proc execCmd(command: string): int =
result = csystem(command)
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
FD_ZERO(fd)
for i in items(s):
m = max(m, int(i.outputHandle))
FD_SET(cint(i.outputHandle), fd)
proc pruneProcessSet(s: var seq[PProcess], fd: var TFdSet) =
var i = 0
var L = s.len
while i < L:
if FD_ISSET(cint(s[i].outputHandle), fd) != 0'i32:
s[i] = s[L-1]
dec(L)
else:
inc(i)
setLen(s, L)
proc select(readfds: var seq[PProcess], timeout = 500): int =
var tv: TTimeVal
tv.tv_sec = 0
tv.tv_usec = timeout * 1000
var rd: TFdSet
var m = 0
createFdSet((rd), readfds, m)
if timeout != -1:
result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
else:
result = int(select(cint(m+1), addr(rd), nil, nil, nil))
pruneProcessSet(readfds, (rd))
when isMainModule:
var x = execProcess("gcc -v")
echo "ECHO ", x