diff --git a/Data/src/sql-parser/src/sqlparser_win.h b/Data/src/sql-parser/src/sqlparser_win.h index 121f28a713..f7a273f859 100644 --- a/Data/src/sql-parser/src/sqlparser_win.h +++ b/Data/src/sql-parser/src/sqlparser_win.h @@ -11,7 +11,7 @@ #ifdef Data_API #define SQLParser_API Data_API - #ifdef Data_EXPORTS + #if defined(Data_EXPORTS) && !defined(SQLParser_EXPORTS) #define SQLParser_EXPORTS #endif #else diff --git a/Foundation/Foundation_vs140.vcxproj b/Foundation/Foundation_vs140.vcxproj index 1ce7d199bf..878e178e49 100644 --- a/Foundation/Foundation_vs140.vcxproj +++ b/Foundation/Foundation_vs140.vcxproj @@ -1093,6 +1093,7 @@ + @@ -1168,6 +1169,7 @@ true true + @@ -1655,6 +1657,7 @@ + @@ -1673,6 +1676,7 @@ + diff --git a/Foundation/Foundation_vs140.vcxproj.filters b/Foundation/Foundation_vs140.vcxproj.filters index 3198581641..4989d157a9 100644 --- a/Foundation/Foundation_vs140.vcxproj.filters +++ b/Foundation/Foundation_vs140.vcxproj.filters @@ -642,6 +642,9 @@ Processes\Source Files + + Processes\Source Files + Processes\Source Files @@ -669,6 +672,9 @@ Processes\Source Files + + Processes\Source Files + Processes\Source Files @@ -1514,6 +1520,9 @@ Processes\Header Files + + Processes\Header Files + Processes\Header Files @@ -1541,6 +1550,9 @@ Processes\Header Files + + Processes\Header Files + Processes\Header Files diff --git a/Foundation/Foundation_vs150.vcxproj b/Foundation/Foundation_vs150.vcxproj index 960d00aba2..e48de4e185 100644 --- a/Foundation/Foundation_vs150.vcxproj +++ b/Foundation/Foundation_vs150.vcxproj @@ -1093,6 +1093,7 @@ + @@ -1168,6 +1169,7 @@ true true + @@ -1655,6 +1657,7 @@ + @@ -1673,6 +1676,7 @@ + diff --git a/Foundation/Foundation_vs150.vcxproj.filters b/Foundation/Foundation_vs150.vcxproj.filters index e97e0132cc..a8fc29f2dc 100644 --- a/Foundation/Foundation_vs150.vcxproj.filters +++ b/Foundation/Foundation_vs150.vcxproj.filters @@ -642,6 +642,9 @@ Processes\Source Files + + Processes\Source Files + Processes\Source Files @@ -669,6 +672,9 @@ Processes\Source Files + + Processes\Source Files + Processes\Source Files @@ -1514,6 +1520,9 @@ Processes\Header Files + + Processes\Header Files + Processes\Header Files @@ -1541,6 +1550,9 @@ Processes\Header Files + + Processes\Header Files + Processes\Header Files diff --git a/Foundation/Foundation_vs160.vcxproj b/Foundation/Foundation_vs160.vcxproj index 99854dff36..791df83912 100644 --- a/Foundation/Foundation_vs160.vcxproj +++ b/Foundation/Foundation_vs160.vcxproj @@ -1099,6 +1099,7 @@ + @@ -1174,6 +1175,7 @@ true true + @@ -1661,6 +1663,7 @@ + @@ -1679,6 +1682,7 @@ + diff --git a/Foundation/Foundation_vs160.vcxproj.filters b/Foundation/Foundation_vs160.vcxproj.filters index 07fcc124b2..2698f3fbbc 100644 --- a/Foundation/Foundation_vs160.vcxproj.filters +++ b/Foundation/Foundation_vs160.vcxproj.filters @@ -642,6 +642,9 @@ Processes\Source Files + + Processes\Source Files + Processes\Source Files @@ -669,6 +672,9 @@ Processes\Source Files + + Processes\Source Files + Processes\Source Files @@ -1514,6 +1520,9 @@ Processes\Header Files + + Processes\Header Files + Processes\Header Files @@ -1541,6 +1550,9 @@ Processes\Header Files + + Processes\Header Files + Processes\Header Files diff --git a/Foundation/Foundation_vs170.vcxproj b/Foundation/Foundation_vs170.vcxproj index 0eac76c958..f79c42792f 100644 --- a/Foundation/Foundation_vs170.vcxproj +++ b/Foundation/Foundation_vs170.vcxproj @@ -1550,6 +1550,7 @@ + @@ -1655,6 +1656,7 @@ true true + @@ -2244,6 +2246,7 @@ + @@ -2262,6 +2265,7 @@ + diff --git a/Foundation/Foundation_vs170.vcxproj.filters b/Foundation/Foundation_vs170.vcxproj.filters index fc8cd62cea..15652e5bdf 100644 --- a/Foundation/Foundation_vs170.vcxproj.filters +++ b/Foundation/Foundation_vs170.vcxproj.filters @@ -642,6 +642,9 @@ Processes\Source Files + + Processes\Source Files + Processes\Source Files @@ -669,6 +672,9 @@ Processes\Source Files + + Processes\Source Files + Processes\Source Files @@ -1514,6 +1520,9 @@ Processes\Header Files + + Processes\Header Files + Processes\Header Files @@ -1541,6 +1550,9 @@ Processes\Header Files + + Processes\Header Files + Processes\Header Files diff --git a/Foundation/Makefile b/Foundation/Makefile index 447fa3629b..96e0100e52 100644 --- a/Foundation/Makefile +++ b/Foundation/Makefile @@ -19,7 +19,7 @@ objects = ArchiveStrategy Ascii ASCIIEncoding AsyncChannel ActiveThreadPool\ NestedDiagnosticContext Notification NotificationCenter \ NotificationQueue PriorityNotificationQueue TimedNotificationQueue \ NullStream NumberFormatter NumberParser NumericString AbstractObserver \ - Path PatternFormatter Process PurgeStrategy RWLock Random RandomStream \ + Path PatternFormatter PIDFile Process ProcessRunner PurgeStrategy RWLock Random RandomStream \ DirectoryIteratorStrategy RegularExpression RefCountedObject Runnable RotateStrategy \ SHA1Engine SHA2Engine Semaphore SharedLibrary SimpleFileChannel \ SignalHandler SplitterChannel SortedDirectoryIterator Stopwatch StreamChannel \ diff --git a/Foundation/include/Poco/PIDFile.h b/Foundation/include/Poco/PIDFile.h new file mode 100644 index 0000000000..ba50870863 --- /dev/null +++ b/Foundation/include/Poco/PIDFile.h @@ -0,0 +1,101 @@ +// +// PIDFile.h +// +// Library: Foundation +// Package: Processes +// Module: PIDFile +// +// Definition of the PIDFile class. +// +// Copyright (c) 2023, Applied Informatics Software Engineering GmbH. +// Aleph ONE Software Engineering d.o.o., +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef Foundation_PIDFile_INCLUDED +#define Foundation_PIDFile_INCLUDED + + +#include "Poco/Foundation.h" +#include + + +namespace Poco { + + +class Foundation_API PIDFile + /// A utility class, creating process ID file on + /// construction and deleting it on destruction. +{ +public: + using Ptr = std::unique_ptr; + + static const int INVALID_PID = -1; + + PIDFile(); + /// Creates the PIDFile. + + PIDFile(const std::string& fileName, bool write = true); + /// Creates the PIDFile. + /// If `fileName` is not empty, creates the PID file. + /// If `write` is true, the file is written. + + ~PIDFile(); + /// Destroys the PIDFile. + /// If fileName is not empty, deletes the PID file. + + const std::string& getName() const; + /// Returns the file name. + + void setName(const std::string& fileName); + /// Sets the file name. + + void create(); + /// Creates the file and writes PID into it. + + void destroy(); + /// Deletes the PID file and invalidates the held PID. + + int getPID() const; + /// Returns the PID. + + bool exists() const; + /// Returns true if PID file exists and its content is + /// equal to the held PID. + + static bool contains(const std::string& fileName, int pid); + /// Returns true if the `fileName` contains the given `pid`. + + static std::string& getFileName(std::string& pidFile); + /// Returns the file name. + +private: + + std::string _fileName; + int _pid = INVALID_PID; +}; + + +// +// inlines +// + +inline const std::string& PIDFile::getName() const +{ + return _fileName; +} + + +inline int PIDFile::getPID() const +{ + return _pid; +} + + +} // namespace Poco + + +#endif // Foundation_ProcessRunner_INCLUDED diff --git a/Foundation/include/Poco/ProcessRunner.h b/Foundation/include/Poco/ProcessRunner.h new file mode 100644 index 0000000000..ed2fa9fdfe --- /dev/null +++ b/Foundation/include/Poco/ProcessRunner.h @@ -0,0 +1,199 @@ +// +// ProcessRunner.h +// +// Library: Foundation +// Package: Processes +// Module: ProcessRunner +// +// Definition of the ProcessRunner class. +// +// Copyright (c) 2023, Applied Informatics Software Engineering GmbH. +// Aleph ONE Software Engineering d.o.o., +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef Foundation_ProcessRunner_INCLUDED +#define Foundation_ProcessRunner_INCLUDED + + +#include "Poco/Foundation.h" +#include "Poco/Process.h" +#include "Poco/ProcessOptions.h" +#include "Poco/Runnable.h" +#include "Poco/Thread.h" +#include "Poco/Format.h" +#include "Poco/Stopwatch.h" +#include +#include + + +namespace Poco { + + +class Foundation_API ProcessRunner: public Poco::Runnable + /// ProcessRunner is a wrapper class for `Poco::ProcessHandle. + /// It starts and terminates a process with enabled or disabled (default) + /// `stdio` pipes, and optionally waits for process PID to be created before + /// returning control to the caller. The process is spawned from an + /// internal thread. Starting/stopping the process may block up to + /// a certain (configurable) period of time. + /// + /// ProcessRunner can hold and control only one process at a time, which + /// can be started/stopped multiple times during the ProcessRunner lifetime. +{ +public: + using Args = Poco::Process::Args; + using PID = Poco::ProcessHandle::PID; + + static const int NO_OUT = Poco::PROCESS_CLOSE_STDOUT|Poco::PROCESS_CLOSE_STDERR; + /// Constant to prevent std out and err from being received from the process. + + ProcessRunner(const std::string& cmd, + const Args& args, + const std::string& pidFile = "", + int options = NO_OUT, + int timeout = 10, /*seconds*/ + bool startProcess = true, + const Args& pidArgFmt = pidArgFormat()); + /// Creates the ProcessRunner. + /// + /// If `pidFile` is not empty, the starting of the process waits + /// until the pid file has been updated with the new pid, and + /// the stopping of the process waits until the pid file is gone. + /// Waiting is terminated after timeout seconds. + /// + /// If `pidFile` is empty and `pidArgFmt` is not empty, autodetect + /// of PID file from `args` is attempted; the default PID file + /// argument format corresponds to the one used by + /// `Poco::Util::Application` + /// + /// The `options` are passed to the process, defaulting to + /// closed stdio output pipes. + /// + /// The `timeout` in seconds determines how long the ProcessRunner + /// waits for the process to start; if PID file name is provided or + /// autodetected from arguments, ProcessRunner will wait until the file + /// exists and contains the process PID or timeout expires (in which + /// case a TimeoutException is thrown). + /// + /// If `startProcess` is true, the process is started on object creation. + + ~ProcessRunner(); + /// Destroys the ProcessRunner. + + PID pid() const; + /// Returns the process PID. + + const std::string& pidFile() const; + /// Returns the process PID filename. + /// Returns empty string when pid filename + /// is not specified at construction, either + /// explicitly, or implicitly through + /// command line argument. + + bool running() const; + /// Returns true if process is running. + + void start(); + /// Starts the process and waits for it to be fully initialized. + /// Process initialization completion is indicated by a new pid in + /// the pid file (if specified at construction, otherwise there + /// is no wating for pid). + /// If pid file is not specified, there is no waiting. + /// + /// Attempting to start a started process results in + /// Poco::InvalidAccessException being thrown. + + void stop(); + /// Stops the process. + /// + /// Calling stop() on a stopped process is a no-op. + + std::string cmdLine() const; + /// Returns process full command line. + + int result() const; + /// Returns process return code. + + int runCount() const; + /// Returns the number of times the process has been executed. + + +private: + static const Poco::ProcessHandle::PID INVALID_PID = -1; + static const int RESULT_UNKNOWN = -1; + + static Args pidArgFormat() + { +#if defined(POCO_OS_FAMILY_WINDOWS) + return Args{"-p", "--pidfile=", "/p", "/pidfile="}; +#else + return Args{"-p", "--pidfile="}; +#endif + } + + + void run(); + /// Starts the process and waits for it to be fully initialized. + /// Process initialization completion is indicated by new pid in + /// the pid file. If pid file is not specified, there is no waiting. + + void checkTimeout(const Poco::Stopwatch& sw, const std::string& msg); + /// If timeout is exceeded, throws TimeoutException with `msg` + /// message. + + Poco::Thread _t; + std::string _cmd; + Args _args; + std::atomic _pid; + std::string _pidFile; + int _options; + int _timeout; + std::atomic _pPH; + std::atomic _started; + std::atomic _rc; + std::atomic _runCount; +}; + + +// +// inlines +// + +inline const std::string& ProcessRunner::pidFile() const +{ + return _pidFile; +} + + +inline bool ProcessRunner::running() const +{ + return _pid != INVALID_PID; +} + + +inline ProcessRunner::PID ProcessRunner::pid() const +{ + return _pid; +} + + +inline int ProcessRunner::result() const +{ + return _rc; +} + + +inline int ProcessRunner::runCount() const +{ + return _runCount; +} + + +} // namespace Poco + + +#endif // Foundation_ProcessRunner_INCLUDED diff --git a/Foundation/include/Poco/Process_UNIX.h b/Foundation/include/Poco/Process_UNIX.h index 749ddde1a5..1012435857 100644 --- a/Foundation/include/Poco/Process_UNIX.h +++ b/Foundation/include/Poco/Process_UNIX.h @@ -23,6 +23,7 @@ #include #include #include +#include namespace Poco { @@ -42,7 +43,7 @@ class Foundation_API ProcessHandleImpl: public RefCountedObject int tryWait() const; private: - pid_t _pid; + std::atomic _pid; }; diff --git a/Foundation/include/Poco/Thread.h b/Foundation/include/Poco/Thread.h index c7e51e5979..30eaa2d518 100644 --- a/Foundation/include/Poco/Thread.h +++ b/Foundation/include/Poco/Thread.h @@ -74,11 +74,23 @@ class Foundation_API Thread: private ThreadImpl POLICY_DEFAULT = POLICY_DEFAULT_IMPL }; - Thread(); + Thread(uint32_t sigMask = 0); /// Creates a thread. Call start() to start it. + /// + /// The optional sigMask parameter specifies which signals should be blocked. + /// To block a specific signal, set the corresponding bit in the sigMask. + /// Multiple bits can be set in the mask to block multiple signals if needed. + /// + /// Available on POSIX platforms only - Thread(const std::string& name); + Thread(const std::string& name, uint32_t sigMask = 0); /// Creates a named thread. Call start() to start it. + /// + /// The optional sigMask parameter specifies which signals should be blocked. + /// To block a specific signal, set the corresponding bit in the sigMask. + /// Multiple bits can be set in the mask to block multiple signals if needed. + /// + /// Available on POSIX platforms only ~Thread(); /// Destroys the thread. diff --git a/Foundation/include/Poco/Thread_POSIX.h b/Foundation/include/Poco/Thread_POSIX.h index 4d8ef6a5ea..f758353d76 100644 --- a/Foundation/include/Poco/Thread_POSIX.h +++ b/Foundation/include/Poco/Thread_POSIX.h @@ -76,6 +76,7 @@ class Foundation_API ThreadImpl static int getMaxOSPriorityImpl(int policy); void setStackSizeImpl(int size); int getStackSizeImpl() const; + void setSignalMaskImpl(uint32_t sigMask); void startImpl(SharedPtr pTarget); void joinImpl(); bool joinImpl(long milliseconds); diff --git a/Foundation/src/PIDFile.cpp b/Foundation/src/PIDFile.cpp new file mode 100644 index 0000000000..ed657bf1c6 --- /dev/null +++ b/Foundation/src/PIDFile.cpp @@ -0,0 +1,122 @@ +// +// PIDFile.cpp +// +// Library: Foundation +// Package: Processes +// Module: PIDFile +// +// Copyright (c) 2023, Applied Informatics Software Engineering GmbH. +// Aleph ONE Software Engineering d.o.o., +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "Poco/PIDFile.h" +#include "Poco/Path.h" +#include "Poco/File.h" +#include "Poco/Process.h" +#include "Poco/FileStream.h" +#include + + +using Poco::Path; +using Poco::File; +using Poco::Process; +using Poco::FileInputStream; +using Poco::FileOutputStream; + + +namespace Poco { + + +PIDFile::PIDFile() +{ +} + + +PIDFile::PIDFile(const std::string& fileName, bool write): + _fileName(fileName) +{ + if (write) create(); +} + + +PIDFile::~PIDFile() +{ + destroy(); +} + + +void PIDFile::setName(const std::string& fileName) +{ + destroy(); + _fileName = fileName; + create(); +} + + +void PIDFile::create() +{ + if (!_fileName.empty()) + { + Path p(getFileName(_fileName)); + if (!File(p.makeParent()).exists()) + File(p).createDirectories(); + _pid = static_cast(Process::id()); + FileOutputStream fos(_fileName); + fos << _pid; fos.close(); + } +} + + +void PIDFile::destroy() +{ + if (!_fileName.empty()) + { + File f(_fileName); + if (f.exists()) f.remove(); + _fileName.clear(); + } + _pid = INVALID_PID; +} + + +bool PIDFile::exists() const +{ + if (File(_fileName).exists()) + { + FileInputStream fis(_fileName); + int fPID = 0; + if (fis.peek() != std::ifstream::traits_type::eof()) + fis >> fPID; + return fPID == _pid; + } + return false; +} + + +bool PIDFile::contains(const std::string& fileName, int pid) +{ + if (File(fileName).exists()) + { + FileInputStream fis(fileName); + int fPID = 0; + if (fis.peek() != std::ifstream::traits_type::eof()) + fis >> fPID; + return fPID == pid; + } + return false; +} + + +std::string& PIDFile::getFileName(std::string& pidFile) +{ + Path p(pidFile); + pidFile = p.makeAbsolute().toString(); + return pidFile; +} + + +} // namespace Poco diff --git a/Foundation/src/ProcessRunner.cpp b/Foundation/src/ProcessRunner.cpp new file mode 100644 index 0000000000..a4f868dda9 --- /dev/null +++ b/Foundation/src/ProcessRunner.cpp @@ -0,0 +1,217 @@ +// +// ProcessRunner.cpp +// +// Library: Foundation +// Package: Processes +// Module: ProcessRunner +// +// Copyright (c) 2023, Applied Informatics Software Engineering GmbH. +// Aleph ONE Software Engineering d.o.o., +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "Poco/ProcessRunner.h" +#include "Poco/PIDFile.h" +#include "Poco/FileStream.h" +#include "Poco/AutoPtr.h" +#include "Poco/File.h" +#include "Poco/Path.h" +#include "Poco/String.h" +#include + + +using Poco::Thread; +using Poco::Process; +using Poco::ProcessHandle; +using Poco::FileInputStream; +using Poco::AutoPtr; +using Poco::File; +using Poco::Path; +using Poco::Stopwatch; + + +namespace Poco { + + +ProcessRunner::ProcessRunner(const std::string& cmd, + const Args& args, + const std::string& pidFile, + int options, + int timeout, + bool startProcess, + const Args& pidArgFmt): _cmd(cmd), + _args(args), + _pid(INVALID_PID), + _pidFile(pidFile), + _options(options), + _timeout(timeout), + _pPH(nullptr), + _started(false), + _rc(RESULT_UNKNOWN), + _runCount(0) +{ + if (_pidFile.empty() && !_args.empty() && !pidArgFmt.empty()) + { + for (const auto& fmt : pidArgFmt) + { + for (const auto& arg : _args) + { + std::string a = Poco::trim(arg); + std::size_t pos = a.find(fmt); + if (pos == 0) + { + _pidFile = a.substr(fmt.length()); + PIDFile::getFileName(_pidFile); + break; + } + } + } + } + if (startProcess) start(); +} + + +ProcessRunner::~ProcessRunner() +{ + try + { + stop(); + } + catch (...) + { + poco_unexpected(); + } +} + + +std::string ProcessRunner::cmdLine() const +{ + std::string cmdL = _cmd + ' '; + auto it = _args.begin(); + auto end = _args.end(); + for (; it != end;) + { + cmdL.append(*it); + if (++it == end) break; + cmdL.append(1, ' '); + } + return cmdL; +} + + +void ProcessRunner::run() +{ + ProcessHandle* pPH = nullptr; + try + { + _pPH = pPH = new ProcessHandle(Process::launch(_cmd, _args, _options)); + _pid = pPH->id(); + _rc = pPH->wait(); + } + catch (...) + { + } + + _pid = INVALID_PID; + _pPH = nullptr; + ++_runCount; + delete pPH; +} + + +void ProcessRunner::stop() +{ + if (_started) + { + PID pid; + Stopwatch sw; sw.start(); + if (_pPH.exchange(nullptr) && ((pid = _pid.exchange(INVALID_PID))) != INVALID_PID) + { + while (Process::isRunning(pid)) + { + if (pid > 0) + { + Process::requestTermination(pid); + checkTimeout(sw, "Waiting for process termination"); + } + else throw Poco::IllegalStateException("Invalid PID, can;t terminate process"); + } + _t.join(); + } + + if (!_pidFile.empty()) + { + if (!_pidFile.empty()) + { + File pidFile(_pidFile); + _pidFile.clear(); + std::string msg; + Poco::format(msg, "Waiting for PID file (pidFile: '%s')", _pidFile); + sw.restart(); + while (pidFile.exists()) + checkTimeout(sw, msg); + } + } + } + _started.store(false); +} + + +void ProcessRunner::checkTimeout(const Stopwatch& sw, const std::string& msg) +{ + if (sw.elapsedSeconds() > _timeout) + { + throw Poco::TimeoutException( + Poco::format("ProcessRunner::checkTimeout(): %s", msg)); + } + Thread::sleep(10); +} + + +void ProcessRunner::start() +{ + if (!_started.exchange(true)) + { + int prevRunCnt = runCount(); + + _t.start(*this); + + std::string msg; + Poco::format(msg, "Waiting for process to start (pidFile: '%s')", _pidFile); + Stopwatch sw; sw.start(); + + // wait for the process to be either running or completed by monitoring run counts. + while (!running() && prevRunCnt >= runCount()) checkTimeout(sw, msg); + + // we could wait for the process handle != INVALID_PID, + // but if pidFile name was given, we should wait for + // the process to write it + if (!_pidFile.empty()) + { + sw.restart(); + // wait until process is fully initialized + File pidFile(_pidFile); + while (!pidFile.exists()) + checkTimeout(sw, "waiting for PID file"); + + // verify that the file content is actually the process PID + FileInputStream fis(_pidFile); + int fPID = 0; + if (fis.peek() != std::ifstream::traits_type::eof()) + fis >> fPID; + while (fPID != pid()) + { + fis.clear(); fis.seekg(0); fis >> fPID; + checkTimeout(sw, Poco::format("waiting for new PID (%s)", _pidFile)); + } + } + } + else + throw Poco::InvalidAccessException("start() called on started ProcessRunner"); +} + + +} // namespace Poco diff --git a/Foundation/src/Thread.cpp b/Foundation/src/Thread.cpp index de5cb3adb9..fdc2b3bae6 100644 --- a/Foundation/src/Thread.cpp +++ b/Foundation/src/Thread.cpp @@ -88,21 +88,27 @@ class CallableHolder: public Runnable } // namespace -Thread::Thread(): +Thread::Thread(uint32_t sigMask): _id(uniqueId()), _pTLS(0), _event(true) { setNameImpl(makeName()); +#if defined(POCO_OS_FAMILY_UNIX) + setSignalMaskImpl(sigMask); +#endif } -Thread::Thread(const std::string& name): +Thread::Thread(const std::string& name, uint32_t sigMask): _id(uniqueId()), _pTLS(0), _event(true) { setNameImpl(name); +#if defined(POCO_OS_FAMILY_UNIX) + setSignalMaskImpl(sigMask); +#endif } diff --git a/Foundation/src/Thread_POSIX.cpp b/Foundation/src/Thread_POSIX.cpp index 7f01d17c71..5e1c584424 100644 --- a/Foundation/src/Thread_POSIX.cpp +++ b/Foundation/src/Thread_POSIX.cpp @@ -257,6 +257,21 @@ void ThreadImpl::setStackSizeImpl(int size) } +void ThreadImpl::setSignalMaskImpl(uint32_t sigMask) +{ + sigset_t sset; + sigemptyset(&sset); + + for (int sig = 0; sig < sizeof(uint32_t) * 8; ++sig) + { + if ((sigMask & (1 << sig)) != 0) + sigaddset(&sset, sig); + } + + pthread_sigmask(SIG_BLOCK, &sset, 0); +} + + void ThreadImpl::startImpl(SharedPtr pTarget) { { diff --git a/Foundation/testsuite/Makefile-Driver b/Foundation/testsuite/Makefile-Driver index e5230f1a5a..1a2b3c64c7 100644 --- a/Foundation/testsuite/Makefile-Driver +++ b/Foundation/testsuite/Makefile-Driver @@ -20,7 +20,7 @@ objects = ActiveMethodTest ActivityTest ActiveDispatcherTest \ NDCTest NotificationCenterTest NotificationQueueTest \ PriorityNotificationQueueTest TimedNotificationQueueTest \ NotificationsTestSuite NullStreamTest NumberFormatterTest \ - NumberParserTest PathTest PatternFormatterTest PBKDF2EngineTest RWLockTest \ + NumberParserTest PathTest PatternFormatterTest PBKDF2EngineTest ProcessRunnerTest RWLockTest \ RandomStreamTest RandomTest RegularExpressionTest SHA1EngineTest SHA2EngineTest \ SemaphoreTest ConditionTest SharedLibraryTest SharedLibraryTestSuite \ SimpleFileChannelTest StopwatchTest \ diff --git a/Foundation/testsuite/TestApp_vs170.vcxproj b/Foundation/testsuite/TestApp_vs170.vcxproj index c6461b28f5..6d7c1f083f 100644 --- a/Foundation/testsuite/TestApp_vs170.vcxproj +++ b/Foundation/testsuite/TestApp_vs170.vcxproj @@ -344,7 +344,7 @@ Disabled - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;WINVER=0x0500;%(PreprocessorDefinitions) false EnableFastChecks @@ -361,8 +361,9 @@ true + ws2_32.lib;iphlpapi.lib;PocoFoundationd.lib;%(AdditionalDependencies) bin\TestAppd.exe - ..\..\..\lib;%(AdditionalLibraryDirectories) + ..\..\lib;%(AdditionalLibraryDirectories) true bin\TestAppd.pdb Console @@ -372,7 +373,7 @@ Disabled - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;WINVER=0x0500;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL @@ -388,8 +389,9 @@ true + ws2_32.lib;iphlpapi.lib;PocoFoundationd.lib;%(AdditionalDependencies) bin64\TestAppd.exe - ..\..\..\lib64;%(AdditionalLibraryDirectories) + ..\..\lib64;%(AdditionalLibraryDirectories) true bin64\TestAppd.pdb Console @@ -398,7 +400,7 @@ Disabled - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;WINVER=0x0500;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL @@ -414,8 +416,9 @@ true + ws2_32.lib;iphlpapi.lib;PocoFoundationd.lib;%(AdditionalDependencies) binA64\TestAppd.exe - ..\..\..\libA64;%(AdditionalLibraryDirectories) + ..\..\libA64;%(AdditionalLibraryDirectories) true binA64\TestAppd.pdb Console @@ -428,7 +431,7 @@ true Speed true - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;WINVER=0x0500;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -445,8 +448,9 @@ true + ws2_32.lib;iphlpapi.lib;PocoFoundation.lib;%(AdditionalDependencies) bin\TestApp.exe - ..\..\..\lib;%(AdditionalLibraryDirectories) + ..\..\lib;%(AdditionalLibraryDirectories) false @@ -463,7 +467,7 @@ true Speed true - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;WINVER=0x0500;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -480,8 +484,9 @@ true + ws2_32.lib;iphlpapi.lib;PocoFoundation.lib;%(AdditionalDependencies) bin64\TestApp.exe - ..\..\..\lib64;%(AdditionalLibraryDirectories) + ..\..\lib64;%(AdditionalLibraryDirectories) false @@ -497,7 +502,7 @@ true Speed true - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;WINVER=0x0500;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -514,8 +519,9 @@ true + ws2_32.lib;iphlpapi.lib;PocoFoundation.lib;%(AdditionalDependencies) binA64\TestApp.exe - ..\..\..\libA64;%(AdditionalLibraryDirectories) + ..\..\libA64;%(AdditionalLibraryDirectories) false @@ -531,7 +537,7 @@ true Speed true - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -548,8 +554,9 @@ true + PocoFoundationmd.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) bin\static_md\TestApp.exe - ..\..\..\lib;%(AdditionalLibraryDirectories) + ..\..\lib;%(AdditionalLibraryDirectories) false @@ -566,7 +573,7 @@ true Speed true - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -583,8 +590,9 @@ true + PocoFoundationmd.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) bin64\static_md\TestApp.exe - ..\..\..\lib64;%(AdditionalLibraryDirectories) + ..\..\lib64;%(AdditionalLibraryDirectories) false @@ -600,7 +608,7 @@ true Speed true - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -617,8 +625,9 @@ true + PocoFoundationmd.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) binA64\static_md\TestApp.exe - ..\..\..\libA64;%(AdditionalLibraryDirectories) + ..\..\libA64;%(AdditionalLibraryDirectories) false @@ -630,7 +639,7 @@ Disabled - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) false EnableFastChecks @@ -647,8 +656,9 @@ true + PocoFoundationmdd.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) bin\static_md\TestAppd.exe - ..\..\..\lib;%(AdditionalLibraryDirectories) + ..\..\lib;%(AdditionalLibraryDirectories) true bin\static_md\TestAppd.pdb Console @@ -658,7 +668,7 @@ Disabled - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL @@ -674,8 +684,9 @@ true + PocoFoundationmdd.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) bin64\static_md\TestAppd.exe - ..\..\..\lib64;%(AdditionalLibraryDirectories) + ..\..\lib64;%(AdditionalLibraryDirectories) true bin64\static_md\TestAppd.pdb Console @@ -684,7 +695,7 @@ Disabled - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL @@ -700,8 +711,9 @@ true + PocoFoundationmdd.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) binA64\static_md\TestAppd.exe - ..\..\..\libA64;%(AdditionalLibraryDirectories) + ..\..\libA64;%(AdditionalLibraryDirectories) true binA64\static_md\TestAppd.pdb Console @@ -710,7 +722,7 @@ Disabled - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) false EnableFastChecks @@ -727,8 +739,9 @@ true + PocoFoundationmtd.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) bin\static_mt\TestAppd.exe - ..\..\..\lib;%(AdditionalLibraryDirectories) + ..\..\lib;%(AdditionalLibraryDirectories) true bin\static_mt\TestAppd.pdb Console @@ -738,7 +751,7 @@ Disabled - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -754,8 +767,9 @@ true + PocoFoundationmtd.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) bin64\static_mt\TestAppd.exe - ..\..\..\lib64;%(AdditionalLibraryDirectories) + ..\..\lib64;%(AdditionalLibraryDirectories) true bin64\static_mt\TestAppd.pdb Console @@ -764,7 +778,7 @@ Disabled - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -780,8 +794,9 @@ true + PocoFoundationmtd.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) binA64\static_mt\TestAppd.exe - ..\..\..\libA64;%(AdditionalLibraryDirectories) + ..\..\libA64;%(AdditionalLibraryDirectories) true binA64\static_mt\TestAppd.pdb Console @@ -794,7 +809,7 @@ true Speed true - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) true MultiThreaded @@ -811,8 +826,9 @@ true + PocoFoundationmt.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) bin\static_mt\TestApp.exe - ..\..\..\lib;%(AdditionalLibraryDirectories) + ..\..\lib;%(AdditionalLibraryDirectories) false @@ -829,7 +845,7 @@ true Speed true - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) true MultiThreaded @@ -846,8 +862,9 @@ true + PocoFoundationmt.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) bin64\static_mt\TestApp.exe - ..\..\..\lib64;%(AdditionalLibraryDirectories) + ..\..\lib64;%(AdditionalLibraryDirectories) false @@ -863,7 +880,7 @@ true Speed true - %(AdditionalIncludeDirectories) + ..\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;WINVER=0x0500;POCO_STATIC;%(PreprocessorDefinitions) true MultiThreaded @@ -880,8 +897,9 @@ true + PocoFoundationmt.lib;winmm.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies) binA64\static_mt\TestApp.exe - ..\..\..\libA64;%(AdditionalLibraryDirectories) + ..\..\libA64;%(AdditionalLibraryDirectories) false diff --git a/Foundation/testsuite/TestSuite_vs170.vcxproj b/Foundation/testsuite/TestSuite_vs170.vcxproj index b476e428f4..dc5bfaca2e 100644 --- a/Foundation/testsuite/TestSuite_vs170.vcxproj +++ b/Foundation/testsuite/TestSuite_vs170.vcxproj @@ -985,6 +985,7 @@ + @@ -1127,6 +1128,7 @@ + diff --git a/Foundation/testsuite/TestSuite_vs170.vcxproj.filters b/Foundation/testsuite/TestSuite_vs170.vcxproj.filters index e2946e043d..cbcc92d96d 100644 --- a/Foundation/testsuite/TestSuite_vs170.vcxproj.filters +++ b/Foundation/testsuite/TestSuite_vs170.vcxproj.filters @@ -519,6 +519,9 @@ Processes\Source Files + + Processes\Source Files + Processes\Source Files @@ -938,6 +941,9 @@ Processes\Header Files + + Processes\Header Files + Processes\Header Files diff --git a/Foundation/testsuite/src/ClassLoaderTest.cpp b/Foundation/testsuite/src/ClassLoaderTest.cpp index 93c149a05d..45bafbecdd 100644 --- a/Foundation/testsuite/src/ClassLoaderTest.cpp +++ b/Foundation/testsuite/src/ClassLoaderTest.cpp @@ -15,6 +15,8 @@ #include "Poco/Manifest.h" #include "Poco/Exception.h" #include "Poco/Path.h" +#include "Poco/File.h" +#include "Poco/Format.h" #include "TestPlugin.h" @@ -24,6 +26,8 @@ using Poco::SharedLibrary; using Poco::AbstractMetaObject; using Poco::NotFoundException; using Poco::InvalidAccessException; +using Poco::Path; +using Poco::File; ClassLoaderTest::ClassLoaderTest(const std::string& name): CppUnit::TestCase(name) @@ -38,17 +42,14 @@ ClassLoaderTest::~ClassLoaderTest() void ClassLoaderTest::testClassLoader1() { - std::string path = "TestLibrary"; - path.append(SharedLibrary::suffix()); - Poco::Path libraryPath = Poco::Path::current(); - libraryPath.append(path); + std::string libraryPath = getFullName("TestLibrary"); ClassLoader cl; assertTrue (cl.begin() == cl.end()); assertNullPtr (cl.findClass("PluginA")); - assertNullPtr (cl.findManifest(libraryPath.toString())); + assertNullPtr (cl.findManifest(libraryPath)); - assertTrue (!cl.isLibraryLoaded(libraryPath.toString())); + assertTrue (!cl.isLibraryLoaded(libraryPath)); try { @@ -65,7 +66,7 @@ void ClassLoaderTest::testClassLoader1() try { - const ClassLoader::Manif& POCO_UNUSED manif = cl.manifestFor(libraryPath.toString()); + const ClassLoader::Manif& POCO_UNUSED manif = cl.manifestFor(libraryPath); fail("not found - must throw exception"); } catch (NotFoundException&) @@ -80,25 +81,22 @@ void ClassLoaderTest::testClassLoader1() void ClassLoaderTest::testClassLoader2() { - std::string path = "TestLibrary"; - path.append(SharedLibrary::suffix()); - Poco::Path libraryPath = Poco::Path::current(); - libraryPath.append(path); + std::string libraryPath = getFullName("TestLibrary"); ClassLoader cl; - cl.loadLibrary(libraryPath.toString()); + cl.loadLibrary(libraryPath); assertTrue (cl.begin() != cl.end()); assertNotNullPtr (cl.findClass("PluginA")); assertNotNullPtr (cl.findClass("PluginB")); assertNotNullPtr (cl.findClass("PluginC")); - assertNotNullPtr (cl.findManifest(libraryPath.toString())); + assertNotNullPtr (cl.findManifest(libraryPath)); - assertTrue (cl.isLibraryLoaded(libraryPath.toString())); - assertTrue (cl.manifestFor(libraryPath.toString()).size() == 3); + assertTrue (cl.isLibraryLoaded(libraryPath)); + assertTrue (cl.manifestFor(libraryPath).size() == 3); ClassLoader::Iterator it = cl.begin(); assertTrue (it != cl.end()); - assertTrue (it->first == libraryPath.toString()); + assertTrue (it->first == libraryPath); assertTrue (it->second->size() == 3); ++it; assertTrue (it == cl.end()); @@ -165,32 +163,55 @@ void ClassLoaderTest::testClassLoader2() meta2.destroy(pPlugin); assertTrue (!meta2.isAutoDelete(pPlugin)); - cl.unloadLibrary(libraryPath.toString()); + cl.unloadLibrary(libraryPath); } void ClassLoaderTest::testClassLoader3() { - std::string path = "TestLibrary"; - path.append(SharedLibrary::suffix()); - Poco::Path libraryPath = Poco::Path::current(); - libraryPath.append(path); + std::string libraryPath = getFullName("TestLibrary"); ClassLoader cl; - cl.loadLibrary(libraryPath.toString()); - cl.loadLibrary(libraryPath.toString()); - cl.unloadLibrary(libraryPath.toString()); + cl.loadLibrary(libraryPath); + cl.loadLibrary(libraryPath); + cl.unloadLibrary(libraryPath); - assertTrue (cl.manifestFor(libraryPath.toString()).size() == 3); + assertTrue (cl.manifestFor(libraryPath).size() == 3); ClassLoader::Iterator it = cl.begin(); assertTrue (it != cl.end()); - assertTrue (it->first == libraryPath.toString()); + assertTrue (it->first == libraryPath); assertTrue (it->second->size() == 3); ++it; assertTrue (it == cl.end()); - cl.unloadLibrary(libraryPath.toString()); - assertNullPtr (cl.findManifest(libraryPath.toString())); + cl.unloadLibrary(libraryPath); + assertNullPtr (cl.findManifest(libraryPath)); +} + + +std::string ClassLoaderTest::getFullName(const std::string& libName) +{ + std::string name = Path::expand("$POCO_BASE"); + char c = Path::separator(); + std::string OSNAME = Path::expand("$OSNAME"); + std::string OSARCH = Path::expand("$OSARCH"); + name.append(1, c) + .append(Poco::format("Foundation%ctestsuite%cbin%c", c, c, c)) + .append(Poco::format("%s%c%s%c", OSNAME, c, OSARCH, c)) + .append(libName).append(SharedLibrary::suffix()); + + // CMake + if (!File(name).exists()) + { + name = Path::expand("$POCO_BASE"); + name.append(Poco::format("%ccmake-build%cbin%c", c, c, c)) + .append(libName).append(SharedLibrary::suffix()); + } + + if (!File(name).exists()) + name = libName + SharedLibrary::suffix(); + + return name; } diff --git a/Foundation/testsuite/src/ClassLoaderTest.h b/Foundation/testsuite/src/ClassLoaderTest.h index aef0e7531f..679deab8c9 100644 --- a/Foundation/testsuite/src/ClassLoaderTest.h +++ b/Foundation/testsuite/src/ClassLoaderTest.h @@ -34,6 +34,7 @@ class ClassLoaderTest: public CppUnit::TestCase static CppUnit::Test* suite(); private: + static std::string getFullName(const std::string& libName); }; diff --git a/Foundation/testsuite/src/ProcessRunnerTest.cpp b/Foundation/testsuite/src/ProcessRunnerTest.cpp new file mode 100644 index 0000000000..fad57fb7bc --- /dev/null +++ b/Foundation/testsuite/src/ProcessRunnerTest.cpp @@ -0,0 +1,348 @@ +// +// ProcessRunnerTest.cpp +// +// Copyright (c) 2023, Applied Informatics Software Engineering GmbH. +// Aleph ONE Software Engineering d.o.o., +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "ProcessRunnerTest.h" +#include "CppUnit/TestCaller.h" +#include "CppUnit/TestSuite.h" +#include "Poco/PIDFile.h" +#include "Poco/Format.h" +#include "Poco/Path.h" +#include "Poco/File.h" +#include "Poco/FileStream.h" + + +using namespace Poco; + + +ProcessRunnerTest::ProcessRunnerTest(const std::string& name): + CppUnit::TestCase(name) +{ +} + + +ProcessRunnerTest::~ProcessRunnerTest() +{ +} + + +void ProcessRunnerTest::testPIDFile() +{ + std::string pidFile = Path::tempHome() + "test.pid"; + + { + PIDFile f; + assertTrue (f.getName().empty()); + assertTrue (f.getPID() == PIDFile::INVALID_PID); + assertFalse (File(pidFile).exists()); + + f.setName(pidFile); + assertTrue (f.getName() == pidFile); + assertTrue (f.getPID() != PIDFile::INVALID_PID); + assertTrue (File(pidFile).exists()); + } + assertFalse (File(pidFile).exists()); + + { + PIDFile f(pidFile); + std::string pf = pidFile; + + assertTrue (f.getName() == pf); + assertTrue (File(pf).exists()); + assertTrue (f.getPID() != PIDFile::INVALID_PID); + + assertTrue (f.exists()); + } + assertFalse (File(pidFile).exists()); + + { + PIDFile f(pidFile); + assertTrue (f.getName() == pidFile); + assertTrue (File(pidFile).exists()); + assertTrue (f.getPID() != PIDFile::INVALID_PID); + + assertTrue (f.exists()); + } + assertFalse (File(pidFile).exists()); + + { + PIDFile f(pidFile, false); + std::string pf = pidFile; + + assertTrue (f.getName() == pf); + assertTrue (!File(pf).exists()); + assertTrue (f.getPID() == PIDFile::INVALID_PID); + + f.create(); + assertTrue (f.exists()); + } + assertFalse (File(pidFile).exists()); +} + + +void ProcessRunnerTest::testProcessRunner() +{ + std::string name("TestApp"); + std::string cmd; +#if defined(_DEBUG) && (POCO_OS != POCO_OS_ANDROID) + name += "d"; +#endif + +#if defined(POCO_OS_FAMILY_UNIX) + cmd += name; +#elif defined(_WIN32_WCE) + cmd = "\\"; + cmd += name; + cmd += ".EXE"; +#else + cmd = name; +#endif + + // non-auto start, no PID + { + std::vector args; + char c = Path::separator(); + std::string pidFile = Poco::format("run%c%s.pid", c, name); + args.push_back(std::string("--pidfile=").append(pidFile)); + ProcessRunner pr(cmd, args, "", ProcessRunner::NO_OUT, 10, false); + assertTrue (pr.cmdLine() == cmdLine(cmd, args)); + assertFalse (pr.running()); + pr.start(); + + Stopwatch sw; sw.start(); + while (!pr.running()) + checkTimeout(sw, "Waiting for process to start", 1000, __LINE__); + + assertTrue (pr.running()); + try + { + pr.start(); + fail("It should not be possible to start a started process."); + } + catch(const Poco::InvalidAccessException&) {} + pr.stop(); + sw.restart(); + while (pr.running()) + checkTimeout(sw, "Waiting for process to stop", 1000, __LINE__); + assertFalse (pr.running()); + pr.start(); + while (!pr.running()) + checkTimeout(sw, "Waiting for process to start", 1000, __LINE__); + assertTrue (pr.running()); + pr.stop(); + pr.stop(); // second stop() should be silent no-op + } + + // non-auto start with PID + { + std::vector args; + char c = Path::separator(); + std::string pidFile = Poco::format("run%c%s.pid", c, name); + args.push_back(std::string("--pidfile=").append(pidFile)); + ProcessRunner pr(cmd, args, "", ProcessRunner::NO_OUT, 10, false); + assertTrue (pr.cmdLine() == cmdLine(cmd, args)); + assertFalse (pr.running()); + pr.start(); + Stopwatch sw; sw.start(); + while (!pr.running()) + checkTimeout(sw, "Waiting for process to start", 1000, __LINE__); + assertTrue (pr.running()); + try + { + pr.start(); + fail("It should not be possible to start a started process."); + } + catch(const Poco::InvalidAccessException&) {} + pr.stop(); + sw.restart(); + while (pr.running()) + checkTimeout(sw, "Waiting for process to stop", 1000, __LINE__); + assertFalse (pr.running()); + pr.start(); + while (!pr.running()) + checkTimeout(sw, "Waiting for process to start", 1000, __LINE__); + assertTrue (pr.running()); + pr.stop(); + pr.stop(); // second stop() should be silent no-op + } + + // autodetect PID file from the long command line argument + { + std::vector args; + char c = Path::separator(); + std::string pidFile = Poco::format("run%c%s.pid", c, name); + args.push_back(std::string("--pidfile=").append(pidFile)); + { + ProcessRunner pr(cmd, args); + assertTrue (pr.cmdLine() == cmdLine(cmd, args)); + assertTrue (pr.pidFile() == PIDFile::getFileName(pidFile)); + assertTrue (File(pidFile).exists()); + assertTrue (PIDFile::contains(pidFile, pr.pid())); + } + assertTrue (!File(pidFile).exists()); + } + + // autodetect PID file from the short command line argument + { + std::vector args; + char c = Path::separator(); + std::string pidFile = Poco::format("run%c%s.pid", c, name); + args.push_back(std::string("-p=").append(pidFile)); + { + ProcessRunner pr(cmd, args, PIDFile::getFileName(pidFile)); + assertTrue (pr.cmdLine() == cmdLine(cmd, args)); + assertTrue (pr.pidFile() == pidFile); + assertTrue (File(pidFile).exists()); + assertTrue (PIDFile::contains(pidFile, pr.pid())); + } + assertTrue (!File(pidFile).exists()); + } + + // ProcessRunner should NOT autodetect PID from command line args + // if argument formats list is empty + { + std::vector args; + char c = Path::separator(); + std::string pidFile = Poco::format("run%c%s.pid", c, name); + args.push_back(std::string("--pidfile=").append(pidFile)); + { + ProcessRunner pr(cmd, args, "", ProcessRunner::NO_OUT, 10, true, {}); + assertTrue (pr.cmdLine() == cmdLine(cmd, args)); + assertTrue (pr.pidFile().empty()); // ProcessRunner has no PID file + + PIDFile::getFileName(pidFile); + Stopwatch sw; sw.start(); + while (!File(pidFile).exists()) + checkTimeout(sw, "Waiting for PID file", 1000, __LINE__); + + // PID file exists and is valid + assertTrue (File(pidFile).exists()); + assertTrue (PIDFile::contains(pidFile, pr.pid())); + } + assertTrue (!File(pidFile).exists()); + } + + { + std::vector args; + char c = Path::separator(); + std::string pidFile = Poco::format("run%c%s.pid", c, name); + args.push_back(std::string("-p=").append(pidFile)); + { + ProcessRunner pr(cmd, args, "", ProcessRunner::NO_OUT, 10, true, {}); + assertTrue (pr.cmdLine() == cmdLine(cmd, args)); + assertTrue (pr.pidFile().empty()); // ProcessRunner has no PID file + + PIDFile::getFileName(pidFile); + Stopwatch sw; sw.start(); + while (!File(pidFile).exists()) + checkTimeout(sw, "Waiting for PID file", 1000, __LINE__); + + // PID file exists and is valid + assertTrue (File(pidFile).exists()); + assertTrue (PIDFile::contains(pidFile, pr.pid())); + } + assertTrue (!File(pidFile).exists()); + } + + // no PID file created at all + { + std::vector args; + char c = Path::separator(); + std::string pidFile = Poco::format("run%c%s.pid", c, name); + { + ProcessRunner pr(cmd, args); + assertTrue (pr.cmdLine() == cmdLine(cmd, args)); + assertTrue (pr.pidFile().empty()); + + Thread::sleep(500); + + assertTrue (!File(pidFile).exists()); + assertTrue (!PIDFile::contains(pidFile, pr.pid())); + } + assertTrue (!File(pidFile).exists()); + } +#if defined(POCO_OS_FAMILY_UNIX) + // start process launching multiple threads + { + std::vector args; + char c = Path::separator(); + std::string pidFile = Poco::format("run%c%s.pid", c, name); + args.push_back(std::string("--pidfile=").append(pidFile)); + args.push_back(std::string("--launch-thread")); + ProcessRunner pr(cmd, args, "", ProcessRunner::NO_OUT, 10, false); + assertTrue (pr.cmdLine() == cmdLine(cmd, args)); + assertFalse (pr.running()); + pr.start(); + Stopwatch sw; sw.start(); + while (!pr.running()) + checkTimeout(sw, "Waiting for process to start", 1000, __LINE__); + assertTrue (pr.running()); + try + { + pr.start(); + fail("It should not be possible to start a started process."); + } + catch(const Poco::InvalidAccessException&) {} + pr.stop(); + sw.restart(); + while (pr.running()) + checkTimeout(sw, "Waiting for process to stop", 1000, __LINE__); + assertFalse (pr.running()); + assertEqual (pr.result(), 0); + } +#endif +} + + +std::string ProcessRunnerTest::cmdLine(const std::string& cmd, const ProcessRunner::Args& args) +{ + std::string cmdL = cmd + ' '; + auto it = args.begin(); + auto end = args.end(); + for (; it != end;) + { + cmdL.append(*it); + if (++it == end) break; + cmdL.append(1, ' '); + } + return cmdL; +} + + +void ProcessRunnerTest::checkTimeout(const Stopwatch& sw, const std::string& msg, int timeoutMS, int line) +{ + if (sw.elapsedSeconds()*1000 > timeoutMS) + { + throw Poco::TimeoutException( + Poco::format("ProcessRunner::checkTimeout(): %s, line: %d", msg, line)); + } + Thread::sleep(10); +} + + +void ProcessRunnerTest::setUp() +{ +} + + +void ProcessRunnerTest::tearDown() +{ +} + + +CppUnit::Test* ProcessRunnerTest::suite() +{ + CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ProcessRunnerTest"); + + CppUnit_addTest(pSuite, ProcessRunnerTest, testPIDFile); + CppUnit_addTest(pSuite, ProcessRunnerTest, testProcessRunner); + + return pSuite; +} \ No newline at end of file diff --git a/Foundation/testsuite/src/ProcessRunnerTest.h b/Foundation/testsuite/src/ProcessRunnerTest.h new file mode 100644 index 0000000000..8590b933f9 --- /dev/null +++ b/Foundation/testsuite/src/ProcessRunnerTest.h @@ -0,0 +1,44 @@ +// +// ProcessRunnerTest.h +// +// Definition of the ProcessRunnerTest class. +// +// Copyright (c) 2023, Applied Informatics Software Engineering GmbH. +// Aleph ONE Software Engineering d.o.o., +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + + +#ifndef ProcessRunnerTest_INCLUDED +#define ProcessRunnerTest_INCLUDED + + +#include "CppUnit/TestCase.h" +#include "Poco/ProcessRunner.h" +#include "Poco/Stopwatch.h" + + +class ProcessRunnerTest: public CppUnit::TestCase +{ +public: + ProcessRunnerTest(const std::string& name); + ~ProcessRunnerTest(); + + void testPIDFile(); + void testProcessRunner(); + + void setUp(); + void tearDown(); + + static CppUnit::Test* suite(); + +private: + std::string cmdLine(const std::string& cmd, const Poco::ProcessRunner::Args& args); + void checkTimeout(const Poco::Stopwatch& sw, const std::string& msg, int timeoutMS, int line); +}; + + +#endif // ProcessRunnerTest_INCLUDED diff --git a/Foundation/testsuite/src/ProcessTest.cpp b/Foundation/testsuite/src/ProcessTest.cpp index e4712be7dc..f66fac4e54 100644 --- a/Foundation/testsuite/src/ProcessTest.cpp +++ b/Foundation/testsuite/src/ProcessTest.cpp @@ -14,12 +14,15 @@ #include "Poco/Process.h" #include "Poco/Pipe.h" #include "Poco/PipeStream.h" +#include "Poco/Path.h" +#include "Poco/Format.h" using namespace std::string_literals; using Poco::Process; using Poco::ProcessHandle; using Poco::Pipe; +using Poco::Path; using Poco::PipeInputStream; using Poco::PipeOutputStream; diff --git a/Foundation/testsuite/src/ProcessTest.h b/Foundation/testsuite/src/ProcessTest.h index b33a9cbf7e..cefbae39ca 100644 --- a/Foundation/testsuite/src/ProcessTest.h +++ b/Foundation/testsuite/src/ProcessTest.h @@ -39,6 +39,7 @@ class ProcessTest: public CppUnit::TestCase static CppUnit::Test* suite(); private: + static std::string getFullName(const std::string& name); }; diff --git a/Foundation/testsuite/src/ProcessesTestSuite.cpp b/Foundation/testsuite/src/ProcessesTestSuite.cpp index f72504191c..ab0f12b594 100644 --- a/Foundation/testsuite/src/ProcessesTestSuite.cpp +++ b/Foundation/testsuite/src/ProcessesTestSuite.cpp @@ -13,6 +13,7 @@ #include "NamedMutexTest.h" #include "NamedEventTest.h" #include "SharedMemoryTest.h" +#include "ProcessRunnerTest.h" CppUnit::Test* ProcessesTestSuite::suite() @@ -23,6 +24,7 @@ CppUnit::Test* ProcessesTestSuite::suite() pSuite->addTest(NamedMutexTest::suite()); pSuite->addTest(NamedEventTest::suite()); pSuite->addTest(SharedMemoryTest::suite()); + pSuite->addTest(ProcessRunnerTest::suite()); return pSuite; } diff --git a/Foundation/testsuite/src/SharedLibraryTest.cpp b/Foundation/testsuite/src/SharedLibraryTest.cpp index 27ee375b99..ee26fbd00a 100644 --- a/Foundation/testsuite/src/SharedLibraryTest.cpp +++ b/Foundation/testsuite/src/SharedLibraryTest.cpp @@ -14,12 +14,16 @@ #include "Poco/SharedLibrary.h" #include "Poco/Exception.h" #include "Poco/Path.h" +#include "Poco/File.h" +#include "Poco/Format.h" using Poco::SharedLibrary; using Poco::NotFoundException; using Poco::LibraryLoadException; using Poco::LibraryAlreadyLoadedException; +using Poco::Path; +using Poco::File; typedef int (*GimmeFiveFunc)(); @@ -37,14 +41,11 @@ SharedLibraryTest::~SharedLibraryTest() void SharedLibraryTest::testSharedLibrary1() { - std::string path = "TestLibrary"; - path.append(SharedLibrary::suffix()); - Poco::Path libraryPath = Poco::Path::current(); - libraryPath.append(path); + std::string libraryPath = getFullName("TestLibrary"); SharedLibrary sl; assertTrue (!sl.isLoaded()); - sl.load(libraryPath.toString()); - assertTrue (sl.getPath() == libraryPath.toString()); + sl.load(libraryPath); + assertTrue (sl.getPath() == libraryPath); assertTrue (sl.isLoaded()); assertTrue (sl.hasSymbol("pocoBuildManifest")); assertTrue (sl.hasSymbol("pocoInitializeLibrary")); @@ -73,12 +74,9 @@ void SharedLibraryTest::testSharedLibrary1() void SharedLibraryTest::testSharedLibrary2() { - std::string path = "TestLibrary"; - path.append(SharedLibrary::suffix()); - Poco::Path libraryPath = Poco::Path::current(); - libraryPath.append(path); - SharedLibrary sl(libraryPath.toString()); - assertTrue (sl.getPath() == libraryPath.toString()); + std::string libraryPath = getFullName("TestLibrary"); + SharedLibrary sl(libraryPath); + assertTrue (sl.getPath() == libraryPath); assertTrue (sl.isLoaded()); GimmeFiveFunc gimmeFive = (GimmeFiveFunc) sl.getSymbol("gimmeFive"); @@ -91,12 +89,12 @@ void SharedLibraryTest::testSharedLibrary2() void SharedLibraryTest::testSharedLibrary3() { - std::string path = "NonexistentLibrary"; - path.append(SharedLibrary::suffix()); + std::string libraryPath = "NonexistentLibrary"; + libraryPath.append(libraryPath); SharedLibrary sl; try { - sl.load(path); + sl.load(libraryPath); failmsg("no such library - must throw exception"); } catch (LibraryLoadException&) @@ -108,16 +106,13 @@ void SharedLibraryTest::testSharedLibrary3() } assertTrue (!sl.isLoaded()); - path = "TestLibrary"; - path.append(SharedLibrary::suffix()); - Poco::Path libraryPath = Poco::Path::current(); - libraryPath.append(path); - sl.load(libraryPath.toString()); + libraryPath = getFullName("TestLibrary"); + sl.load(libraryPath); assertTrue (sl.isLoaded()); try { - sl.load(libraryPath.toString()); + sl.load(libraryPath); failmsg("library already loaded - must throw exception"); } catch (LibraryAlreadyLoadedException&) @@ -134,6 +129,33 @@ void SharedLibraryTest::testSharedLibrary3() } +std::string SharedLibraryTest::getFullName(const std::string& libName) +{ + // make + std::string name = Path::expand("$POCO_BASE"); + char c = Path::separator(); + std::string OSNAME = Path::expand("$OSNAME"); + std::string OSARCH = Path::expand("$OSARCH"); + name.append(1, c) + .append(Poco::format("Foundation%ctestsuite%cbin%c", c, c, c)) + .append(Poco::format("%s%c%s%c", OSNAME, c, OSARCH, c)) + .append(libName).append(SharedLibrary::suffix()); + + // CMake + if (!File(name).exists()) + { + name = Path::expand("$POCO_BASE"); + name.append(Poco::format("%ccmake-build%cbin%c", c, c, c)) + .append(libName).append(SharedLibrary::suffix()); + } + + if (!File(name).exists()) + name = libName + SharedLibrary::suffix(); + + return name; +} + + void SharedLibraryTest::setUp() { } diff --git a/Foundation/testsuite/src/SharedLibraryTest.h b/Foundation/testsuite/src/SharedLibraryTest.h index fe641181f4..845203994e 100644 --- a/Foundation/testsuite/src/SharedLibraryTest.h +++ b/Foundation/testsuite/src/SharedLibraryTest.h @@ -34,6 +34,7 @@ class SharedLibraryTest: public CppUnit::TestCase static CppUnit::Test* suite(); private: + static std::string getFullName(const std::string& libName); }; diff --git a/Foundation/testsuite/src/TestApp.cpp b/Foundation/testsuite/src/TestApp.cpp index 259c169dc8..ff62cd6f21 100644 --- a/Foundation/testsuite/src/TestApp.cpp +++ b/Foundation/testsuite/src/TestApp.cpp @@ -12,12 +12,151 @@ #define _CRT_SECURE_NO_DEPRECATE #endif +#include "Poco/PIDFile.h" #include #include #include #include +#if defined(POCO_OS_FAMILY_UNIX) +#include "Poco/Thread.h" +#include "Poco/Runnable.h" +#elif defined(POCO_OS_FAMILY_WINDOWS) +#include "Poco/Process.h" +#include "Poco/Event.h" +#include "Poco/NamedEvent.h" +#endif + +using namespace Poco; + +#if defined(POCO_OS_FAMILY_UNIX) +class MyRunnable: public Runnable +{ +public: + MyRunnable(): _ran(false) + { + } + + void run() + { + Thread* pThread = Thread::current(); + if (pThread) + { + _threadName = pThread->name(); + } + _ran = true; + } + + bool ran() const + { + return _ran; + } + +private: + bool _ran; + std::string _threadName; +}; +#endif + +#if POCO_OS != POCO_OS_ANDROID +class MyApp +{ +public: + MyApp() {} + ~MyApp() {} + + std::unique_ptr _pPIDFile; +#if defined(POCO_OS_FAMILY_WINDOWS) + static Poco::Event _terminated; + static Poco::NamedEvent _terminate; + + + static void terminate() + { + _terminate.set(); + } + + + static BOOL WINAPI ConsoleCtrlHandler(DWORD ctrlType) + { + switch (ctrlType) + { + case CTRL_C_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_BREAK_EVENT: + terminate(); + return _terminated.tryWait(10000) ? TRUE : FALSE; + default: + return FALSE; + } + } + + void waitForTerminationRequest() + { + SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); + _terminate.wait(); + _terminated.set(); + } +#elif defined(POCO_OS_FAMILY_UNIX) + void waitForTerminationRequest() + { + sigset_t sset; + sigemptyset(&sset); + if (!std::getenv("POCO_ENABLE_DEBUGGER")) + { + sigaddset(&sset, SIGINT); + } + sigaddset(&sset, SIGQUIT); + sigaddset(&sset, SIGTERM); + sigprocmask(SIG_BLOCK, &sset, NULL); + int sig; + sigwait(&sset, &sig); + } + + int runThreads(std::string pidPath) + { + _pPIDFile.reset(new PIDFile(pidPath, true)); + + uint32_t sigMask = 4; // Block SIGINT + + Thread thread1(sigMask); + Thread thread2(sigMask); + Thread thread3(sigMask); + Thread thread4(sigMask); + + MyRunnable r1; + MyRunnable r2; + MyRunnable r3; + MyRunnable r4; + + thread1.start(r1); + thread2.start(r2); + thread3.start(r3); + thread4.start(r4); + + waitForTerminationRequest(); + + thread1.join(); + thread2.join(); + thread3.join(); + thread4.join(); + return 0; + } +#endif + + int run(std::string pidPath) + { + _pPIDFile.reset(new PIDFile(pidPath, true)); + waitForTerminationRequest(); + return 0; + } +}; +#endif +#if defined(POCO_OS_FAMILY_WINDOWS) +Poco::Event MyApp::_terminated; +Poco::NamedEvent MyApp::_terminate(Poco::ProcessImpl::terminationEventName(Poco::Process::id())); +#endif int main(int argc, char** argv) { @@ -57,6 +196,32 @@ int main(int argc, char** argv) std::cout << argv[i] << std::endl; } } +#if defined(POCO_OS_FAMILY_UNIX) + else if (argc > 2 && arg.find("--pidfile") != std::string::npos && std::string(argv[2]) == "--launch-thread") + { + size_t equals_pos = arg.find('='); + if (equals_pos != std::string::npos) + { + std::string pidPath = arg.substr(equals_pos + 1); + MyApp myApp; + int result = myApp.runThreads(pidPath); + return result; + } + } +#endif +#if POCO_OS != POCO_OS_ANDROID + else if (arg.find("--pidfile") != std::string::npos || arg.find("-p") != std::string::npos) + { + size_t equals_pos = arg.find('='); + if (equals_pos != std::string::npos) + { + std::string pidPath = arg.substr(equals_pos + 1); + MyApp myApp; + int result = myApp.run(pidPath); + return result; + } + } +#endif } return argc - 1; } diff --git a/build/script/runtests.sh b/build/script/runtests.sh index 658d9de1f9..23f766f23d 100755 --- a/build/script/runtests.sh +++ b/build/script/runtests.sh @@ -87,7 +87,7 @@ do echo "" runs=$((runs + 1)) - if ! sh -c "cd $POCO_BUILD/$comp/testsuite/$BINDIR && PATH=.:$PATH && LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH $TESTRUNNER $IGNORE $TESTRUNNERARGS"; + if ! sh -c "export POCO_BASE='$POCO_BASE'; export OSNAME='$OSNAME'; export OSARCH='$OSARCH'; cd $POCO_BUILD/$comp/testsuite/$BINDIR && PATH=.:$PATH && LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH $TESTRUNNER $IGNORE $TESTRUNNERARGS"; then failures=$((failures + 1)) failedTests="$failedTests $comp" diff --git a/poco_env.bash b/poco_env.bash index 2f8d63a726..c3902364f8 100644 --- a/poco_env.bash +++ b/poco_env.bash @@ -93,6 +93,8 @@ esac # uncomment for sanitizer builds #LSAN_OPTIONS=verbosity=1:log_threads=1 #export LSAN_OPTIONS +#TSAN_OPTIONS="suppressions=$POCO_BASE/tsan.suppress,second_deadlock_stack=1" +#export TSAN_OPTIONS echo "\$OSNAME = $OSNAME" echo "\$OSARCH = $OSARCH" diff --git a/tsan.suppress b/tsan.suppress index c35a7079d5..8701cfc4aa 100644 --- a/tsan.suppress +++ b/tsan.suppress @@ -3,7 +3,7 @@ # https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions # # To apply: -# export TSAN_OPTIONS="suppressions=$POCO_BASE/tsan.supress,second_deadlock_stack=1" +# export TSAN_OPTIONS="suppressions=$POCO_BASE/tsan.suppress,second_deadlock_stack=1" ############## # Suppressions: