diff options
author | cheatfate <ka@hardcore.kiev.ua> | 2017-12-11 21:12:07 +0200 |
---|---|---|
committer | cheatfate <ka@hardcore.kiev.ua> | 2017-12-11 21:12:07 +0200 |
commit | 59d45305629e54b8cc9a1f2e0991609363d792a4 (patch) | |
tree | bb1a557e7c40962e983c6f1eaea815651cb13a94 | |
parent | 28e0bf9dcd62f387c79f767848cadd8b71d825de (diff) | |
download | Nim-59d45305629e54b8cc9a1f2e0991609363d792a4.tar.gz |
Remove `-3` as marker of exited process.
Cache exiting process for Windows to omit unnecessary syscalls. Fix closing hThread for Windows. Fix for pause/resume on Windows. Fix process handle leak on Windows. Change behavior for waitForExit on Windows.
-rw-r--r-- | lib/pure/osproc.nim | 118 | ||||
-rw-r--r-- | lib/windows/winlean.nim | 1 |
2 files changed, 78 insertions, 41 deletions
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 2a1ce0c58..f762a713b 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -47,6 +47,7 @@ type ProcessObj = object of RootObj when defined(windows): fProcessHandle: Handle + fThreadHandle: Handle inHandle, outHandle, errHandle: FileHandle id: Handle else: @@ -54,6 +55,7 @@ type inStream, outStream, errStream: Stream id: Pid exitStatus: cint + exitFlag: bool options: set[ProcessOption] Process* = ref ProcessObj ## represents an operating system process @@ -237,11 +239,13 @@ proc execProcesses*(cmds: openArray[string], if n > 1: var i = 0 var q = newSeq[Process](n) - var m = min(n, cmds.len) when defined(windows): var w: WOHandleArray + var m = min(min(n, MAXIMUM_WAIT_OBJECTS), cmds.len) var wcount = m + else: + var m = min(n, cmds.len) while i < m: if beforeRunEvent != nil: @@ -262,8 +266,17 @@ proc execProcesses*(cmds: openArray[string], discard elif ret == WAIT_FAILED: raiseOSError(osLastError()) + else: + var status: int32 + for r in 0..m-1: + if not isNil(q[r]) and q[r].fProcessHandle == w[ret]: + discard getExitCodeProcess(q[r].fProcessHandle, status) + q[r].exitFlag = true + q[r].exitStatus = status + discard closeHandle(q[r].fProcessHandle) + break else: - var status : cint = 1 + var status: cint = 1 # waiting for all children, get result if any child exits let res = waitpid(-1, status, 0) if res > 0: @@ -271,6 +284,7 @@ proc execProcesses*(cmds: openArray[string], 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].exitFlag = true q[r].exitStatus = status break else: @@ -491,6 +505,7 @@ when defined(Windows) and not defined(useNimRtl): hi, ho, he: Handle new(result) result.options = options + result.exitFlag = true si.cb = sizeof(si).cint if poParentStreams notin options: si.dwFlags = STARTF_USESTDHANDLES # STARTF_USESHOWWINDOW or @@ -559,28 +574,31 @@ when defined(Windows) and not defined(useNimRtl): "Requested command not found: '$1'. OS error:" % command) else: raiseOSError(lastError, command) - # Close the handle now so anyone waiting is woken: - discard closeHandle(procInfo.hThread) + result.fProcessHandle = procInfo.hProcess + result.fThreadHandle = procInfo.hThread result.id = procInfo.dwProcessId + result.exitFlag = false proc close(p: Process) = if poInteractive in p.options: - # somehow this is not always required on Windows: discard closeHandle(p.inHandle) discard closeHandle(p.outHandle) discard closeHandle(p.errHandle) - #discard closeHandle(p.FProcessHandle) + discard closeHandle(p.fProcessHandle) proc suspend(p: Process) = - discard suspendThread(p.fProcessHandle) + discard suspendThread(p.fThreadHandle) proc resume(p: Process) = - discard resumeThread(p.fProcessHandle) + discard resumeThread(p.fThreadHandle) proc running(p: Process): bool = - var x = waitForSingleObject(p.fProcessHandle, 50) - return x == WAIT_TIMEOUT + if p.exitFlag: + return false + else: + var x = waitForSingleObject(p.fProcessHandle, 0) + return x == WAIT_TIMEOUT proc terminate(p: Process) = if running(p): @@ -590,22 +608,35 @@ when defined(Windows) and not defined(useNimRtl): terminate(p) proc waitForExit(p: Process, timeout: int = -1): int = - discard waitForSingleObject(p.fProcessHandle, timeout.int32) - - var res: int32 - discard getExitCodeProcess(p.fProcessHandle, res) - result = res - p.exitStatus = res - discard closeHandle(p.fProcessHandle) + if p.exitFlag: + return p.exitStatus + + let res = waitForSingleObject(p.fProcessHandle, timeout.int32) + if res == WAIT_TIMEOUT: + terminate(p) + var status: int32 + discard getExitCodeProcess(p.fProcessHandle, status) + if status != STILL_ACTIVE: + p.exitFlag = true + p.exitStatus = status + discard closeHandle(p.fProcessHandle) + result = status + else: + result = -1 proc peekExitCode(p: Process): int = - var b = waitForSingleObject(p.fProcessHandle, 50) == WAIT_TIMEOUT - if b: result = -1 - else: - var res: int32 - discard getExitCodeProcess(p.fProcessHandle, res) - if res == 0: return p.exitStatus - return res + if p.exitFlag: + return p.exitStatus + + result = -1 + var b = waitForSingleObject(p.fProcessHandle, 0) == WAIT_TIMEOUT + if not b: + var status: int32 + discard getExitCodeProcess(p.fProcessHandle, status) + p.exitFlag = true + p.exitStatus = status + discard closeHandle(p.fProcessHandle) + result = status proc inputStream(p: Process): Stream = streamAccess(p) @@ -737,7 +768,8 @@ elif not defined(useNimRtl): pStdin, pStdout, pStderr: array[0..1, cint] new(result) result.options = options - result.exitStatus = -3 # for ``waitForExit`` + result.exitFlag = true + if poParentStreams notin options: if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or pipe(pStderr) != 0'i32: @@ -792,6 +824,7 @@ elif not defined(useNimRtl): if poEchoCmd in options: echo(command, " ", join(args, " ")) result.id = pid + result.exitFlag = false if poParentStreams in options: # does not make much sense, but better than nothing: @@ -968,14 +1001,14 @@ elif not defined(useNimRtl): if kill(p.id, SIGCONT) != 0'i32: raiseOsError(osLastError()) proc running(p: Process): bool = - if p.exitStatus != -3: + if p.exitFlag: return false else: - var ret : int - var status : cint = 1 - ret = waitpid(p.id, status, WNOHANG) + var status: cint = 1 + let ret = waitpid(p.id, status, WNOHANG) if ret == int(p.id): if isExitStatus(status): + p.exitFlag = true p.exitStatus = status return false else: @@ -998,13 +1031,14 @@ elif not defined(useNimRtl): import kqueue, times proc waitForExit(p: Process, timeout: int = -1): int = - if p.exitStatus != -3: + if p.exitFlag: return exitStatus(p.exitStatus) if timeout == -1: - var status : cint = 1 + var status: cint = 1 if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitFlag = true p.exitStatus = status else: var kqFD = kqueue() @@ -1025,7 +1059,7 @@ elif not defined(useNimRtl): try: while true: - var status : cint = 1 + var status: cint = 1 var count = kevent(kqFD, addr(kevIn), 1, addr(kevOut), 1, addr(tmspec)) if count < 0: @@ -1038,12 +1072,14 @@ elif not defined(useNimRtl): raiseOSError(osLastError()) if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitFlag = true p.exitStatus = status break else: if kevOut.ident == p.id.uint and kevOut.filter == EVFILT_PROC: if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitFlag = true p.exitStatus = status break else: @@ -1083,17 +1119,14 @@ elif not defined(useNimRtl): s.tv_sec = b.tv_sec s.tv_nsec = b.tv_nsec - #if waitPid(p.id, p.exitStatus, 0) == int(p.id): - # ``waitPid`` fails if the process is not running anymore. But then - # ``running`` probably set ``p.exitStatus`` for us. Since ``p.exitStatus`` is - # initialized with -3, wrong success exit codes are prevented. - if p.exitStatus != -3: + if p.exitFlag: return exitStatus(p.exitStatus) if timeout == -1: - var status : cint = 1 + var status: cint = 1 if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitFlag = true p.exitStatus = status else: var nmask, omask: Sigset @@ -1125,9 +1158,10 @@ elif not defined(useNimRtl): let res = sigtimedwait(nmask, sinfo, tmspec) if res == SIGCHLD: if sinfo.si_pid == p.id: - var status : cint = 1 + var status: cint = 1 if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitFlag = true p.exitStatus = status break else: @@ -1148,9 +1182,10 @@ elif not defined(useNimRtl): # timeout expired, so we trying to kill process if posix.kill(p.id, SIGKILL) == -1: raiseOSError(osLastError()) - var status : cint = 1 + var status: cint = 1 if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitFlag = true p.exitStatus = status break else: @@ -1168,12 +1203,13 @@ elif not defined(useNimRtl): proc peekExitCode(p: Process): int = var status = cint(0) result = -1 - if p.exitStatus != -3: + if p.exitFlag: return exitStatus(p.exitStatus) var ret = waitpid(p.id, status, WNOHANG) if ret > 0: if isExitStatus(status): + p.exitFlag = true p.exitStatus = status result = exitStatus(status) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 7eb268a9a..a833377e5 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -111,6 +111,7 @@ const WAIT_TIMEOUT* = 0x00000102'i32 WAIT_FAILED* = 0xFFFFFFFF'i32 INFINITE* = -1'i32 + STILL_ACTIVE* = 0x00000103'i32 STD_INPUT_HANDLE* = -10'i32 STD_OUTPUT_HANDLE* = -11'i32 |