diff --git a/Foundation/include/Poco/Process_UNIX.h b/Foundation/include/Poco/Process_UNIX.h index 26666cb4be..ef7cca7380 100644 --- a/Foundation/include/Poco/Process_UNIX.h +++ b/Foundation/include/Poco/Process_UNIX.h @@ -19,11 +19,14 @@ #include "Poco/Foundation.h" +#include "Poco/Event.h" +#include "Poco/Mutex.h" #include "Poco/RefCountedObject.h" #include #include #include #include +#include namespace Poco { @@ -40,10 +43,13 @@ class Foundation_API ProcessHandleImpl: public RefCountedObject pid_t id() const; int wait() const; + int wait(int options) const; int tryWait() const; private: - std::atomic _pid; + const std::atomic _pid; + mutable Event _event; + mutable std::optional> _status; }; diff --git a/Foundation/src/Process_UNIX.cpp b/Foundation/src/Process_UNIX.cpp index 68c491e7cf..2b06550007 100644 --- a/Foundation/src/Process_UNIX.cpp +++ b/Foundation/src/Process_UNIX.cpp @@ -40,7 +40,8 @@ namespace Poco { // ProcessHandleImpl // ProcessHandleImpl::ProcessHandleImpl(pid_t pid): - _pid(pid) + _pid(pid), + _event(Event::EVENT_MANUALRESET) { } @@ -58,20 +59,40 @@ pid_t ProcessHandleImpl::id() const int ProcessHandleImpl::wait() const { + if (wait(0) != _pid) + throw SystemException("Cannot wait for process", NumberFormatter::format(_pid)); + + return WEXITSTATUS(_status.value()); +} + + +int ProcessHandleImpl::wait(int options) const +{ + if (_status.has_value()) return _pid; int status; int rc; do { - rc = waitpid(_pid, &status, 0); + rc = waitpid(_pid, &status, options); } while (rc < 0 && errno == EINTR); - if (rc != _pid) - throw SystemException("Cannot wait for process", NumberFormatter::format(_pid)); + if (rc == _pid) + { + _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(); - if (WIFEXITED(status)) // normal termination - return WEXITSTATUS(status); - else // termination by a signal - return 256 + WTERMSIG(status); + if (_status.has_value()) + { + rc = _pid; + } + } + + return rc; } diff --git a/Foundation/testsuite/src/ProcessTest.cpp b/Foundation/testsuite/src/ProcessTest.cpp index b70e157ba2..31a68fb848 100644 --- a/Foundation/testsuite/src/ProcessTest.cpp +++ b/Foundation/testsuite/src/ProcessTest.cpp @@ -16,6 +16,7 @@ #include "Poco/PipeStream.h" #include "Poco/Path.h" #include "Poco/Format.h" +#include "Poco/Thread.h" using namespace std::string_literals; @@ -25,6 +26,7 @@ using Poco::Pipe; using Poco::Path; using Poco::PipeInputStream; using Poco::PipeOutputStream; +using Poco::Thread; ProcessTest::ProcessTest(const std::string& name): CppUnit::TestCase(name) @@ -283,6 +285,45 @@ void ProcessTest::testIsRunning() } +void ProcessTest::testIsRunningAllowsForTermination() +{ +#if !defined(_WIN32_WCE) + std::string name("TestApp"); + std::string cmd; + +#if defined(POCO_OS_FAMILY_UNIX) + cmd = "./"; + cmd += name; +#else + cmd = name; +#endif + + std::vector args; + ProcessHandle ph = Process::launch(cmd, args, 0, 0, 0); + while (Process::isRunning(ph)) + Thread::sleep(100); +#endif // !defined(_WIN32_WCE) +} + + +void ProcessTest::testSignalExitCode() +{ +#if defined(POCO_OS_FAMILY_UNIX) + std::string name("TestApp"); + std::string cmd; + + cmd = "./"; + cmd += name; + + std::vector args; + args.push_back("-raise-int"); + ProcessHandle ph = Process::launch(cmd, args, 0, 0, 0); + int rc = ph.wait(); + assertEqual (-SIGINT, rc); +#endif // defined(POCO_OS_FAMILY_UNIX) +} + + void ProcessTest::setUp() { } @@ -305,6 +346,8 @@ CppUnit::Test* ProcessTest::suite() CppUnit_addTest(pSuite, ProcessTest, testLaunchArgs); CppUnit_addTest(pSuite, ProcessTest, testLaunchInvalidCommand); CppUnit_addTest(pSuite, ProcessTest, testIsRunning); + CppUnit_addTest(pSuite, ProcessTest, testIsRunningAllowsForTermination); + CppUnit_addTest(pSuite, ProcessTest, testSignalExitCode); return pSuite; } diff --git a/Foundation/testsuite/src/ProcessTest.h b/Foundation/testsuite/src/ProcessTest.h index cefbae39ca..90b3121243 100644 --- a/Foundation/testsuite/src/ProcessTest.h +++ b/Foundation/testsuite/src/ProcessTest.h @@ -32,6 +32,8 @@ class ProcessTest: public CppUnit::TestCase void testLaunchArgs(); void testLaunchInvalidCommand(); void testIsRunning(); + void testIsRunningAllowsForTermination(); + void testSignalExitCode(); void setUp(); void tearDown();