mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-28 11:31:53 +01:00
Allow for process termination when polling with isRunning
On *NIX, one needs to call `waitpid()` in order for process to exit the zombie state. If one uses `Process::isRunning()` to emulate non-blocking wait for child process termination, process will stay zombie and function will always return true. This commit changes `Process::isRunning()` to call `waitpid()` with `WNOHANG` instead of using `kill()` when checking for child process (i.e. the one we have ProcessHandle for), which allows for process termination. Additional trickery with mutex and event is needed to prevent exceptions when `Process::isRunning()` and/or `Process::wait()` is called concurrently on the same handle from different threads. Fixes #1097.
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
#include "Poco/Exception.h"
|
||||
#include "Poco/NumberFormatter.h"
|
||||
#include "Poco/Pipe.h"
|
||||
#include "Poco/Thread.h"
|
||||
#include <limits>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
@@ -42,7 +43,10 @@ namespace Poco {
|
||||
// ProcessHandleImpl
|
||||
//
|
||||
ProcessHandleImpl::ProcessHandleImpl(pid_t pid):
|
||||
_pid(pid)
|
||||
_pid(pid),
|
||||
_mutex(),
|
||||
_event(Event::EVENT_MANUALRESET),
|
||||
_status()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -60,24 +64,60 @@ pid_t ProcessHandleImpl::id() const
|
||||
|
||||
int ProcessHandleImpl::wait() const
|
||||
{
|
||||
int status;
|
||||
int rc;
|
||||
do
|
||||
{
|
||||
rc = waitpid(_pid, &status, 0);
|
||||
}
|
||||
while (rc < 0 && errno == EINTR);
|
||||
if (rc != _pid)
|
||||
if (wait(0) != _pid)
|
||||
throw SystemException("Cannot wait for process", NumberFormatter::format(_pid));
|
||||
|
||||
const int status = _status.value();
|
||||
if (WIFEXITED(status))
|
||||
return WEXITSTATUS(status);
|
||||
if (WIFSIGNALED(status))
|
||||
return -WTERMSIG(status);
|
||||
|
||||
// This line should never be reached.
|
||||
return std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
|
||||
int ProcessHandleImpl::wait(int options) const
|
||||
{
|
||||
{
|
||||
FastMutex::ScopedLock lock(_mutex);
|
||||
if (_status.isSpecified())
|
||||
{
|
||||
return _pid;
|
||||
}
|
||||
}
|
||||
|
||||
int status;
|
||||
int rc;
|
||||
do
|
||||
{
|
||||
rc = waitpid(_pid, &status, options);
|
||||
}
|
||||
while (rc < 0 && errno == EINTR);
|
||||
|
||||
if (rc == _pid)
|
||||
{
|
||||
FastMutex::ScopedLock lock(_mutex);
|
||||
_status = status;
|
||||
_event.set();
|
||||
}
|
||||
else if (rc < 0 && errno == ECHILD)
|
||||
{
|
||||
// Looks like another thread was lucky and it should update the status for us shortly
|
||||
_event.wait();
|
||||
|
||||
FastMutex::ScopedLock lock(_mutex);
|
||||
if (_status.isSpecified())
|
||||
{
|
||||
rc = _pid;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ProcessImpl
|
||||
//
|
||||
@@ -251,7 +291,7 @@ void ProcessImpl::killImpl(PIDImpl pid)
|
||||
|
||||
bool ProcessImpl::isRunningImpl(const ProcessHandleImpl& handle)
|
||||
{
|
||||
return isRunningImpl(handle.id());
|
||||
return handle.wait(WNOHANG) == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user