diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2021-11-16 08:54:55 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-11-16 08:54:55 -0500 |
| commit | c51f1e27f0e307a80a57a840b2337e3226b3c2be (patch) | |
| tree | 22a0d055683740fc21e402c91159baf1c7313e64 /source/core | |
| parent | 5a29c15cc3c227d9bb93a71cb50491a822d0ccf3 (diff) | |
Support around JSON-RPC (#2014)
* #include an absolute path didn't work - because paths were taken to always be relative.
* Use 'Process' to communicate with an command line tool.
* Remove slang-win-stream
* Tidy up windows ProcessUtil.
* First version of BufferedReadStream.
* Windows working IPC for steams.
* Test proxy count option.
* Split Process/ProcessUtil. Process is platform dependant. ProcessUtil are functions that are platform independent.
* First implementation of Unix Process interface.
* Unix process compiles on cygwin.
* Fix typo in unix process.
* Separate unix pipe stream error of invalid access, from pipe availability.
* Fix in standard line extraction.
* Make fd non blocking.
* Fix issues with Windows Process streams.
* Added UnixPipe.
* Some fixes around UnixPipeStream.
* Make a unix stream closed explicit.
* Hack to debug linux process/stream.
* Revert to old linux pipe handling.
* Pass executable path for unit tests.
Split out CommandLine into own source.
* Small improvements in process/command line.
* Check process behavior with crash.
* Make stderr and stdout unbuffered for crash testing.
* Only turn disable buffering in crash test.
* Disable crash test on CI.
* Fix crash on clang/linux.
* Enable crash test.
Remove _appendBuffer as can use StreamUtil functionality.
* Added inital processing for http headers.
* Small improvements to HttpHeader.
* First pass HTTPPacketConnection working on windows.
* Enable other Process communication tests.
* Update comments.
* WIP JSON RPC.
* Add terminate to Process.
Made JSONRPC a Util.
* Small tidy up around HTTPPacketConnection.
* Improve process termination options.
Co-authored-by: Yong He <yonghe@outlook.com>
Diffstat (limited to 'source/core')
| -rw-r--r-- | source/core/slang-http.cpp | 2 | ||||
| -rw-r--r-- | source/core/slang-http.h | 4 | ||||
| -rw-r--r-- | source/core/slang-process.h | 22 | ||||
| -rw-r--r-- | source/core/unix/slang-unix-process.cpp | 72 | ||||
| -rw-r--r-- | source/core/windows/slang-win-process.cpp | 50 |
5 files changed, 125 insertions, 25 deletions
diff --git a/source/core/slang-http.cpp b/source/core/slang-http.cpp index ea76d82a9..43be26c1c 100644 --- a/source/core/slang-http.cpp +++ b/source/core/slang-http.cpp @@ -287,7 +287,7 @@ SlangResult HTTPPacketConnection::update() return m_readResult; } -SlangResult HTTPPacketConnection::waitForContent() +SlangResult HTTPPacketConnection::waitForResult() { while (m_readState == ReadState::Header || m_readState == ReadState::Content) diff --git a/source/core/slang-http.h b/source/core/slang-http.h index a7c6f6628..a298e9e27 100644 --- a/source/core/slang-http.h +++ b/source/core/slang-http.h @@ -123,8 +123,8 @@ public: /// Write. Will potentially block if write stream is blocking. SlangResult write(const void* content, size_t sizeInBytes); - /// Blocks until full read packet is available or the stream is appropriately closed - SlangResult waitForContent(); + /// Blocks until some result - a packet, closure, or some kind of error + SlangResult waitForResult(); /// Consume the content - so can read next content void consumeContent(); diff --git a/source/core/slang-process.h b/source/core/slang-process.h index ddbe7cbf0..7cd7596ae 100644 --- a/source/core/slang-process.h +++ b/source/core/slang-process.h @@ -35,13 +35,27 @@ public: /// Get the stream for the type Stream* getStream(StreamType type) const { return m_streams[Index(type)]; } + + /// Get the value returned from the process when it exited/returned. int32_t getReturnValue() const { return m_returnValue; } /// True if the process has terminated virtual bool isTerminated() = 0; - /// Blocks until the process has completed - virtual void waitForTermination() = 0; + /// Blocks until the process has terminated or the timeout completes + /// Can optionally supply a timeout time. -1 means 'infinite' and is the default. + /// Note that the timeOut is only used approximately. + /// Returns true if has terminated. + virtual bool waitForTermination(Int timeOutInMs = -1) = 0; + + /// Terminate the process gracefully. + /// After calling it may take time before the process actually terminates + /// Ie calling isTerminated directly after `terminate` may return false. + /// The return code depending on implementation/termination style, may not be set. + virtual void terminate(int32_t returnCode) = 0; + + /// Kill the process - attempt to terminate immediately. + virtual void kill(int32_t returnCode) = 0; /// The quoting style used for the command line on this target. Currently just uses Space, /// but in future may take into account platform sec @@ -54,13 +68,15 @@ public: static SlangResult create(const CommandLine& commandLine, Process::Flags flags, RefPtr<Process>& outProcess); /// Sleep the current thread for time specified in milliseconds. 0 indicates to OS ok to yield this thread. - static void sleepCurrentThread(Index timeInMs); + static void sleepCurrentThread(Int timeInMs); /// Get a standard stream static SlangResult getStdStream(StreamType type, RefPtr<Stream>& out); + /// Get the clock frequency static uint64_t getClockFrequency(); + /// Get the clock tick. static uint64_t getClockTick(); protected: diff --git a/source/core/unix/slang-unix-process.cpp b/source/core/unix/slang-unix-process.cpp index 083160f02..5131e37ec 100644 --- a/source/core/unix/slang-unix-process.cpp +++ b/source/core/unix/slang-unix-process.cpp @@ -28,7 +28,9 @@ class UnixProcess : public Process public: // Process virtual bool isTerminated() SLANG_OVERRIDE; - virtual void waitForTermination() SLANG_OVERRIDE; + virtual bool waitForTermination(Int timeInMs) SLANG_OVERRIDE; + virtual void terminate(int32_t returnValue) SLANG_OVERRIDE; + virtual void kill(int32_t returnValue) SLANG_OVERRIDE; UnixProcess(pid_t pid, Stream*const* streams); @@ -133,9 +135,64 @@ bool UnixProcess::isTerminated() return _updateTerminationState(WNOHANG); } -void UnixProcess::waitForTermination() +bool UnixProcess::waitForTermination(Int timeInMs) { - while (!_updateTerminationState(0)); + // If < 0 we will wait blocking until terminated + if (timeInMs < 0) + { + while (!_updateTerminationState(0)); + return true; + } + + // Note that the amount of time waiting is very approximate (we are relying on sleeps time and don't take into + // account time outside of sleeping) + + // How often to test + const Int checkRateMs = 100; /// Check every 0.1 seconds + + while (timeInMs > 0) + { + if (_updateTerminationState(WNOHANG)) + { + return true; + } + + // Work out how long to sleep for + const Int sleepMs = (timeInMs >= checkRateMs) ? checkRateMs : timeInMs; + + // Sleep + sleepCurrentThread(sleepMs); + + timeInMs -= sleepMs; + } + + return _updateTerminationState(WNOHANG); +} + +void UnixProcess::terminate(int32_t returnValue) +{ + // Using this mechanism, we can't set a returnValue so just ignore + SLANG_UNUSED(returnValue); + + if (!isTerminated()) + { + // Request the process terminates + ::kill(m_pid, SIGTERM); + } +} + +void UnixProcess::kill(int32_t returnValue) +{ + if (!isTerminated()) + { + // We waited, lets just terminate with kill + ::kill(m_pid, SIGKILL); + + // Set the return value + m_returnValue = returnValue; + // Mark as terminated + m_isTerminated = true; + } } /* !!!!!!!!!!!!!!!!!!!!!! UnixPipeStream !!!!!!!!!!!!!!!!!!!!!!!!!!!! */ @@ -386,15 +443,20 @@ SlangResult UnixPipeStream::write(const void* buffer, size_t length) return uint64_t(now.tv_sec) * 1000000000 + now.tv_nsec; } -/* static */void Process::sleepCurrentThread(Index timeInMs) +/* static */void Process::sleepCurrentThread(Int timeInMs) { struct timespec timeSpec; - if (timeInMs > 0) + if (timeInMs >= 1000) { timeSpec.tv_sec = timeInMs / 1000; timeSpec.tv_nsec = (timeInMs % 1000) * 1000 * 1000; } + else if (timeInMs > 0) + { + timeSpec.tv_sec = 0; + timeSpec.tv_nsec = timeInMs * 1000 * 1000; + } else { timeSpec.tv_sec = 0; diff --git a/source/core/windows/slang-win-process.cpp b/source/core/windows/slang-win-process.cpp index c37936a30..ed06f5692 100644 --- a/source/core/windows/slang-win-process.cpp +++ b/source/core/windows/slang-win-process.cpp @@ -111,8 +111,11 @@ class WinProcess : public Process { public: + // Process virtual bool isTerminated() SLANG_OVERRIDE; - virtual void waitForTermination() SLANG_OVERRIDE; + virtual bool waitForTermination(Int timeInMs) SLANG_OVERRIDE; + virtual void terminate(int32_t returnCode) SLANG_OVERRIDE; + virtual void kill(int32_t returnCode) SLANG_OVERRIDE; WinProcess(HANDLE handle, Stream* const* streams) : m_processHandle(handle) @@ -300,7 +303,7 @@ void WinProcess::_hasTerminated() // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess DWORD childExitCode = 0; - if (GetExitCodeProcess(m_processHandle, &childExitCode)) + if (::GetExitCodeProcess(m_processHandle, &childExitCode)) { m_returnValue = int32_t(childExitCode); } @@ -308,35 +311,54 @@ void WinProcess::_hasTerminated() } } -void WinProcess::waitForTermination() +bool WinProcess::waitForTermination(Int timeInMs) { if (m_processHandle.isNull()) { - return; + return true; } + const DWORD timeOutTime = (timeInMs < 0) ? INFINITE : DWORD(timeInMs); + // wait for the process to exit // TODO: set a timeout as a safety measure... - WaitForSingleObject(m_processHandle, INFINITE); + auto res = ::WaitForSingleObject(m_processHandle, timeOutTime); + + if (res == WAIT_TIMEOUT) + { + return false; + } _hasTerminated(); + return true; } bool WinProcess::isTerminated() { - if (m_processHandle.isNull()) + return waitForTermination(0); +} + +void WinProcess::terminate(int32_t returnCode) +{ + if (!isTerminated()) { - return true; + // If it's not terminated, try terminating. + // Might take time, so use isTerminated to check + ::TerminateProcess(m_processHandle, UINT32(returnCode)); } +} - auto res = WaitForSingleObject(m_processHandle, 0); - - if (res == WAIT_TIMEOUT) +void WinProcess::kill(int32_t returnCode) +{ + if (!isTerminated()) { - return false; + // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess + ::TerminateProcess(m_processHandle, UINT32(returnCode)); + + // Just assume it's done and set the return code + m_returnValue = returnCode; + m_processHandle.setNull(); } - _hasTerminated(); - return true; } /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ @@ -505,7 +527,7 @@ bool WinProcess::isTerminated() return SLANG_OK; } -/* static */void Process::sleepCurrentThread(Index timeInMs) +/* static */void Process::sleepCurrentThread(Int timeInMs) { ::Sleep(DWORD(timeInMs)); } |
