mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-18 13:30:33 +00:00
Merge branch 'nexecProcesses' of https://github.com/cheatfate/Nim into cheatfate-nexecProcesses
This commit is contained in:
@@ -167,8 +167,7 @@ proc waitForExit*(p: Process, timeout: int = -1): int {.rtl,
|
||||
## On posix, if the process has exited because of a signal, 128 + signal
|
||||
## number will be returned.
|
||||
|
||||
|
||||
proc peekExitCode*(p: Process): int {.tags: [].}
|
||||
proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", tags: [].}
|
||||
## return -1 if the process is still running. Otherwise the process' exit code
|
||||
##
|
||||
## On posix, if the process has exited because of a signal, 128 + signal
|
||||
@@ -231,55 +230,79 @@ proc execProcesses*(cmds: openArray[string],
|
||||
## executes the commands `cmds` in parallel. Creates `n` processes
|
||||
## that execute in parallel. The highest return value of all processes
|
||||
## is returned. Runs `beforeRunEvent` before running each command.
|
||||
when false:
|
||||
# poParentStreams causes problems on Posix, so we simply disable it:
|
||||
var options = options - {poParentStreams}
|
||||
|
||||
|
||||
assert n > 0
|
||||
if n > 1:
|
||||
var q: seq[Process]
|
||||
newSeq(q, n)
|
||||
var i = 0
|
||||
var q = newSeq[Process](n)
|
||||
var m = min(n, cmds.len)
|
||||
for i in 0..m-1:
|
||||
|
||||
when defined(windows):
|
||||
var w: WOHandleArray
|
||||
var wcount = m
|
||||
|
||||
while i < m:
|
||||
if beforeRunEvent != nil:
|
||||
beforeRunEvent(i)
|
||||
q[i] = startProcess(cmds[i], options=options + {poEvalCommand})
|
||||
when defined(noBusyWaiting):
|
||||
var r = 0
|
||||
for i in m..high(cmds):
|
||||
when defined(debugExecProcesses):
|
||||
var err = ""
|
||||
var outp = outputStream(q[r])
|
||||
while running(q[r]) or not atEnd(outp):
|
||||
err.add(outp.readLine())
|
||||
err.add("\n")
|
||||
echo(err)
|
||||
result = max(waitForExit(q[r]), result)
|
||||
if afterRunEvent != nil: afterRunEvent(r, q[r])
|
||||
if q[r] != nil: close(q[r])
|
||||
if beforeRunEvent != nil:
|
||||
beforeRunEvent(i)
|
||||
q[r] = startProcess(cmds[i], options=options + {poEvalCommand})
|
||||
r = (r + 1) mod n
|
||||
else:
|
||||
var i = m
|
||||
while i <= high(cmds):
|
||||
sleep(50)
|
||||
for r in 0..n-1:
|
||||
q[i] = startProcess(cmds[i], options = options + {poEvalCommand})
|
||||
when defined(windows):
|
||||
w[i] = q[i].fProcessHandle
|
||||
inc(i)
|
||||
|
||||
var ecount = len(cmds)
|
||||
while ecount > 0:
|
||||
when defined(windows):
|
||||
# waiting for all children, get result if any child exits
|
||||
var ret = waitForMultipleObjects(int32(wcount), addr(w), 0'i32,
|
||||
INFINITE)
|
||||
if ret == WAIT_TIMEOUT:
|
||||
# must not be happen
|
||||
discard
|
||||
elif ret == WAIT_FAILED:
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
var status : cint = 1
|
||||
# waiting for all children, get result if any child exits
|
||||
let res = waitpid(-1, status, 0)
|
||||
if res > 0:
|
||||
for r in 0..m-1:
|
||||
if not isNil(q[r]) and q[r].id == res:
|
||||
# we updating `exitStatus` manually, so `running()` can work.
|
||||
if WIFEXITED(status) or WIFSIGNALED(status):
|
||||
q[r].exitStatus = status
|
||||
break
|
||||
else:
|
||||
let err = osLastError()
|
||||
if err == OSErrorCode(ECHILD):
|
||||
# some child exits, we need to check our childs exit codes
|
||||
discard
|
||||
elif err == OSErrorCode(EINTR):
|
||||
# signal interrupted our syscall, lets repeat it
|
||||
continue
|
||||
else:
|
||||
# all other errors are exceptions
|
||||
raiseOSError(err)
|
||||
|
||||
for r in 0..m-1:
|
||||
if not isNil(q[r]):
|
||||
if not running(q[r]):
|
||||
#echo(outputStream(q[r]).readLine())
|
||||
result = max(waitForExit(q[r]), result)
|
||||
result = max(result, q[r].peekExitCode())
|
||||
if afterRunEvent != nil: afterRunEvent(r, q[r])
|
||||
if q[r] != nil: close(q[r])
|
||||
if beforeRunEvent != nil:
|
||||
beforeRunEvent(i)
|
||||
q[r] = startProcess(cmds[i], options=options + {poEvalCommand})
|
||||
inc(i)
|
||||
if i > high(cmds): break
|
||||
for j in 0..m-1:
|
||||
result = max(waitForExit(q[j]), result)
|
||||
if afterRunEvent != nil: afterRunEvent(j, q[j])
|
||||
if q[j] != nil: close(q[j])
|
||||
close(q[r])
|
||||
if i < len(cmds):
|
||||
if beforeRunEvent != nil: beforeRunEvent(i)
|
||||
q[r] = startProcess(cmds[i],
|
||||
options = options + {poEvalCommand})
|
||||
when defined(windows):
|
||||
w[r] = q[r].fProcessHandle
|
||||
inc(i)
|
||||
else:
|
||||
q[r] = nil
|
||||
when defined(windows):
|
||||
for c in r..MAXIMUM_WAIT_OBJECTS - 2:
|
||||
w[c] = w[c + 1]
|
||||
dec(wcount)
|
||||
dec(ecount)
|
||||
else:
|
||||
for i in 0..high(cmds):
|
||||
if beforeRunEvent != nil:
|
||||
@@ -944,19 +967,22 @@ elif not defined(useNimRtl):
|
||||
if kill(p.id, SIGCONT) != 0'i32: raiseOsError(osLastError())
|
||||
|
||||
proc running(p: Process): bool =
|
||||
var ret : int
|
||||
var status : cint = 1
|
||||
ret = waitpid(p.id, status, WNOHANG)
|
||||
if ret == int(p.id):
|
||||
if isExitStatus(status):
|
||||
p.exitStatus = status
|
||||
return false
|
||||
else:
|
||||
return true
|
||||
elif ret == 0:
|
||||
return true # Can't establish status. Assume running.
|
||||
else:
|
||||
if p.exitStatus != -3:
|
||||
return false
|
||||
else:
|
||||
var ret : int
|
||||
var status : cint = 1
|
||||
ret = waitpid(p.id, status, WNOHANG)
|
||||
if ret == int(p.id):
|
||||
if isExitStatus(status):
|
||||
p.exitStatus = status
|
||||
return false
|
||||
else:
|
||||
return true
|
||||
elif ret == 0:
|
||||
return true # Can't establish status. Assume running.
|
||||
else:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc terminate(p: Process) =
|
||||
if kill(p.id, SIGTERM) != 0'i32:
|
||||
|
||||
32
tests/osproc/texecps.nim
Normal file
32
tests/osproc/texecps.nim
Normal file
@@ -0,0 +1,32 @@
|
||||
discard """
|
||||
file: "texecps.nim"
|
||||
output: ""
|
||||
"""
|
||||
|
||||
import osproc, streams, strutils, os
|
||||
|
||||
const NumberOfProcesses = 13
|
||||
|
||||
var gResults {.threadvar.}: seq[string]
|
||||
|
||||
proc execCb(idx: int, p: Process) =
|
||||
let exitCode = p.peekExitCode
|
||||
if exitCode < len(gResults):
|
||||
gResults[exitCode] = p.outputStream.readAll.strip
|
||||
|
||||
when isMainModule:
|
||||
|
||||
if paramCount() == 0:
|
||||
gResults = newSeq[string](NumberOfProcesses)
|
||||
var checks = newSeq[string](NumberOfProcesses)
|
||||
var commands = newSeq[string](NumberOfProcesses)
|
||||
for i in 0..len(commands) - 1:
|
||||
commands[i] = getAppFileName() & " " & $i
|
||||
checks[i] = $i
|
||||
let cres = execProcesses(commands, options = {poStdErrToStdOut},
|
||||
afterRunEvent = execCb)
|
||||
doAssert(cres == len(commands) - 1)
|
||||
doAssert(gResults == checks)
|
||||
else:
|
||||
echo paramStr(1)
|
||||
programResult = parseInt(paramStr(1))
|
||||
Reference in New Issue
Block a user