summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2017-12-01 02:06:51 +0100
committerAndreas Rumpf <rumpf_a@web.de>2017-12-01 02:06:51 +0100
commit8646a567384a8eb5c36be3f21eca1092b555ea73 (patch)
tree41f5717a56dc0abba625ad45eb571fb2822b34bf /lib
parent547961f71e75594ae1d9e2e26b0e54e0a63008e3 (diff)
parentfbe0ae74fba5a22bbaea0cc8e68dbf9327c4ce6d (diff)
downloadNim-8646a567384a8eb5c36be3f21eca1092b555ea73.tar.gz
Merge branch 'nexecProcesses' of https://github.com/cheatfate/Nim into cheatfate-nexecProcesses
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/osproc.nim138
1 files changed, 82 insertions, 56 deletions
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 9865f114f..5440ceb67 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -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: