mirror of
https://github.com/pocoproject/poco.git
synced 2026-01-01 07:53:31 +01:00
* fix(SharedLibrary): Missing DLLs not reported #5069 * fix(CMake): not producing proper binary names #5070 * fix(SharedLibrary): disable shared lib tests in static build #5069 * fix(misc): add pdjson links to gitignore, remove unused var in SharedLibrary, harden TaskManagerTest * fic(ci): separate oracle and sqlserver odbc (out of disk space) (#5075) * fic(ci): separate oracle and sqlserver odbc (out of disk space) * use oracle odbc driver * use oracle free * ad db user * postpone adding user after build * remove default tablespace (does not exist) * reinstate all ci jobs * add postgresl odb tests to ci * remove spurious syminks * fix gitignore (pdjson) * Remove VS projects #5076 * chore: revert leftover ODB IP address * fix(CodeQL): float comparison alerts * fix: compile errors * chore: upgrade asan to macos-14 (tryout) * fix: .gitignore symlinks; XML Makefile wrong pattern * Optimize PatternFormatter and Timezone performance #5078 PatternFormatter: - Cache node name (Environment::nodeName()) to avoid repeated syscalls - Add extractBasename() for efficient %O format specifier - Add string reserve(128) to reduce reallocations during formatting Timezone: - Cache UTC offset to avoid repeated syscalls (8x speedup for %L patterns) - Auto-detect TZ environment variable changes to invalidate cache - Add reloadCache() method for explicit cache refresh Tests: - Add TimezoneTest::testUtcOffsetCaching() - Add PatternFormatterTest::testExtractBasename() * fix: use Path::separatorin extractBasename #5078 * Add Benchmark #5080 * enh(Logging): move constructors for Message and Logger #5078 * chore(AsyncNotificationCenter): eliminate MSVC warnings * enh(build): c++20 support #5084 * feat(CppUnit): print class name execute all named tests (not only the first one) accept test name with class (eg. testrunner LoggerTest::testLogger) #5083 * feat(Benchmark): Add Logger/FastLogger comparison benchmarks and Windows support - Add LoggerBench.cpp with AsyncChannel vs FastLogger benchmarks - Add compare.sh (Linux/macOS) and compare.ps1 (Windows) scripts - Add LOGGER_BENCHMARK.md with cross-platform benchmark results - Update README.md with Windows build instructions (Ninja, CMAKE_PREFIX_PATH) - Add error message when -- options are used on Windows (should use /) - Update CMakeLists.txt and Makefile to include LoggerBench #5080 * feat(FastLogger): #5078 FastLogger provides a Poco-compatible wrapper around the Quill logging library, offering significant performance improvements over AsyncChannel through lock-free SPSC queues and backend thread processing. Key features: - Drop-in replacement for Poco::Logger with FastLogger::get() - Support for all standard Poco channels (Console, File, Rotating, etc.) - XML/properties configuration via FastLoggerConfigurator - Thread affinity for backend worker on Linux and Windows - Log file rotation with size and time-based policies Performance (CPU time - calling thread latency): - Linux: 31-70x faster than AsyncChannel - Windows: 23-87x faster than AsyncChannel - macOS: Limited improvement due to lack of thread affinity support New files: - Foundation/include/Poco/FastLogger.h - Foundation/src/FastLogger.cpp - Util/include/Poco/Util/FastLoggerConfigurator.h - Util/src/FastLoggerConfigurator.cpp - dependencies/quill/ (header-only Quill 7.5.0 library) * fix(cmake): disable FastLogger on emscripten (not supported) #5078 * feat(FastLogger): add cpuAfinity config parameter #5087 * fix(FastLogger): Fix lock-order-inversion in FastLogger (TSAN) #5078 * fix(cmake): build not stripping release binaries #5085 * fix(PCRE): fails to compile with clang/c++20 #5131 * feat(AsyncChannel): add CPU affinity property #5087 * feat(SpinlockMutex): make it adaptive #5132 * feat(AsyncChannel): add CPU affinity property #5087 * chore: remove leftover file commited by mistake * feat(build): allow FastLogger to be fully disabled at build time #5078 Build system changes: - Add POCO_NO_FASTLOGGER compile definition in CMake when ENABLE_FASTLOGGER=OFF to prevent Config.h from auto-enabling FastLogger - Add ifdef guards around FastLogger tests in LoggingTestSuite.cpp - Exclude FastLoggerTest.cpp and FastLoggerChannelsTest.cpp from CMake build when FastLogger is disabled - Add POCO_NO_FASTLOGGER support to Make build system for Foundation and Util - Add CI jobs to verify builds work without FastLogger (CMake and Make) Code changes: - Add LoggingConfigurator::configure() convenience method for quick logging setup * fix(ci): testrunner args * chore(progen): remove leftover script #5076 * fix(test): give ANC a bit more time to process * fix(ci): set env before test run * chore(doc): quill license * feat(Channel): add log(Message&&) #5133 * fix(ci): set env before test run * fix(TestRunner): don't search children #5083 * feat: lock-free queues #5134 * feat(Benchmark): various comparisons * chore: cleanup benchmark
207 lines
7.4 KiB
C++
207 lines
7.4 KiB
C++
//
|
|
// AsyncNotificationCenter.h
|
|
//
|
|
// Library: Foundation
|
|
// Package: Notifications
|
|
// Module: AsyncNotificationCenter
|
|
//
|
|
// Definition of the AsyncNotificationCenter class.
|
|
//
|
|
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
|
|
// Aleph ONE Software Engineering LLC,
|
|
// and Contributors.
|
|
//
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
//
|
|
|
|
|
|
#ifndef Foundation_AsyncNotificationCenter_INCLUDED
|
|
#define Foundation_AsyncNotificationCenter_INCLUDED
|
|
|
|
#include "Poco/Foundation.h"
|
|
#include "Poco/NotificationCenter.h"
|
|
#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)
|
|
#pragma message ("NOTE: std::jthread is expected but is not available. Please check your compiler version and settings.")
|
|
#endif
|
|
#endif
|
|
|
|
#include <thread>
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
#include <vector>
|
|
#include <list>
|
|
#include <map>
|
|
#include <optional>
|
|
|
|
namespace Poco {
|
|
|
|
|
|
class Foundation_API AsyncNotificationCenter: public NotificationCenter
|
|
/// AsyncNotificationCenter decouples posting of notifications
|
|
/// from notifying subscribers by calling observers' notification
|
|
/// handler in a dedicated thread.
|
|
///
|
|
/// It supports multiple modes of operation:
|
|
///
|
|
/// - ENQUEUE: Notifications are added to a queue, separate single thread
|
|
/// asynchronously dispatches them to observers sequentially
|
|
///
|
|
/// - NOTIFY: Notifications are added to a list for each observer, multiple
|
|
/// worker threads process notifications in parallel
|
|
///
|
|
/// - BOTH: Combination of both modes, notifications are enqueued and worker
|
|
/// threads dispatch them to observers in parallel.
|
|
///
|
|
/// NOTIFY and BOTH mode:
|
|
///
|
|
/// These modes are only available if the compiler supports C++20 std::jthread.
|
|
///
|
|
/// Notifications can be delivered to observers in a different order than they
|
|
/// were posted, as they are processed by multiple worker threads. Observer
|
|
/// handlers must also be thread-safe as multiple notifications can be dispatched
|
|
/// to the same observer in parallel.
|
|
///
|
|
/// Note about using AsyncObserver
|
|
///
|
|
/// Although it is possible to use them with AsyncNotificationCenter,
|
|
/// it is more efficient to use NObserver.
|
|
{
|
|
public:
|
|
|
|
enum class AsyncMode { ENQUEUE, NOTIFY, BOTH };
|
|
/// ENQUEUE: Notifications are enqueued in a separate thread.
|
|
/// NOTIFY: Notifications are dispatched to observers from worker threads
|
|
/// BOTH: Notifications are enqueued and dispatched to observers in worker threads.
|
|
|
|
#if (POCO_HAVE_JTHREAD)
|
|
|
|
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();
|
|
/// Creates the AsyncNotificationCenter and starts the notifying thread.
|
|
|
|
#endif
|
|
|
|
~AsyncNotificationCenter() override;
|
|
/// Stops the notifying thread and destroys the AsyncNotificationCenter.
|
|
|
|
AsyncNotificationCenter& operator = (const AsyncNotificationCenter&) = delete;
|
|
AsyncNotificationCenter(const AsyncNotificationCenter&) = delete;
|
|
AsyncNotificationCenter& operator = (AsyncNotificationCenter&&) = delete;
|
|
AsyncNotificationCenter(AsyncNotificationCenter&&) = delete;
|
|
|
|
void postNotification(Notification::Ptr pNotification) override;
|
|
/// Enqueues notification into the notification queue.
|
|
|
|
int backlog() const override;
|
|
/// Returns the number of notifications in the notification queue.
|
|
|
|
std::vector<NotificationResult> synchronousDispatch(Notification::Ptr pNotification);
|
|
/// Dispatches the notification synchronously to all observers that have a function
|
|
/// for synchronous notification processing and accept the notification.
|
|
/// 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;
|
|
|
|
private:
|
|
void start();
|
|
void stop();
|
|
void dequeue();
|
|
|
|
using Adapter = RunnableAdapter<AsyncNotificationCenter>;
|
|
|
|
const AsyncMode _mode { AsyncMode::ENQUEUE };
|
|
|
|
// Async enqueue for notifications
|
|
Thread _enqueueThread;
|
|
NotificationQueue _nq;
|
|
Adapter _ra;
|
|
std::atomic<bool> _enqueueThreadStarted;
|
|
std::atomic<bool> _enqueueThreadDone;
|
|
|
|
#if (POCO_HAVE_JTHREAD)
|
|
// Async notification dispatching
|
|
|
|
using NotificationList = std::list<Notification::Ptr>;
|
|
using ObserversMap = std::map<AbstractObserverPtr, NotificationList>;
|
|
using NotificationTuple = std::tuple<AbstractObserverPtr, Notification::Ptr>;
|
|
|
|
std::optional<NotificationTuple> nextNotification();
|
|
|
|
void dispatchNotifications(std::stop_token& stopToken, std::size_t workerId);
|
|
/// Dispatching function executed by each worker thread.
|
|
|
|
const std::size_t _workersCount { defaultWorkersCount() };
|
|
/// Number of worker threads to process notifications.
|
|
/// This can be configured to a different value if needed.
|
|
|
|
std::vector<std::jthread> _workers;
|
|
/// Workers pop notifications from the lists and call the observers' notification handlers.
|
|
/// If an observer is not registered anymore (hasObserver), it is removed from the map.
|
|
/// Workers pick observers in a round robin fashion.
|
|
|
|
ObserversMap _lists;
|
|
/// Each observer has its own list of pending notifications.
|
|
/// Observers are identifed by their pointers.
|
|
/// When adding to the queue, observersToNotify is used to get the observers
|
|
/// that are registered for such notification.
|
|
|
|
ObserversMap::iterator _workerIterator;
|
|
/// Iterator to the current observer list being processed by the worker threads.
|
|
/// It is used to ensure that workers process observers in a round robin fashion.
|
|
|
|
std::mutex _listsMutex;
|
|
/// Mutex to protect access to the lists of notifications.
|
|
/// It is used to ensure that workers can safely access the lists.
|
|
std::atomic<bool> _listsEmpty { true };
|
|
std::condition_variable _listsEmptyCondition;
|
|
// Condition variable to notify workers when new notifications are added to lists.
|
|
|
|
#endif
|
|
};
|
|
} // namespace Poco
|
|
|
|
|
|
#endif // Foundation_AsyncNotificationCenter_INCLUDED
|