diff --git a/Data/DataTest/include/Poco/Data/Test/DataTest.h b/Data/DataTest/include/Poco/Data/Test/DataTest.h index e3c520b65..f3de9dd35 100644 --- a/Data/DataTest/include/Poco/Data/Test/DataTest.h +++ b/Data/DataTest/include/Poco/Data/Test/DataTest.h @@ -10,7 +10,8 @@ // header file. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH., -// Aleph ONE Software Engineering d.o.o., and Contributors. +// Aleph ONE Software Engineering LLC, +// and Contributors. // // SPDX-License-Identifier: BSL-1.0 // diff --git a/Data/DataTest/include/Poco/Data/Test/SQLExecutor.h b/Data/DataTest/include/Poco/Data/Test/SQLExecutor.h index 6df3e444f..3a0a09b77 100644 --- a/Data/DataTest/include/Poco/Data/Test/SQLExecutor.h +++ b/Data/DataTest/include/Poco/Data/Test/SQLExecutor.h @@ -4,7 +4,8 @@ // Definition of the SQLExecutor class. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH., -// Aleph ONE Software Engineering d.o.o., and Contributors. +// Aleph ONE Software Engineering LLC, +// and Contributors. // // SPDX-License-Identifier: BSL-1.0 // diff --git a/Foundation/include/Poco/AsyncNotificationCenter.h b/Foundation/include/Poco/AsyncNotificationCenter.h index a63f63dcc..5991b7b9f 100644 --- a/Foundation/include/Poco/AsyncNotificationCenter.h +++ b/Foundation/include/Poco/AsyncNotificationCenter.h @@ -8,7 +8,7 @@ // Definition of the AsyncNotificationCenter class. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. -// Aleph ONE Software Engineering d.o.o., +// Aleph ONE Software Engineering LLC, // and Contributors. // // SPDX-License-Identifier: BSL-1.0 @@ -23,6 +23,7 @@ #include "Poco/Thread.h" #include "Poco/RunnableAdapter.h" #include "Poco/NotificationQueue.h" +#include "Poco/AsyncObserver.h" #if (POCO_HAVE_CPP20_COMPILER) #if !(POCO_HAVE_JTHREAD) @@ -80,8 +81,11 @@ public: #if (POCO_HAVE_JTHREAD) - AsyncNotificationCenter(AsyncMode mode = AsyncMode::ENQUEUE, std::size_t workersCount = AsyncNotificationCenter::DEFAULT_WORKERS_COUNT); - /// Creates the AsyncNotificationCenter and starts the notifying thread and workers. + explicit AsyncNotificationCenter(AsyncMode mode = AsyncMode::ENQUEUE); + /// Creates the AsyncNotificationCenter with default worker count and starts the notifying thread and workers. + + AsyncNotificationCenter(AsyncMode mode, std::size_t workersCount); + /// Creates the AsyncNotificationCenter with explicit worker count and starts the notifying thread and workers. #else AsyncNotificationCenter(); @@ -109,6 +113,33 @@ public: /// This method blocks until the notification is processed by /// all observers. Returns results from all observers that accepted the notification. + template + void addAsyncObserver(C& object, void (C::*method)(const AutoPtr&), bool (C::*matcher)(const std::string&) const = nullptr) + /// Convenience method for registering an AsyncObserver. + /// Creates an AsyncObserver with optional matcher and registers it. + /// Usage: + /// asyncNotificationCenter.addAsyncObserver(*this, &MyClass::handleNotification); + /// asyncNotificationCenter.addAsyncObserver(*this, &MyClass::handleNotification, &MyClass::matchNotification); + { + addObserver(AsyncObserver(object, method, matcher)); + } + + template + void removeAsyncObserver(C& object, void (C::*method)(const AutoPtr&), bool (C::*matcher)(const std::string&) const = nullptr) + /// Convenience method for unregistering an AsyncObserver. + /// Removes the AsyncObserver with the given callback and matcher. + { + removeObserver(AsyncObserver(object, method, matcher)); + } + +#if (POCO_HAVE_JTHREAD) + + static std::size_t defaultWorkersCount(); + /// Returns the default number of worker threads based on hardware capabilities. + /// Scales from 2 (embedded) to 6 (server) based on available CPU cores. + +#endif + protected: void notifyObservers(Notification::Ptr& pNotification) override; @@ -120,11 +151,6 @@ private: using Adapter = RunnableAdapter; - class ShutdownNotification: public Notification - /// Internal notification used to signal the dequeue loop to stop. - { - }; - const AsyncMode _mode { AsyncMode::ENQUEUE }; // Async enqueue for notifications @@ -146,11 +172,7 @@ private: void dispatchNotifications(std::stop_token& stopToken, int workerId); /// Dispatching function executed by each worker thread. - constexpr static std::size_t DEFAULT_WORKERS_COUNT { 5 }; - /// Default number of worker threads to process notifications. - /// This can be configured to a different value if needed. - - const std::size_t _workersCount { DEFAULT_WORKERS_COUNT }; + const std::size_t _workersCount { defaultWorkersCount() }; /// Number of worker threads to process notifications. /// This can be configured to a different value if needed. @@ -178,8 +200,6 @@ private: #endif }; - - } // namespace Poco diff --git a/Foundation/include/Poco/AsyncObserver.h b/Foundation/include/Poco/AsyncObserver.h index 718e9be35..628aeab11 100644 --- a/Foundation/include/Poco/AsyncObserver.h +++ b/Foundation/include/Poco/AsyncObserver.h @@ -8,7 +8,7 @@ // Definition of the AsyncObserver class template. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. -// Aleph ONE Software Engineering d.o.o., +// Aleph ONE Software Engineering LLC, // and Contributors. // // SPDX-License-Identifier: BSL-1.0 diff --git a/Foundation/include/Poco/NotificationCenter.h b/Foundation/include/Poco/NotificationCenter.h index ce5ab2fc8..f6ba906dc 100644 --- a/Foundation/include/Poco/NotificationCenter.h +++ b/Foundation/include/Poco/NotificationCenter.h @@ -22,6 +22,8 @@ #include "Poco/Notification.h" #include "Poco/Mutex.h" #include "Poco/SharedPtr.h" +#include "Poco/Observer.h" +#include "Poco/NObserver.h" #include #include @@ -97,6 +99,43 @@ public: void removeObserver(const AbstractObserver& observer); /// Unregisters an observer with the NotificationCenter. + template + void addObserver(C& object, void (C::*method)(N*)) + /// Convenience method for registering an Observer. + /// Creates an Observer and registers it. + /// Usage: + /// notificationCenter.addObserver(*this, &MyClass::handleNotification); + { + addObserver(Observer(object, method)); + } + + template + void removeObserver(C& object, void (C::*method)(N*)) + /// Convenience method for unregistering an Observer. + /// Removes the Observer with the given callback. + { + removeObserver(Observer(object, method)); + } + + template + void addNObserver(C& object, void (C::*method)(const AutoPtr&), bool (C::*matcher)(const std::string&) const = nullptr) + /// Convenience method for registering an NObserver. + /// Creates an NObserver with optional matcher and registers it. + /// Usage: + /// notificationCenter.addNObserver(*this, &MyClass::handleNotification); + /// notificationCenter.addNObserver(*this, &MyClass::handleNotification, &MyClass::matchNotification); + { + addObserver(NObserver(object, method, matcher)); + } + + template + void removeNObserver(C& object, void (C::*method)(const AutoPtr&), bool (C::*matcher)(const std::string&) const = nullptr) + /// Convenience method for unregistering an NObserver. + /// Removes the NObserver with the given callback and matcher. + { + removeObserver(NObserver(object, method, matcher)); + } + bool hasObserver(const AbstractObserver& observer) const; /// Returns true if the observer is registered with this NotificationCenter. @@ -148,8 +187,6 @@ private: ObserverList _observers; mutable Mutex _mutex; }; - - } // namespace Poco diff --git a/Foundation/include/Poco/PIDFile.h b/Foundation/include/Poco/PIDFile.h index ba5087086..65d17d52a 100644 --- a/Foundation/include/Poco/PIDFile.h +++ b/Foundation/include/Poco/PIDFile.h @@ -8,7 +8,7 @@ // Definition of the PIDFile class. // // Copyright (c) 2023, Applied Informatics Software Engineering GmbH. -// Aleph ONE Software Engineering d.o.o., +// Aleph ONE Software Engineering LLC, // and Contributors. // // SPDX-License-Identifier: BSL-1.0 diff --git a/Foundation/include/Poco/ProcessRunner.h b/Foundation/include/Poco/ProcessRunner.h index 410444ed8..d4fa372cd 100644 --- a/Foundation/include/Poco/ProcessRunner.h +++ b/Foundation/include/Poco/ProcessRunner.h @@ -8,7 +8,7 @@ // Definition of the ProcessRunner class. // // Copyright (c) 2023, Applied Informatics Software Engineering GmbH. -// Aleph ONE Software Engineering d.o.o., +// Aleph ONE Software Engineering LLC, // and Contributors. // // SPDX-License-Identifier: BSL-1.0 diff --git a/Foundation/src/AsyncNotificationCenter.cpp b/Foundation/src/AsyncNotificationCenter.cpp index 4adb54261..2dfdcc655 100644 --- a/Foundation/src/AsyncNotificationCenter.cpp +++ b/Foundation/src/AsyncNotificationCenter.cpp @@ -6,7 +6,7 @@ // Module: AsyncNotificationCenter // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. -// Aleph ONE Software Engineering d.o.o., +// Aleph ONE Software Engineering LLC, // and Contributors. // // SPDX-License-Identifier: BSL-1.0 @@ -26,6 +26,17 @@ namespace Poco { #if (POCO_HAVE_JTHREAD) +AsyncNotificationCenter::AsyncNotificationCenter(AsyncMode mode) : + _mode(mode), + _ra(*this, &AsyncNotificationCenter::dequeue), + _enqueueThreadStarted(false), + _enqueueThreadDone(false), + _workersCount(defaultWorkersCount()) +{ + start(); +} + + AsyncNotificationCenter::AsyncNotificationCenter(AsyncMode mode, std::size_t workersCount) : _mode(mode), _ra(*this, &AsyncNotificationCenter::dequeue), @@ -46,7 +57,7 @@ AsyncNotificationCenter::AsyncNotificationCenter() : start(); } -#endif +#endif // POCO_HAVE_JTHREAD AsyncNotificationCenter::~AsyncNotificationCenter() { @@ -54,6 +65,20 @@ AsyncNotificationCenter::~AsyncNotificationCenter() } +#if (POCO_HAVE_JTHREAD) + +std::size_t AsyncNotificationCenter::defaultWorkersCount() +{ + unsigned int cores = std::thread::hardware_concurrency(); + if (cores <= 2) return 2; + else if (cores <= 4) return 3; + else if (cores <= 8) return 4; + else return 6; +} + +#endif // POCO_HAVE_JTHREAD + + void AsyncNotificationCenter::postNotification(Notification::Ptr pNotification) { #if (POCO_HAVE_JTHREAD) @@ -108,8 +133,6 @@ void AsyncNotificationCenter::notifyObservers(Notification::Ptr& pNotification) if (_mode == AsyncMode::NOTIFY || _mode == AsyncMode::BOTH) { - // Notification is asynchronous, add it to the lists - // for appropriate observers. std::unique_lock lock(_listsMutex); auto observers = observersToNotify(pNotification); if (observers.empty()) @@ -121,15 +144,12 @@ void AsyncNotificationCenter::notifyObservers(Notification::Ptr& pNotification) } _listsEmpty = false; _listsEmptyCondition.notify_all(); + return; } - else - { - // Notification is synchronous - NotificationCenter::notifyObservers(pNotification); - } -#else + +#endif // POCO_HAVE_JTHREAD + NotificationCenter::notifyObservers(pNotification); -#endif } @@ -180,7 +200,6 @@ void AsyncNotificationCenter::stop() { if (_enqueueThreadStarted.exchange(false)) { - _nq.enqueueUrgentNotification(new ShutdownNotification); _nq.wakeUpAll(); while (!_enqueueThreadDone) Thread::sleep(100); _enqueueThread.join(); @@ -196,9 +215,6 @@ void AsyncNotificationCenter::stop() { if (t.joinable()) t.join(); } - -// TODO: Should the observer lists be cleared here or -// shall the workers send all of them to observers and then finish? #endif } @@ -208,11 +224,8 @@ void AsyncNotificationCenter::dequeue() Notification::Ptr pNf; _enqueueThreadStarted = true; _enqueueThreadDone = false; - while (true) + while ((pNf = _nq.waitDequeueNotification())) { - pNf = _nq.waitDequeueNotification(); - if (!pNf) break; - if (pNf.cast()) break; try { notifyObservers(pNf); diff --git a/Foundation/src/PIDFile.cpp b/Foundation/src/PIDFile.cpp index ed657bf1c..af1599c1b 100644 --- a/Foundation/src/PIDFile.cpp +++ b/Foundation/src/PIDFile.cpp @@ -6,7 +6,7 @@ // Module: PIDFile // // Copyright (c) 2023, Applied Informatics Software Engineering GmbH. -// Aleph ONE Software Engineering d.o.o., +// Aleph ONE Software Engineering LLC, // and Contributors. // // SPDX-License-Identifier: BSL-1.0 diff --git a/Foundation/src/ProcessRunner.cpp b/Foundation/src/ProcessRunner.cpp index 20ef32bc9..6e7d12ccb 100644 --- a/Foundation/src/ProcessRunner.cpp +++ b/Foundation/src/ProcessRunner.cpp @@ -6,7 +6,7 @@ // Module: ProcessRunner // // Copyright (c) 2023, Applied Informatics Software Engineering GmbH. -// Aleph ONE Software Engineering d.o.o., +// Aleph ONE Software Engineering LLC, // and Contributors. // // SPDX-License-Identifier: BSL-1.0 diff --git a/Foundation/testsuite/Makefile-Driver b/Foundation/testsuite/Makefile-Driver index 846c62f84..660e5bf3f 100644 --- a/Foundation/testsuite/Makefile-Driver +++ b/Foundation/testsuite/Makefile-Driver @@ -17,7 +17,7 @@ objects = ActiveMethodTest ActivityTest ActiveDispatcherTest \ ListMapTest LoggingFactoryTest LoggingRegistryTest LoggingTestSuite LogStreamTest \ NamedEventTest NamedMutexTest ProcessesTestSuite ProcessTest \ MemoryPoolTest MD4EngineTest MD5EngineTest ManifestTest \ - NDCTest NotificationCenterTest NotificationQueueTest \ + NDCTest NotificationCenterTest AsyncNotificationCenterTest NotificationQueueTest \ PriorityNotificationQueueTest TimedNotificationQueueTest \ NotificationsTestSuite NullStreamTest NumberFormatterTest \ NumberParserTest PathTest PatternFormatterTest JSONFormatterTest PBKDF2EngineTest ProcessRunnerTest \ diff --git a/Foundation/testsuite/src/AsyncNotificationCenterTest.cpp b/Foundation/testsuite/src/AsyncNotificationCenterTest.cpp new file mode 100644 index 000000000..c5e4b55fb --- /dev/null +++ b/Foundation/testsuite/src/AsyncNotificationCenterTest.cpp @@ -0,0 +1,759 @@ +// +// AsyncNotificationCenterTest.cpp +// +// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. +// Aleph ONE Software Engineering LLC, +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "AsyncNotificationCenterTest.h" +#include "CppUnit/TestCaller.h" +#include "CppUnit/TestSuite.h" +#include "Poco/NotificationCenter.h" +#include "Poco/AsyncNotificationCenter.h" +#include "Poco/Observer.h" +#include "Poco/NObserver.h" +#include "Poco/AsyncObserver.h" +#include "Poco/AutoPtr.h" +#include + + +using Poco::NotificationCenter; +using Poco::AsyncNotificationCenter; +using Poco::Observer; +using Poco::NObserver; +using Poco::AsyncObserver; +using Poco::Notification; +using Poco::AutoPtr; +using AsyncMode = AsyncNotificationCenter::AsyncMode; + + +class TestNotification: public Notification +{ +public: + TestNotification() + {} + + TestNotification(const std::string& name): + Notification(name) + {} +}; + + +// Custom observer that supports synchronous dispatch +template +class SyncNObserver: public Poco::AbstractObserver +{ +public: + using NotificationPtr = Poco::AutoPtr; + using Callback = Poco::NotificationResult (C::*)(const NotificationPtr&); + + SyncNObserver(C& object, Callback method): + _pObject(&object), + _callback(method) + { + } + + SyncNObserver(const SyncNObserver& observer): + Poco::AbstractObserver(observer), + _pObject(observer._pObject), + _callback(observer._callback) + { + } + + ~SyncNObserver() + { + } + + SyncNObserver& operator = (const SyncNObserver& observer) + { + if (&observer != this) + { + _pObject = observer._pObject; + _callback = observer._callback; + } + return *this; + } + + void notify(Poco::Notification* pNf) const override + { + // Async notification (not used for sync dispatch) + Poco::AutoPtr nf(pNf, true); + } + + Poco::NotificationResult notifySync(Poco::Notification* pNf) const override + { + NotificationPtr ptr(static_cast(pNf), true); + return (_pObject->*_callback)(ptr); + } + + bool acceptsSync() const override + { + return true; + } + + bool equals(const Poco::AbstractObserver& abstractObserver) const override + { + const SyncNObserver* pObs = dynamic_cast(&abstractObserver); + return pObs && pObs->_pObject == _pObject && pObs->_callback == _callback; + } + + bool accepts(Poco::Notification* pNf, const char* /*pName*/) const override + { + return dynamic_cast(pNf) != nullptr; + } + + bool accepts(const Poco::Notification::Ptr& pNf) const override + { + return pNf.template cast() != nullptr; + } + + Poco::AbstractObserver* clone() const override + { + return new SyncNObserver(*this); + } + + void disable() override + { + _pObject = nullptr; + } + +private: + C* _pObject; + Callback _callback; +}; + + +AsyncNotificationCenterTest::AsyncNotificationCenterTest(const std::string& name): + CppUnit::TestCase(name), + _handle1Done(false), + _handleAuto1Done(false), + _handleAsync1Done(false), + _handleAsync2Done(false), + _notificationCount(0), + _syncCallCount(0), + _threadSafeCount(0), + _exceptionCount(0) +{ +} + + +AsyncNotificationCenterTest::~AsyncNotificationCenterTest() +{ +} + + +void AsyncNotificationCenterTest::testAsyncObserver() +{ + using AObserver = AsyncObserver::Type; + + NotificationCenter nc; + + nc.addObserver(AObserver(*this, &AsyncNotificationCenterTest::handleAsync1, &AsyncNotificationCenterTest::matchAsync)); + nc.addObserver(AObserver(*this, &AsyncNotificationCenterTest::handleAsync2, &AsyncNotificationCenterTest::matchAsync)); + + nc.postNotification(new TestNotification("asyncNotification")); + nc.postNotification(new TestNotification("anotherNotification")); + nc.postNotification(new Notification); + + while (!_handleAsync1Done || !_handleAsync2Done) + Poco::Thread::sleep(100); + + nc.removeObserver(AObserver(*this, &AsyncNotificationCenterTest::handleAsync1, &AsyncNotificationCenterTest::matchAsync)); + nc.removeObserver(AObserver(*this, &AsyncNotificationCenterTest::handleAsync2, &AsyncNotificationCenterTest::matchAsync)); + + Poco::Mutex::ScopedLock l(_mutex); + assertEqual(2u, _set.size()); + assertTrue(_set.find("handleAsync1") != _set.end()); + assertTrue(_set.find("handleAsync2") != _set.end()); +} + + +void AsyncNotificationCenterTest::testAsyncNotificationCenter() +{ + AsyncNotificationCenter nc; + + nc.addAsyncObserver(*this, &AsyncNotificationCenterTest::handleAsync1, &AsyncNotificationCenterTest::matchAsync); + nc.addAsyncObserver(*this, &AsyncNotificationCenterTest::handleAsync2, &AsyncNotificationCenterTest::matchAsync); + + nc.postNotification(new TestNotification("asyncNotification")); + nc.postNotification(new TestNotification("anotherNotification")); + nc.postNotification(new Notification); + + while (!_handleAsync1Done || !_handleAsync2Done) + Poco::Thread::sleep(100); + + nc.removeAsyncObserver(*this, &AsyncNotificationCenterTest::handleAsync1, &AsyncNotificationCenterTest::matchAsync); + nc.removeAsyncObserver(*this, &AsyncNotificationCenterTest::handleAsync2, &AsyncNotificationCenterTest::matchAsync); + + Poco::Mutex::ScopedLock l(_mutex); + assertEqual(2u, _set.size()); + assertTrue(_set.find("handleAsync1") != _set.end()); + assertTrue(_set.find("handleAsync2") != _set.end()); +} + + +void AsyncNotificationCenterTest::testAsyncNotificationCenterModes() +{ +#if (POCO_HAVE_JTHREAD) + // Test ENQUEUE mode (default) + { + AsyncNotificationCenter nc(AsyncMode::ENQUEUE); + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleAuto); + nc.postNotification(new Notification); + while (!_handleAuto1Done) + Poco::Thread::sleep(100); + assertTrue(_set.find("handleAuto") != _set.end()); + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleAuto); + } + + _set.clear(); + _handleAuto1Done = false; + + // Test NOTIFY mode + { + AsyncNotificationCenter nc(AsyncMode::NOTIFY); + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleAuto); + nc.postNotification(new Notification); + while (!_handleAuto1Done) + Poco::Thread::sleep(100); + assertTrue(_set.find("handleAuto") != _set.end()); + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleAuto); + } + + _set.clear(); + _handleAuto1Done = false; + + // Test BOTH mode + { + AsyncNotificationCenter nc(AsyncMode::BOTH); + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleAuto); + nc.postNotification(new Notification); + while (!_handleAuto1Done) + Poco::Thread::sleep(100); + assertTrue(_set.find("handleAuto") != _set.end()); + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleAuto); + } +#endif +} + + +void AsyncNotificationCenterTest::testAsyncNotificationCenterWorkerCount() +{ +#if (POCO_HAVE_JTHREAD) + workerCount(AsyncMode::ENQUEUE); + resetState(); + workerCount(AsyncMode::NOTIFY); + resetState(); + workerCount(AsyncMode::BOTH); +#endif +} + + +void AsyncNotificationCenterTest::testAsyncNotificationCenterDefaultWorkers() +{ +#if (POCO_HAVE_JTHREAD) + // Test default worker count calculation + std::size_t workers = AsyncNotificationCenter::defaultWorkersCount(); + unsigned int cores = std::thread::hardware_concurrency(); + + if (cores <= 2) + assertEqual(2u, workers); + else if (cores <= 4) + assertEqual(3u, workers); + else if (cores <= 8) + assertEqual(4u, workers); + else + assertEqual(6u, workers); +#endif +} + + +void AsyncNotificationCenterTest::testAsyncNotificationCenterBacklog() +{ +#if (POCO_HAVE_JTHREAD) + backlog(AsyncMode::ENQUEUE); + resetState(); + backlog(AsyncMode::NOTIFY); + resetState(); + backlog(AsyncMode::BOTH); +#endif +} + + +void AsyncNotificationCenterTest::testAsyncNotificationCenterParallelDispatch() +{ +#if (POCO_HAVE_JTHREAD) + parallelDispatch(AsyncMode::ENQUEUE); + resetState(); + parallelDispatch(AsyncMode::NOTIFY); + resetState(); + parallelDispatch(AsyncMode::BOTH); +#endif +} + + +void AsyncNotificationCenterTest::testMixedObservers() +{ +#if (POCO_HAVE_JTHREAD) + mixedObservers(AsyncMode::ENQUEUE); + resetState(); + mixedObservers(AsyncMode::NOTIFY); + resetState(); + mixedObservers(AsyncMode::BOTH); +#endif +} + + +void AsyncNotificationCenterTest::testSynchronousDispatch() +{ +#if (POCO_HAVE_JTHREAD) + synchronousDispatch(AsyncMode::ENQUEUE); + resetState(); + synchronousDispatch(AsyncMode::NOTIFY); + resetState(); + synchronousDispatch(AsyncMode::BOTH); +#endif +} + + +void AsyncNotificationCenterTest::testErrorHandling() +{ +#if (POCO_HAVE_JTHREAD) + // Test that exceptions from observers are caught and don't crash the system + AsyncNotificationCenter nc(AsyncMode::ENQUEUE); + + // Register an observer that throws + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleThrow); + + // Register a normal observer that should still be called + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleCount); + + // Post notification - exception should be caught by ErrorHandler + nc.postNotification(new Notification); + + // Give time for async processing + Poco::Thread::sleep(200); + + // The throwing observer should have been called + assertTrue(_exceptionCount > 0); + + // The normal observer should also have been called (error handling shouldn't stop dispatch) + // Note: This depends on ErrorHandler implementation - it may or may not continue + + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleThrow); + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleCount); +#endif +} + + +void AsyncNotificationCenterTest::testThreadSafety() +{ +#if (POCO_HAVE_JTHREAD) + // Test concurrent postNotification calls from multiple threads + AsyncNotificationCenter nc(AsyncMode::NOTIFY, 4); + + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleThreadSafe); + + const int numThreads = 8; + const int notificationsPerThread = 100; + std::vector threads; + + // Launch multiple threads posting notifications concurrently + for (int i = 0; i < numThreads; ++i) + { + threads.emplace_back([&nc, notificationsPerThread]() { + for (int j = 0; j < notificationsPerThread; ++j) + { + nc.postNotification(new Notification); + } + }); + } + + // Wait for all threads to finish posting + for (auto& t : threads) + { + t.join(); + } + + // Wait for all notifications to be processed + int timeout = 0; + int expected = numThreads * notificationsPerThread; + while (_threadSafeCount < expected && timeout < 200) + { + Poco::Thread::sleep(50); + ++timeout; + } + + // Verify all notifications were received + assertEqual(expected, _threadSafeCount.load()); + + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleThreadSafe); +#endif +} + + +void AsyncNotificationCenterTest::testObserverLifecycle() +{ +#if (POCO_HAVE_JTHREAD) + // Test removing observer while notifications are being dispatched + AsyncNotificationCenter nc(AsyncMode::NOTIFY, 2); + + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleCount); + + // Post many notifications + for (int i = 0; i < 100; ++i) + { + nc.postNotification(new Notification); + } + + // Give a tiny bit of time for processing to start + Poco::Thread::sleep(10); + + // Remove observer while notifications might still be in queue + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleCount); + + // Wait a bit for cleanup + Poco::Thread::sleep(100); + + // Test passes if: + // 1. We didn't crash + // 2. Count is in valid range (some might have been processed before removal) + int finalCount = _notificationCount; + assertTrue(finalCount >= 0 && finalCount <= 100); +#endif +} + + +void AsyncNotificationCenterTest::testCleanupDestruction() +{ +#if (POCO_HAVE_JTHREAD) + const int testCount = 50; + + // Test 1: NOTIFY mode - workers stop immediately, may not process all notifications + { + _notificationCount = 0; + AsyncNotificationCenter nc(AsyncMode::NOTIFY, 2); + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleCount); + + for (int i = 0; i < testCount; ++i) + { + nc.postNotification(new Notification); + } + + Poco::Thread::sleep(5); + // Destructor should complete quickly, possibly without processing all notifications + } + int notifyProcessed = _notificationCount.load(); + + // Test 2: ENQUEUE mode - must process all queued notifications before destructor completes + int startCount = _notificationCount.load(); + { + AsyncNotificationCenter nc(AsyncMode::ENQUEUE); + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleCount); + + for (int i = 0; i < testCount; ++i) + { + nc.postNotification(new Notification); + } + + // Give a moment for enqueue thread to start processing + Poco::Thread::sleep(10); + + // Destructor MUST process all remaining notifications in ENQUEUE mode + } + + // Verify ENQUEUE processed all notifications + int enqueueProcessed = _notificationCount.load() - startCount; + assertEqual(testCount, enqueueProcessed); + + // NOTIFY may have processed fewer (workers stop immediately when destructor runs) + assertTrue(notifyProcessed >= 0 && notifyProcessed <= testCount); +#endif +} + + +void AsyncNotificationCenterTest::testEdgeCases() +{ +#if (POCO_HAVE_JTHREAD) + // Test 1: Post to empty observer list + { + AsyncNotificationCenter nc(AsyncMode::NOTIFY); + nc.postNotification(new Notification); + Poco::Thread::sleep(50); + // Should not crash + } + + // Test 2: Stress test with many notifications + { + AsyncNotificationCenter nc(AsyncMode::BOTH, 4); + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleCount); + + const int stressCount = 1000; + for (int i = 0; i < stressCount; ++i) + { + nc.postNotification(new Notification); + } + + // Wait for all to process (max 10 seconds) + int timeout = 0; + while (_notificationCount < stressCount && timeout < 100) + { + Poco::Thread::sleep(100); + ++timeout; + } + + // Verify we got most/all notifications + int received = _notificationCount.load(); + assertTrue(received >= stressCount * 0.9); // Allow 10% loss for timing issues + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleCount); + } + + // Test 3: Observer registering other observers during notification + // This is complex and can lead to undefined behavior, so we skip it + // as it's not a supported use case + + assertTrue(true); +#endif +} + + +#if (POCO_HAVE_JTHREAD) + +void AsyncNotificationCenterTest::workerCount(AsyncMode mode) +{ + AsyncNotificationCenter nc(mode, 8); + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleAuto); + nc.postNotification(new Notification); + while (!_handleAuto1Done) + Poco::Thread::sleep(100); + assertTrue(_set.find("handleAuto") != _set.end()); + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleAuto); +} + + +void AsyncNotificationCenterTest::backlog(AsyncMode mode) +{ + AsyncNotificationCenter nc(mode); + + // Post notifications without observers (they'll queue up) + nc.postNotification(new Notification); + nc.postNotification(new Notification); + nc.postNotification(new Notification); + + // Give time to process + Poco::Thread::sleep(100); + + // Backlog should be drained (or nearly drained) + int backlog = nc.backlog(); + assertTrue(backlog >= 0); +} + + +void AsyncNotificationCenterTest::parallelDispatch(AsyncMode mode) +{ + AsyncNotificationCenter nc(mode, 4); + + _notificationCount = 0; + + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleCount); + + // Post multiple notifications + const int numNotifications = 10; + for (int i = 0; i < numNotifications; ++i) + { + nc.postNotification(new Notification); + } + + // Wait for all to be processed + int timeout = 0; + while (_notificationCount < numNotifications && timeout < 100) + { + Poco::Thread::sleep(50); + ++timeout; + } + + assertEqual(numNotifications, _notificationCount.load()); + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleCount); +} + + +void AsyncNotificationCenterTest::mixedObservers(AsyncMode mode) +{ + AsyncNotificationCenter nc(mode); + + nc.addObserver(*this, &AsyncNotificationCenterTest::handle1); + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleAuto); + nc.addAsyncObserver(*this, &AsyncNotificationCenterTest::handleAsync1, &AsyncNotificationCenterTest::matchAsync); + + nc.postNotification(new Notification); + nc.postNotification(new TestNotification("asyncNotification")); + + while (!_handle1Done || !_handleAuto1Done || !_handleAsync1Done) + Poco::Thread::sleep(100); + + nc.removeAsyncObserver(*this, &AsyncNotificationCenterTest::handleAsync1, &AsyncNotificationCenterTest::matchAsync); + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleAuto); + nc.removeObserver(*this, &AsyncNotificationCenterTest::handle1); + + Poco::Mutex::ScopedLock l(_mutex); + assertEqual(3u, _set.size()); + assertTrue (_set.find("handle1") != _set.end()); + assertTrue (_set.find("handleAuto") != _set.end()); + assertTrue (_set.find("handleAsync1") != _set.end()); +} + + +void AsyncNotificationCenterTest::synchronousDispatch(AsyncMode mode) +{ + AsyncNotificationCenter nc(mode); + + // Register sync observers using custom SyncNObserver + using SyncObs = SyncNObserver; + nc.addObserver(SyncObs(*this, &AsyncNotificationCenterTest::handleSync)); + + // Also register a regular NObserver (should not be called by synchronousDispatch) + nc.addNObserver(*this, &AsyncNotificationCenterTest::handleCount); + + // Call synchronousDispatch - should only call sync observers + auto results = nc.synchronousDispatch(new Notification); + + // Verify that handleSync was called (returns result) + assertEqual(1u, results.size()); + assertTrue(results[0].first == "result"); + assertEqual(1, Poco::AnyCast(results[0].second)); + assertEqual(1, _syncCallCount.load()); + + // Verify that handleCount was NOT called by synchronousDispatch + assertEqual(0, _notificationCount.load()); + + // Test with no sync observers - should return empty vector + nc.removeObserver(SyncObs(*this, &AsyncNotificationCenterTest::handleSync)); + results = nc.synchronousDispatch(new Notification); + assertEqual(0u, results.size()); + + nc.removeNObserver(*this, &AsyncNotificationCenterTest::handleCount); +} + +#endif // POCO_HAVE_JTHREAD + + +void AsyncNotificationCenterTest::handle1(Poco::Notification* pNf) +{ + Poco::Mutex::ScopedLock l(_mutex); + poco_check_ptr (pNf); + AutoPtr nf = pNf; + _set.insert("handle1"); + _handle1Done = true; +} + + +void AsyncNotificationCenterTest::handleAuto(const AutoPtr& pNf) +{ + Poco::Mutex::ScopedLock l(_mutex); + _set.insert("handleAuto"); + _handleAuto1Done = true; +} + + +void AsyncNotificationCenterTest::handleAsync1(const AutoPtr& pNf) +{ + Poco::Mutex::ScopedLock l(_mutex); + _set.insert("handleAsync1"); + _handleAsync1Done = true; +} + + +void AsyncNotificationCenterTest::handleAsync2(const AutoPtr& pNf) +{ + Poco::Mutex::ScopedLock l(_mutex); + _set.insert("handleAsync2"); + _handleAsync2Done = true; +} + + +void AsyncNotificationCenterTest::handleCount(const AutoPtr& pNf) +{ + ++_notificationCount; +} + + +Poco::NotificationResult AsyncNotificationCenterTest::handleSync(const AutoPtr& pNf) +{ + int callNum = ++_syncCallCount; + Poco::Mutex::ScopedLock l(_mutex); + _set.insert("handleSync"); + return {"result", callNum}; +} + + +void AsyncNotificationCenterTest::handleThrow(const AutoPtr& pNf) +{ + ++_exceptionCount; + throw Poco::RuntimeException("Test exception from observer"); +} + + +void AsyncNotificationCenterTest::handleThreadSafe(const AutoPtr& pNf) +{ + ++_threadSafeCount; + // Simulate some processing time + Poco::Thread::sleep(1); +} + + +bool AsyncNotificationCenterTest::matchAsync(const std::string& name) const +{ + return name.find("asyncNotification") == 0; +} + + +void AsyncNotificationCenterTest::resetState() +{ + _set.clear(); + _notificationCount = 0; + _syncCallCount = 0; + _threadSafeCount = 0; + _exceptionCount = 0; + _handle1Done = false; + _handleAuto1Done = false; + _handleAsync1Done = false; + _handleAsync2Done = false; +} + + +void AsyncNotificationCenterTest::setUp() +{ + resetState(); +} + + +void AsyncNotificationCenterTest::tearDown() +{ +} + + +CppUnit::Test* AsyncNotificationCenterTest::suite() +{ + CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("AsyncNotificationCenterTest"); + + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testAsyncObserver); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testAsyncNotificationCenter); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testAsyncNotificationCenterModes); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testAsyncNotificationCenterWorkerCount); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testAsyncNotificationCenterDefaultWorkers); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testAsyncNotificationCenterBacklog); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testAsyncNotificationCenterParallelDispatch); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testMixedObservers); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testSynchronousDispatch); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testErrorHandling); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testThreadSafety); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testObserverLifecycle); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testCleanupDestruction); + CppUnit_addTest(pSuite, AsyncNotificationCenterTest, testEdgeCases); + + return pSuite; +} diff --git a/Foundation/testsuite/src/AsyncNotificationCenterTest.h b/Foundation/testsuite/src/AsyncNotificationCenterTest.h new file mode 100644 index 000000000..d8e33544c --- /dev/null +++ b/Foundation/testsuite/src/AsyncNotificationCenterTest.h @@ -0,0 +1,119 @@ +// +// AsyncNotificationCenterTest.h +// +// Definition of the AsyncNotificationCenterTest class. +// +// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. +// Aleph ONE Software Engineering LLC, +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef AsyncNotificationCenterTest_INCLUDED +#define AsyncNotificationCenterTest_INCLUDED + + +#include "Poco/Foundation.h" +#include "CppUnit/TestCase.h" +#include "Poco/Notification.h" +#include "Poco/AutoPtr.h" +#include "Poco/Mutex.h" +#include "Poco/AsyncNotificationCenter.h" +#include + + +class TestNotification; + + +class AsyncNotificationCenterTest: public CppUnit::TestCase +{ +public: + AsyncNotificationCenterTest(const std::string& name); + ~AsyncNotificationCenterTest(); + + void testAsyncObserver(); + /// Tests AsyncObserver with NotificationCenter (async dispatch in observer's own thread) + + void testAsyncNotificationCenter(); + /// Tests AsyncObserver with AsyncNotificationCenter (async dispatch in NC's worker pool) + + void testAsyncNotificationCenterModes(); + /// Tests AsyncNotificationCenter modes: ENQUEUE, NOTIFY, and BOTH + + void testAsyncNotificationCenterWorkerCount(); + /// Tests AsyncNotificationCenter with custom worker thread count + + void testAsyncNotificationCenterDefaultWorkers(); + /// Tests that default worker count scales with CPU cores (2-6 workers) + + void testAsyncNotificationCenterBacklog(); + /// Tests backlog() method returns queued notification count + + void testAsyncNotificationCenterParallelDispatch(); + /// Tests that multiple workers dispatch notifications in parallel + + void testMixedObservers(); + /// Tests mixing Observer, NObserver, and AsyncObserver in same NotificationCenter + + void testSynchronousDispatch(); + /// Tests synchronousDispatch() method that returns results from sync observers + + void testErrorHandling(); + /// Tests that exceptions thrown by observers are handled gracefully + + void testThreadSafety(); + /// Tests concurrent postNotification() calls from multiple threads + + void testObserverLifecycle(); + /// Tests removing observers while notifications are being dispatched + + void testCleanupDestruction(); + /// Tests stopping AsyncNotificationCenter while notifications are queued/processing + + void testEdgeCases(); + /// Tests edge cases: empty observer list, stress testing, dynamic observer registration + + void setUp(); + void tearDown(); + + static CppUnit::Test* suite(); + +protected: + void handle1(Poco::Notification* pNf); + void handleAsync1(const Poco::AutoPtr& pNf); + void handleAsync2(const Poco::AutoPtr& pNf); + void handleAuto(const Poco::AutoPtr& pNf); + void handleCount(const Poco::AutoPtr& pNf); + Poco::NotificationResult handleSync(const Poco::AutoPtr& pNf); + void handleThrow(const Poco::AutoPtr& pNf); + void handleThreadSafe(const Poco::AutoPtr& pNf); + bool matchAsync(const std::string& name) const; + + void resetState(); + /// Resets test state between sub-tests. Not to be confused with setUp() which is called by the framework. + +#if (POCO_HAVE_JTHREAD) + void workerCount(Poco::AsyncNotificationCenter::AsyncMode mode); + void backlog(Poco::AsyncNotificationCenter::AsyncMode mode); + void parallelDispatch(Poco::AsyncNotificationCenter::AsyncMode mode); + void mixedObservers(Poco::AsyncNotificationCenter::AsyncMode mode); + void synchronousDispatch(Poco::AsyncNotificationCenter::AsyncMode mode); +#endif + +private: + std::set _set; + std::atomic _handle1Done; + std::atomic _handleAuto1Done; + std::atomic _handleAsync1Done; + std::atomic _handleAsync2Done; + std::atomic _notificationCount; + std::atomic _syncCallCount; + std::atomic _threadSafeCount; + std::atomic _exceptionCount; + Poco::Mutex _mutex; +}; + + +#endif // AsyncNotificationCenterTest_INCLUDED diff --git a/Foundation/testsuite/src/NotificationCenterTest.cpp b/Foundation/testsuite/src/NotificationCenterTest.cpp index fa5cf5e74..999035b22 100644 --- a/Foundation/testsuite/src/NotificationCenterTest.cpp +++ b/Foundation/testsuite/src/NotificationCenterTest.cpp @@ -10,43 +10,43 @@ #include "NotificationCenterTest.h" #include "CppUnit/TestCaller.h" -#include "CppUnit/TestCase.h" #include "CppUnit/TestSuite.h" -#include "Poco/Notification.h" #include "Poco/NotificationCenter.h" -#include "Poco/AsyncNotificationCenter.h" +#include "Poco/Observer.h" #include "Poco/NObserver.h" -#include "Poco/AsyncObserver.h" #include "Poco/AutoPtr.h" using Poco::NotificationCenter; -using Poco::AsyncNotificationCenter; +using Poco::Observer; using Poco::NObserver; -using Poco::AsyncObserver; using Poco::Notification; using Poco::AutoPtr; + class TestNotification: public Notification { public: - TestNotification() = default; - - TestNotification(const std::string& name, int n = 0): - Notification(name), num(n) + TestNotification() {} - int num {0}; + TestNotification(const std::string& name): + Notification(name) + {} }; NotificationCenterTest::NotificationCenterTest(const std::string& name): - CppUnit::TestCase(name) + CppUnit::TestCase(name), + _handle1Done(false), + _handleAuto1Done(false) { } -NotificationCenterTest::~NotificationCenterTest() = default; +NotificationCenterTest::~NotificationCenterTest() +{ +} void NotificationCenterTest::testNotificationCenter1() @@ -59,7 +59,7 @@ void NotificationCenterTest::testNotificationCenter1() void NotificationCenterTest::testNotificationCenter2() { NotificationCenter nc; - NObserver o(*this, &NotificationCenterTest::handle1); + Observer o(*this, &NotificationCenterTest::handle1); nc.addObserver(o); assertTrue (nc.hasObserver(o)); assertTrue (nc.hasObservers()); @@ -67,7 +67,7 @@ void NotificationCenterTest::testNotificationCenter2() nc.postNotification(new Notification); assertTrue (_set.size() == 1); assertTrue (_set.find("handle1") != _set.end()); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handle1)); + nc.removeObserver(Observer(*this, &NotificationCenterTest::handle1)); assertTrue (!nc.hasObserver(o)); assertTrue (!nc.hasObservers()); assertTrue (nc.countObservers() == 0); @@ -77,8 +77,8 @@ void NotificationCenterTest::testNotificationCenter2() void NotificationCenterTest::testNotificationCenter3() { NotificationCenter nc; - NObserver o1(*this, &NotificationCenterTest::handle1); - NObserver o2(*this, &NotificationCenterTest::handle2); + Observer o1(*this, &NotificationCenterTest::handle1); + Observer o2(*this, &NotificationCenterTest::handle2); nc.addObserver(o1); assertTrue (nc.hasObserver(o1)); nc.addObserver(o2); @@ -89,9 +89,9 @@ void NotificationCenterTest::testNotificationCenter3() assertTrue (_set.size() == 2); assertTrue (_set.find("handle1") != _set.end()); assertTrue (_set.find("handle2") != _set.end()); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handle1)); + nc.removeObserver(Observer(*this, &NotificationCenterTest::handle1)); assertTrue (!nc.hasObserver(o1)); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handle2)); + nc.removeObserver(Observer(*this, &NotificationCenterTest::handle2)); assertTrue (!nc.hasObserver(o2)); assertTrue (!nc.hasObservers()); assertTrue (nc.countObservers() == 0); @@ -101,8 +101,8 @@ void NotificationCenterTest::testNotificationCenter3() void NotificationCenterTest::testNotificationCenter4() { NotificationCenter nc; - NObserver o1(*this, &NotificationCenterTest::handle1); - NObserver o2(*this, &NotificationCenterTest::handle2); + Observer o1(*this, &NotificationCenterTest::handle1); + Observer o2(*this, &NotificationCenterTest::handle2); nc.addObserver(o1); assertTrue (nc.hasObserver(o1)); nc.addObserver(o2); @@ -111,20 +111,20 @@ void NotificationCenterTest::testNotificationCenter4() assertTrue (_set.size() == 2); assertTrue (_set.find("handle1") != _set.end()); assertTrue (_set.find("handle2") != _set.end()); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handle1)); + nc.removeObserver(Observer(*this, &NotificationCenterTest::handle1)); assertTrue (!nc.hasObserver(o1)); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handle2)); + nc.removeObserver(Observer(*this, &NotificationCenterTest::handle2)); assertTrue (!nc.hasObserver(o2)); _set.clear(); nc.postNotification(new Notification); assertTrue (_set.empty()); - NObserver o3(*this, &NotificationCenterTest::handle3); + Observer o3(*this, &NotificationCenterTest::handle3); nc.addObserver(o3); assertTrue (nc.hasObserver(o3)); nc.postNotification(new Notification); assertTrue (_set.size() == 1); assertTrue (_set.find("handle3") != _set.end()); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handle3)); + nc.removeObserver(Observer(*this, &NotificationCenterTest::handle3)); assertTrue (!nc.hasObserver(o3)); } @@ -132,8 +132,8 @@ void NotificationCenterTest::testNotificationCenter4() void NotificationCenterTest::testNotificationCenter5() { NotificationCenter nc; - nc.addObserver(NObserver(*this, &NotificationCenterTest::handle1)); - nc.addObserver(NObserver(*this, &NotificationCenterTest::handleTest)); + nc.addObserver(Observer(*this, &NotificationCenterTest::handle1)); + nc.addObserver(Observer(*this, &NotificationCenterTest::handleTest)); nc.postNotification(new Notification); assertTrue (_set.size() == 1); assertTrue (_set.find("handle1") != _set.end()); @@ -142,8 +142,8 @@ void NotificationCenterTest::testNotificationCenter5() assertTrue (_set.size() == 2); assertTrue (_set.find("handle1") != _set.end()); assertTrue (_set.find("handleTest") != _set.end()); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handle1)); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handleTest)); + nc.removeObserver(Observer(*this, &NotificationCenterTest::handle1)); + nc.removeObserver(Observer(*this, &NotificationCenterTest::handleTest)); } @@ -158,309 +158,47 @@ void NotificationCenterTest::testNotificationCenterAuto() } -void NotificationCenterTest::testAsyncObserver() -{ - using ObserverT = AsyncObserver::Type; - - NotificationCenter nc; - - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - nc.postNotification(new TestNotification("asyncNotification")); - nc.postNotification(new TestNotification("anotherNotification")); - nc.postNotification(new Notification); - - while (!_handleAsync1Done || !_handleAsync2Done) - Poco::Thread::sleep(100); - - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - Poco::Mutex::ScopedLock l(_mutex); - assertTrue(_set.size() == 2); - assertTrue(_set.find("handleAsync1") != _set.end()); - assertTrue(_set.find("handleAsync2") != _set.end()); -} - - -void NotificationCenterTest::testAsyncNotificationCenter() -{ - using ObserverT = NObserver::Type; - - AsyncNotificationCenter nc; - - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - nc.postNotification(new TestNotification("asyncNotification")); - nc.postNotification(new TestNotification("anotherNotification")); - nc.postNotification(new Notification); - - while (!_handleAsync1Done || !_handleAsync2Done) - Poco::Thread::sleep(100); - - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - Poco::Mutex::ScopedLock l(_mutex); - assertTrue(_set.size() == 2); - assertTrue(_set.find("handleAsync1") != _set.end()); - assertTrue(_set.find("handleAsync2") != _set.end()); -} - - -void NotificationCenterTest::testAsyncNotificationCenter2() -{ - using ObserverT = NObserver::Type; - - AsyncNotificationCenter nc; - - const auto matchAsync = [](const std::string& s) -> bool - { - return s.find("asyncNotification") == 0; - }; - - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, matchAsync)); - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, matchAsync)); - - nc.postNotification(new TestNotification("asyncNotification")); - nc.postNotification(new TestNotification("anotherNotification")); - nc.postNotification(new Notification); - - while (!_handleAsync1Done || !_handleAsync2Done) - Poco::Thread::sleep(100); - - Poco::Mutex::ScopedLock l(_mutex); - assertTrue(_set.size() == 2); - assertTrue(_set.find("handleAsync1") != _set.end()); - assertTrue(_set.find("handleAsync2") != _set.end()); -} - -void NotificationCenterTest::testAsyncNotificationCenterSyncNotify() -{ - using ObserverT = NObserver::Type; - - AsyncNotificationCenter nc; - - const auto matchAsync = [](const std::string& s) -> bool - { - return s.find("asyncNotification") == 0; - }; - - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, matchAsync, &NotificationCenterTest::handleSync)); - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, matchAsync)); - - const auto res = nc.synchronousDispatch(new TestNotification("asyncNotification")); - assertFalse(res.empty()); - assertEquals(res.size(), 1); - assertEquals(res[0].first, "handleAsync1"); - - Poco::Mutex::ScopedLock l(_mutex); - assertTrue(_set.size() == 1); - assertTrue(_set.find("handleAsync1") != _set.end()); - assertTrue(_handleAsync1Counter == 1); -} - - -void NotificationCenterTest::testAsyncNotificationCenterAsyncNotify() -{ -#if (POCO_HAVE_JTHREAD) - - using ObserverT = NObserver::Type; - - AsyncNotificationCenter nc(AsyncNotificationCenter::AsyncMode::NOTIFY); - - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - nc.postNotification(new TestNotification("asyncNotification")); - nc.postNotification(new TestNotification("anotherNotification")); - nc.postNotification(new Notification); - - while (!_handleAsync1Done || !_handleAsync2Done) - Poco::Thread::sleep(100); - - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - Poco::Mutex::ScopedLock l(_mutex); - assertTrue(_set.size() == 2); - assertTrue(_set.find("handleAsync1") != _set.end()); - assertTrue(_set.find("handleAsync2") != _set.end()); -#endif -} - - -void NotificationCenterTest::testAsyncNotificationCenterAsyncBoth() -{ -#if (POCO_HAVE_JTHREAD) - - using ObserverT = NObserver::Type; - - AsyncNotificationCenter nc(AsyncNotificationCenter::AsyncMode::BOTH); - - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - nc.postNotification(new TestNotification("asyncNotification")); - nc.postNotification(new TestNotification("anotherNotification")); - nc.postNotification(new Notification); - - while (!_handleAsync1Done || !_handleAsync2Done) - Poco::Thread::sleep(100); - - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - Poco::Mutex::ScopedLock l(_mutex); - assertTrue(_set.size() == 2); - assertTrue(_set.find("handleAsync1") != _set.end()); - assertTrue(_set.find("handleAsync2") != _set.end()); -#endif -} - - -void NotificationCenterTest::testAsyncNotificationCenterAsyncNotifyStress() -{ -#if (POCO_HAVE_JTHREAD) - - using ObserverT = NObserver::Type; - - AsyncNotificationCenter nc(AsyncNotificationCenter::AsyncMode::NOTIFY); - - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - for (int i {0}; i < 1000; ++i) - { - nc.postNotification(new TestNotification("asyncNotification", i)); - nc.postNotification(new TestNotification("anotherNotification", i)); - nc.postNotification(new Notification); - } - - // Give enough time for the notifications to be delivered - Poco::Thread::sleep(2000); - - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - Poco::Mutex::ScopedLock l(_mutex); - assertTrue(_set.size() == 2); - assertTrue(_set.find("handleAsync1") != _set.end()); - assertTrue(_set.find("handleAsync2") != _set.end()); - - assertTrue(_handleAsync1Counter == 1000ul); - assertTrue(_handleAsync2Counter == 1000ul); - -#endif -} - - -void NotificationCenterTest::testAsyncNotificationCenterAsyncRemoveObserver() -{ -#if (POCO_HAVE_JTHREAD) - - using ObserverT = NObserver::Type; - - AsyncNotificationCenter nc(AsyncNotificationCenter::AsyncMode::NOTIFY); - - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.addObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - - for (int i {0}; i < 1000; ++i) - { - nc.postNotification(new TestNotification("asyncNotification", i)); - nc.postNotification(new TestNotification("anotherNotification", i)); - - if (i == 100) - { - while (!_handleAsync2Done) - Poco::Thread::sleep(50); - // Remove one observer while notifications are still being posted - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync2, &NotificationCenterTest::matchAsync)); - } - } - - // Give enough time for the notifications to be delivered - Poco::Thread::sleep(2000); - - nc.removeObserver(ObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - - Poco::Mutex::ScopedLock l(_mutex); - assertTrue(_set.size() == 2); - assertTrue(_set.find("handleAsync1") != _set.end()); - assertTrue(_set.find("handleAsync2") != _set.end()); - - assertTrue(_handleAsync1Counter == 1000ul); - assertTrue(_handleAsync2Counter < 1000ul); - -#endif -} - - void NotificationCenterTest::testDefaultNotificationCenter() { NotificationCenter& nc = NotificationCenter::defaultCenter(); - nc.addObserver(NObserver(*this, &NotificationCenterTest::handle1)); + nc.addObserver(Observer(*this, &NotificationCenterTest::handle1)); nc.postNotification(new Notification); assertTrue (_set.size() == 1); assertTrue (_set.find("handle1") != _set.end()); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handle1)); + nc.removeObserver(Observer(*this, &NotificationCenterTest::handle1)); } -void NotificationCenterTest::testMixedObservers() -{ - using AObserverT = AsyncObserver::Type; - - AsyncNotificationCenter nc; - nc.addObserver(NObserver(*this, &NotificationCenterTest::handle1)); - nc.addObserver(NObserver(*this, &NotificationCenterTest::handleAuto)); - nc.addObserver(AObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.postNotification(new Notification); - nc.postNotification(new TestNotification("asyncNotification")); - - while (!_handle1Done || !_handleAuto1Done || !_handleAsync1Done) - Poco::Thread::sleep(100); - - nc.removeObserver(AObserverT(*this, &NotificationCenterTest::handleAsync1, &NotificationCenterTest::matchAsync)); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handleAuto)); - nc.removeObserver(NObserver(*this, &NotificationCenterTest::handle1)); - Poco::Mutex::ScopedLock l(_mutex); - assertTrue (_set.size() == 3); - assertTrue (_set.find("handle1") != _set.end()); - assertTrue (_set.find("handleAuto") != _set.end()); - assertTrue (_set.find("handleAsync1") != _set.end()); -} - - -void NotificationCenterTest::handle1(const AutoPtr& pNf) +void NotificationCenterTest::handle1(Poco::Notification* pNf) { Poco::Mutex::ScopedLock l(_mutex); poco_check_ptr (pNf); + AutoPtr nf = pNf; _set.insert("handle1"); _handle1Done = true; } -void NotificationCenterTest::handle2(const AutoPtr& pNf) +void NotificationCenterTest::handle2(Poco::Notification* pNf) { poco_check_ptr (pNf); + AutoPtr nf = pNf; _set.insert("handle2"); } -void NotificationCenterTest::handle3(const AutoPtr& pNf) +void NotificationCenterTest::handle3(Poco::Notification* pNf) { poco_check_ptr (pNf); + AutoPtr nf = pNf; _set.insert("handle3"); } -void NotificationCenterTest::handleTest(const AutoPtr& pNf) +void NotificationCenterTest::handleTest(TestNotification* pNf) { poco_check_ptr (pNf); + AutoPtr nf = pNf; _set.insert("handleTest"); } @@ -473,48 +211,11 @@ void NotificationCenterTest::handleAuto(const AutoPtr& pNf) } -void NotificationCenterTest::handleAsync1(const AutoPtr& pNf) -{ - Poco::Mutex::ScopedLock l(_mutex); - _set.insert("handleAsync1"); - _handleAsync1Done = true; - _handleAsync1Counter++; -} - -Poco::NotificationResult NotificationCenterTest::handleSync(const AutoPtr& pNf) -{ - Poco::Mutex::ScopedLock l(_mutex); - - _set.insert("handleAsync1"); - return std::make_pair("handleAsync1", Poco::Any(++_handleAsync1Counter)); -} - - -void NotificationCenterTest::handleAsync2(const AutoPtr& pNf) -{ - Poco::Mutex::ScopedLock l(_mutex); - _set.insert("handleAsync2"); - _handleAsync2Done = true; - _handleAsync2Counter++; -} - - -bool NotificationCenterTest::matchAsync(const std::string& name) const -{ - return name.find("asyncNotification") == 0; -} - - void NotificationCenterTest::setUp() { _set.clear(); _handle1Done = false; _handleAuto1Done = false; - _handleAsync1Done = false; - _handleAsync2Done = false; - - _handleAsync1Counter = 0; - _handleAsync2Counter = 0; } @@ -533,16 +234,7 @@ CppUnit::Test* NotificationCenterTest::suite() CppUnit_addTest(pSuite, NotificationCenterTest, testNotificationCenter4); CppUnit_addTest(pSuite, NotificationCenterTest, testNotificationCenter5); CppUnit_addTest(pSuite, NotificationCenterTest, testNotificationCenterAuto); - CppUnit_addTest(pSuite, NotificationCenterTest, testAsyncObserver); - CppUnit_addTest(pSuite, NotificationCenterTest, testAsyncNotificationCenter); - CppUnit_addTest(pSuite, NotificationCenterTest, testAsyncNotificationCenter2); - CppUnit_addTest(pSuite, NotificationCenterTest, testAsyncNotificationCenterSyncNotify); - CppUnit_addTest(pSuite, NotificationCenterTest, testAsyncNotificationCenterAsyncNotify); - CppUnit_addTest(pSuite, NotificationCenterTest, testAsyncNotificationCenterAsyncBoth); - CppUnit_addTest(pSuite, NotificationCenterTest, testAsyncNotificationCenterAsyncNotifyStress); - CppUnit_addTest(pSuite, NotificationCenterTest, testAsyncNotificationCenterAsyncRemoveObserver); CppUnit_addTest(pSuite, NotificationCenterTest, testDefaultNotificationCenter); - CppUnit_addTest(pSuite, NotificationCenterTest, testMixedObservers); return pSuite; } diff --git a/Foundation/testsuite/src/NotificationCenterTest.h b/Foundation/testsuite/src/NotificationCenterTest.h index fd5136b4d..f1d79e71d 100644 --- a/Foundation/testsuite/src/NotificationCenterTest.h +++ b/Foundation/testsuite/src/NotificationCenterTest.h @@ -29,7 +29,7 @@ class NotificationCenterTest: public CppUnit::TestCase { public: NotificationCenterTest(const std::string& name); - ~NotificationCenterTest() override; + ~NotificationCenterTest(); void testNotificationCenter1(); void testNotificationCenter2(); @@ -37,43 +37,24 @@ public: void testNotificationCenter4(); void testNotificationCenter5(); void testNotificationCenterAuto(); - void testAsyncObserver(); - void testAsyncNotificationCenter(); - void testAsyncNotificationCenter2(); - void testAsyncNotificationCenterSyncNotify(); - void testAsyncNotificationCenterAsyncNotify(); - void testAsyncNotificationCenterAsyncBoth(); - void testAsyncNotificationCenterAsyncNotifyStress(); - void testAsyncNotificationCenterAsyncRemoveObserver(); void testDefaultNotificationCenter(); - void testMixedObservers(); - void setUp() override; - void tearDown() override; + void setUp(); + void tearDown(); static CppUnit::Test* suite(); protected: - void handle1(const Poco::AutoPtr& pNf); - void handle2(const Poco::AutoPtr& pNf); - void handle3(const Poco::AutoPtr& pNf); - void handleTest(const Poco::AutoPtr& pNf); + void handle1(Poco::Notification* pNf); + void handle2(Poco::Notification* pNf); + void handle3(Poco::Notification* pNf); + void handleTest(TestNotification* pNf); void handleAuto(const Poco::AutoPtr& pNf); - void handleAsync1(const Poco::AutoPtr& pNf); - void handleAsync2(const Poco::AutoPtr& pNf); - Poco::NotificationResult handleSync(const Poco::AutoPtr& pNf); - bool matchAsync(const std::string& name) const; private: std::set _set; - std::atomic _handle1Done {false}; - std::atomic _handleAuto1Done {false}; - std::atomic _handleAsync1Done {false}; - std::atomic _handleAsync2Done {false}; - - std::atomic _handleAsync1Counter {0}; - std::atomic _handleAsync2Counter {0}; - + std::atomic _handle1Done; + std::atomic _handleAuto1Done; Poco::Mutex _mutex; }; diff --git a/Foundation/testsuite/src/NotificationsTestSuite.cpp b/Foundation/testsuite/src/NotificationsTestSuite.cpp index bfec49561..f536c909f 100644 --- a/Foundation/testsuite/src/NotificationsTestSuite.cpp +++ b/Foundation/testsuite/src/NotificationsTestSuite.cpp @@ -10,6 +10,7 @@ #include "NotificationsTestSuite.h" #include "NotificationCenterTest.h" +#include "AsyncNotificationCenterTest.h" #include "NotificationQueueTest.h" #include "PriorityNotificationQueueTest.h" #include "TimedNotificationQueueTest.h" @@ -20,6 +21,7 @@ CppUnit::Test* NotificationsTestSuite::suite() CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("NotificationsTestSuite"); pSuite->addTest(NotificationCenterTest::suite()); + pSuite->addTest(AsyncNotificationCenterTest::suite()); pSuite->addTest(NotificationQueueTest::suite()); pSuite->addTest(PriorityNotificationQueueTest::suite()); pSuite->addTest(TimedNotificationQueueTest::suite()); diff --git a/Foundation/testsuite/src/ProcessRunnerTest.cpp b/Foundation/testsuite/src/ProcessRunnerTest.cpp index fb473f32e..e649d5017 100644 --- a/Foundation/testsuite/src/ProcessRunnerTest.cpp +++ b/Foundation/testsuite/src/ProcessRunnerTest.cpp @@ -2,7 +2,7 @@ // ProcessRunnerTest.cpp // // Copyright (c) 2023, Applied Informatics Software Engineering GmbH. -// Aleph ONE Software Engineering d.o.o., +// Aleph ONE Software Engineering LLC, // and Contributors. // // SPDX-License-Identifier: BSL-1.0 diff --git a/Foundation/testsuite/src/ProcessRunnerTest.h b/Foundation/testsuite/src/ProcessRunnerTest.h index 229dd942f..27750a716 100644 --- a/Foundation/testsuite/src/ProcessRunnerTest.h +++ b/Foundation/testsuite/src/ProcessRunnerTest.h @@ -4,7 +4,7 @@ // Definition of the ProcessRunnerTest class. // // Copyright (c) 2023, Applied Informatics Software Engineering GmbH. -// Aleph ONE Software Engineering d.o.o., +// Aleph ONE Software Engineering LLC, // and Contributors. // // SPDX-License-Identifier: BSL-1.0 @@ -26,7 +26,7 @@ class ProcessRunnerTest: public CppUnit::TestCase public: ProcessRunnerTest(const std::string& name); ~ProcessRunnerTest(); - + void testPIDFile(); void testProcessRunner(); diff --git a/poco_env.bash b/poco_env.bash old mode 100644 new mode 100755