feat(AsyncNotificationCenter): Auto-configure number of workers #5060 (#5061)

* feat(AsyncNotificationCenter): Auto-configure number of workers #5060
add/removeObserver utility functions
move AsyncNotificationCenter tests into a separate file
tests

* chore: update copyrighht
This commit is contained in:
Aleksandar Fabijanic
2025-11-13 01:36:07 -06:00
committed by GitHub
parent 1284103b98
commit c775d5f3b7
19 changed files with 1047 additions and 422 deletions

View File

@@ -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
//

View File

@@ -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
//

View File

@@ -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 <class C, class N>
void addAsyncObserver(C& object, void (C::*method)(const AutoPtr<N>&), bool (C::*matcher)(const std::string&) const = nullptr)
/// Convenience method for registering an AsyncObserver.
/// Creates an AsyncObserver<C, N> with optional matcher and registers it.
/// Usage:
/// asyncNotificationCenter.addAsyncObserver(*this, &MyClass::handleNotification);
/// asyncNotificationCenter.addAsyncObserver(*this, &MyClass::handleNotification, &MyClass::matchNotification);
{
addObserver(AsyncObserver<C, N>(object, method, matcher));
}
template <class C, class N>
void removeAsyncObserver(C& object, void (C::*method)(const AutoPtr<N>&), bool (C::*matcher)(const std::string&) const = nullptr)
/// Convenience method for unregistering an AsyncObserver.
/// Removes the AsyncObserver<C, N> with the given callback and matcher.
{
removeObserver(AsyncObserver<C, N>(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<AsyncNotificationCenter>;
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

View File

@@ -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

View File

@@ -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 <vector>
#include <cstddef>
@@ -97,6 +99,43 @@ public:
void removeObserver(const AbstractObserver& observer);
/// Unregisters an observer with the NotificationCenter.
template <class C, class N>
void addObserver(C& object, void (C::*method)(N*))
/// Convenience method for registering an Observer.
/// Creates an Observer<C, N> and registers it.
/// Usage:
/// notificationCenter.addObserver(*this, &MyClass::handleNotification);
{
addObserver(Observer<C, N>(object, method));
}
template <class C, class N>
void removeObserver(C& object, void (C::*method)(N*))
/// Convenience method for unregistering an Observer.
/// Removes the Observer<C, N> with the given callback.
{
removeObserver(Observer<C, N>(object, method));
}
template <class C, class N>
void addNObserver(C& object, void (C::*method)(const AutoPtr<N>&), bool (C::*matcher)(const std::string&) const = nullptr)
/// Convenience method for registering an NObserver.
/// Creates an NObserver<C, N> with optional matcher and registers it.
/// Usage:
/// notificationCenter.addNObserver(*this, &MyClass::handleNotification);
/// notificationCenter.addNObserver(*this, &MyClass::handleNotification, &MyClass::matchNotification);
{
addObserver(NObserver<C, N>(object, method, matcher));
}
template <class C, class N>
void removeNObserver(C& object, void (C::*method)(const AutoPtr<N>&), bool (C::*matcher)(const std::string&) const = nullptr)
/// Convenience method for unregistering an NObserver.
/// Removes the NObserver<C, N> with the given callback and matcher.
{
removeObserver(NObserver<C, N>(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

View File

@@ -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

View File

@@ -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

View File

@@ -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<std::mutex> 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<ShutdownNotification>()) break;
try
{
notifyObservers(pNf);

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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 <thread>
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 C, class N>
class SyncNObserver: public Poco::AbstractObserver
{
public:
using NotificationPtr = Poco::AutoPtr<N>;
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<Poco::Notification> nf(pNf, true);
}
Poco::NotificationResult notifySync(Poco::Notification* pNf) const override
{
NotificationPtr ptr(static_cast<N*>(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<const SyncNObserver*>(&abstractObserver);
return pObs && pObs->_pObject == _pObject && pObs->_callback == _callback;
}
bool accepts(Poco::Notification* pNf, const char* /*pName*/) const override
{
return dynamic_cast<N*>(pNf) != nullptr;
}
bool accepts(const Poco::Notification::Ptr& pNf) const override
{
return pNf.template cast<N>() != 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<AsyncNotificationCenterTest, TestNotification>::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<std::thread> 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<AsyncNotificationCenterTest, Notification>(*this, &AsyncNotificationCenterTest::handle1);
nc.addNObserver<AsyncNotificationCenterTest, Notification>(*this, &AsyncNotificationCenterTest::handleAuto);
nc.addAsyncObserver<AsyncNotificationCenterTest, TestNotification>(*this, &AsyncNotificationCenterTest::handleAsync1, &AsyncNotificationCenterTest::matchAsync);
nc.postNotification(new Notification);
nc.postNotification(new TestNotification("asyncNotification"));
while (!_handle1Done || !_handleAuto1Done || !_handleAsync1Done)
Poco::Thread::sleep(100);
nc.removeAsyncObserver<AsyncNotificationCenterTest, TestNotification>(*this, &AsyncNotificationCenterTest::handleAsync1, &AsyncNotificationCenterTest::matchAsync);
nc.removeNObserver<AsyncNotificationCenterTest, Notification>(*this, &AsyncNotificationCenterTest::handleAuto);
nc.removeObserver<AsyncNotificationCenterTest, Notification>(*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<AsyncNotificationCenterTest, Notification>;
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<int>(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<Notification> nf = pNf;
_set.insert("handle1");
_handle1Done = true;
}
void AsyncNotificationCenterTest::handleAuto(const AutoPtr<Notification>& pNf)
{
Poco::Mutex::ScopedLock l(_mutex);
_set.insert("handleAuto");
_handleAuto1Done = true;
}
void AsyncNotificationCenterTest::handleAsync1(const AutoPtr<TestNotification>& pNf)
{
Poco::Mutex::ScopedLock l(_mutex);
_set.insert("handleAsync1");
_handleAsync1Done = true;
}
void AsyncNotificationCenterTest::handleAsync2(const AutoPtr<TestNotification>& pNf)
{
Poco::Mutex::ScopedLock l(_mutex);
_set.insert("handleAsync2");
_handleAsync2Done = true;
}
void AsyncNotificationCenterTest::handleCount(const AutoPtr<Notification>& pNf)
{
++_notificationCount;
}
Poco::NotificationResult AsyncNotificationCenterTest::handleSync(const AutoPtr<Notification>& pNf)
{
int callNum = ++_syncCallCount;
Poco::Mutex::ScopedLock l(_mutex);
_set.insert("handleSync");
return {"result", callNum};
}
void AsyncNotificationCenterTest::handleThrow(const AutoPtr<Notification>& pNf)
{
++_exceptionCount;
throw Poco::RuntimeException("Test exception from observer");
}
void AsyncNotificationCenterTest::handleThreadSafe(const AutoPtr<Notification>& 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;
}

View File

@@ -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 <set>
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<TestNotification>& pNf);
void handleAsync2(const Poco::AutoPtr<TestNotification>& pNf);
void handleAuto(const Poco::AutoPtr<Poco::Notification>& pNf);
void handleCount(const Poco::AutoPtr<Poco::Notification>& pNf);
Poco::NotificationResult handleSync(const Poco::AutoPtr<Poco::Notification>& pNf);
void handleThrow(const Poco::AutoPtr<Poco::Notification>& pNf);
void handleThreadSafe(const Poco::AutoPtr<Poco::Notification>& 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<std::string> _set;
std::atomic<bool> _handle1Done;
std::atomic<bool> _handleAuto1Done;
std::atomic<bool> _handleAsync1Done;
std::atomic<bool> _handleAsync2Done;
std::atomic<int> _notificationCount;
std::atomic<int> _syncCallCount;
std::atomic<int> _threadSafeCount;
std::atomic<int> _exceptionCount;
Poco::Mutex _mutex;
};
#endif // AsyncNotificationCenterTest_INCLUDED

View File

@@ -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<NotificationCenterTest, Notification> o(*this, &NotificationCenterTest::handle1);
Observer<NotificationCenterTest, Notification> 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<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.removeObserver(Observer<NotificationCenterTest, Notification>(*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<NotificationCenterTest, Notification> o1(*this, &NotificationCenterTest::handle1);
NObserver<NotificationCenterTest, Notification> o2(*this, &NotificationCenterTest::handle2);
Observer<NotificationCenterTest, Notification> o1(*this, &NotificationCenterTest::handle1);
Observer<NotificationCenterTest, Notification> 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<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.removeObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
assertTrue (!nc.hasObserver(o1));
nc.removeObserver(NObserver<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle2));
nc.removeObserver(Observer<NotificationCenterTest, Notification>(*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<NotificationCenterTest, Notification> o1(*this, &NotificationCenterTest::handle1);
NObserver<NotificationCenterTest, Notification> o2(*this, &NotificationCenterTest::handle2);
Observer<NotificationCenterTest, Notification> o1(*this, &NotificationCenterTest::handle1);
Observer<NotificationCenterTest, Notification> 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<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.removeObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
assertTrue (!nc.hasObserver(o1));
nc.removeObserver(NObserver<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle2));
nc.removeObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle2));
assertTrue (!nc.hasObserver(o2));
_set.clear();
nc.postNotification(new Notification);
assertTrue (_set.empty());
NObserver<NotificationCenterTest, Notification> o3(*this, &NotificationCenterTest::handle3);
Observer<NotificationCenterTest, Notification> 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<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle3));
nc.removeObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle3));
assertTrue (!nc.hasObserver(o3));
}
@@ -132,8 +132,8 @@ void NotificationCenterTest::testNotificationCenter4()
void NotificationCenterTest::testNotificationCenter5()
{
NotificationCenter nc;
nc.addObserver(NObserver<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.addObserver(NObserver<NotificationCenterTest, TestNotification>(*this, &NotificationCenterTest::handleTest));
nc.addObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.addObserver(Observer<NotificationCenterTest, TestNotification>(*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<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.removeObserver(NObserver<NotificationCenterTest, TestNotification>(*this, &NotificationCenterTest::handleTest));
nc.removeObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.removeObserver(Observer<NotificationCenterTest, TestNotification>(*this, &NotificationCenterTest::handleTest));
}
@@ -158,309 +158,47 @@ void NotificationCenterTest::testNotificationCenterAuto()
}
void NotificationCenterTest::testAsyncObserver()
{
using ObserverT = AsyncObserver<NotificationCenterTest, TestNotification>::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<NotificationCenterTest, TestNotification>::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<NotificationCenterTest, TestNotification>::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<NotificationCenterTest, TestNotification>::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<NotificationCenterTest, TestNotification>::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<NotificationCenterTest, TestNotification>::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<NotificationCenterTest, TestNotification>::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<NotificationCenterTest, TestNotification>::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<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.addObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.postNotification(new Notification);
assertTrue (_set.size() == 1);
assertTrue (_set.find("handle1") != _set.end());
nc.removeObserver(NObserver<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.removeObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
}
void NotificationCenterTest::testMixedObservers()
{
using AObserverT = AsyncObserver<NotificationCenterTest, TestNotification>::Type;
AsyncNotificationCenter nc;
nc.addObserver(NObserver<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
nc.addObserver(NObserver<NotificationCenterTest, Notification>(*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<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handleAuto));
nc.removeObserver(NObserver<NotificationCenterTest, Notification>(*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<Notification>& pNf)
void NotificationCenterTest::handle1(Poco::Notification* pNf)
{
Poco::Mutex::ScopedLock l(_mutex);
poco_check_ptr (pNf);
AutoPtr<Notification> nf = pNf;
_set.insert("handle1");
_handle1Done = true;
}
void NotificationCenterTest::handle2(const AutoPtr<Notification>& pNf)
void NotificationCenterTest::handle2(Poco::Notification* pNf)
{
poco_check_ptr (pNf);
AutoPtr<Notification> nf = pNf;
_set.insert("handle2");
}
void NotificationCenterTest::handle3(const AutoPtr<Notification>& pNf)
void NotificationCenterTest::handle3(Poco::Notification* pNf)
{
poco_check_ptr (pNf);
AutoPtr<Notification> nf = pNf;
_set.insert("handle3");
}
void NotificationCenterTest::handleTest(const AutoPtr<TestNotification>& pNf)
void NotificationCenterTest::handleTest(TestNotification* pNf)
{
poco_check_ptr (pNf);
AutoPtr<TestNotification> nf = pNf;
_set.insert("handleTest");
}
@@ -473,48 +211,11 @@ void NotificationCenterTest::handleAuto(const AutoPtr<Notification>& pNf)
}
void NotificationCenterTest::handleAsync1(const AutoPtr<TestNotification>& pNf)
{
Poco::Mutex::ScopedLock l(_mutex);
_set.insert("handleAsync1");
_handleAsync1Done = true;
_handleAsync1Counter++;
}
Poco::NotificationResult NotificationCenterTest::handleSync(const AutoPtr<TestNotification>& pNf)
{
Poco::Mutex::ScopedLock l(_mutex);
_set.insert("handleAsync1");
return std::make_pair("handleAsync1", Poco::Any(++_handleAsync1Counter));
}
void NotificationCenterTest::handleAsync2(const AutoPtr<TestNotification>& 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;
}

View File

@@ -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<Poco::Notification>& pNf);
void handle2(const Poco::AutoPtr<Poco::Notification>& pNf);
void handle3(const Poco::AutoPtr<Poco::Notification>& pNf);
void handleTest(const Poco::AutoPtr<TestNotification>& 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<Poco::Notification>& pNf);
void handleAsync1(const Poco::AutoPtr<TestNotification>& pNf);
void handleAsync2(const Poco::AutoPtr<TestNotification>& pNf);
Poco::NotificationResult handleSync(const Poco::AutoPtr<TestNotification>& pNf);
bool matchAsync(const std::string& name) const;
private:
std::set<std::string> _set;
std::atomic<bool> _handle1Done {false};
std::atomic<bool> _handleAuto1Done {false};
std::atomic<bool> _handleAsync1Done {false};
std::atomic<bool> _handleAsync2Done {false};
std::atomic<std::size_t> _handleAsync1Counter {0};
std::atomic<std::size_t> _handleAsync2Counter {0};
std::atomic<bool> _handle1Done;
std::atomic<bool> _handleAuto1Done;
Poco::Mutex _mutex;
};

View File

@@ -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());

View File

@@ -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

View File

@@ -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

0
poco_env.bash Normal file → Executable file
View File