summaryrefslogtreecommitdiff
path: root/source/core
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2021-11-16 08:54:55 -0500
committerGitHub <noreply@github.com>2021-11-16 08:54:55 -0500
commitc51f1e27f0e307a80a57a840b2337e3226b3c2be (patch)
tree22a0d055683740fc21e402c91159baf1c7313e64 /source/core
parent5a29c15cc3c227d9bb93a71cb50491a822d0ccf3 (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.cpp2
-rw-r--r--source/core/slang-http.h4
-rw-r--r--source/core/slang-process.h22
-rw-r--r--source/core/unix/slang-unix-process.cpp72
-rw-r--r--source/core/windows/slang-win-process.cpp50
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));
}