AsyncObserver (#4444)

* feat(AsyncObserver): Improve NotificationCenter speed and usability #4414

* fix(Notification): add missing header

* feat(Any): add checkers for holding nullptr #4447

* feat(NotificationCenter): g++ build and refactoring #4414

* fix(Observer): compile errors on some compilers #4414

* fix(NotificationCenter): compile errors #4414

* chore(ParallelSocketAcceptor): remove unnecessary include and using from header

* feat(AsyncNotificationCenter): add #4414

* test(AsyncNotificationCenter): add mixed observer types to the test #4414

* fix(AsyncNotificationCenter): hangs on program exit #4414

* fix(dev): friend not honored, temporarily make private members public

* fix(AsyncNotificationCenter); remove default #4414
This commit is contained in:
Aleksandar Fabijanic
2024-02-16 09:34:19 +01:00
committed by GitHub
parent 30a0a06bac
commit 88be66972a
24 changed files with 882 additions and 139 deletions

View File

@@ -577,6 +577,7 @@
<ClCompile Include="src\Ascii.cpp" /> <ClCompile Include="src\Ascii.cpp" />
<ClCompile Include="src\ASCIIEncoding.cpp" /> <ClCompile Include="src\ASCIIEncoding.cpp" />
<ClCompile Include="src\AsyncChannel.cpp" /> <ClCompile Include="src\AsyncChannel.cpp" />
<ClCompile Include="src\AsyncNotificationCenter.cpp" />
<ClCompile Include="src\AtomicCounter.cpp" /> <ClCompile Include="src\AtomicCounter.cpp" />
<ClCompile Include="src\Base32Decoder.cpp" /> <ClCompile Include="src\Base32Decoder.cpp" />
<ClCompile Include="src\Base32Encoder.cpp" /> <ClCompile Include="src\Base32Encoder.cpp" />
@@ -1493,6 +1494,7 @@
<ClInclude Include="include\Poco\AccessExpireStrategy.h" /> <ClInclude Include="include\Poco\AccessExpireStrategy.h" />
<ClInclude Include="include\Poco\ActiveDispatcher.h" /> <ClInclude Include="include\Poco\ActiveDispatcher.h" />
<ClInclude Include="include\Poco\ActiveMethod.h" /> <ClInclude Include="include\Poco\ActiveMethod.h" />
<ClInclude Include="include\Poco\AsyncObserver.h" />
<ClInclude Include="include\Poco\ActiveResult.h" /> <ClInclude Include="include\Poco\ActiveResult.h" />
<ClInclude Include="include\Poco\ActiveRunnable.h" /> <ClInclude Include="include\Poco\ActiveRunnable.h" />
<ClInclude Include="include\Poco\ActiveStarter.h" /> <ClInclude Include="include\Poco\ActiveStarter.h" />
@@ -1502,6 +1504,8 @@
<ClInclude Include="include\Poco\Ascii.h" /> <ClInclude Include="include\Poco\Ascii.h" />
<ClInclude Include="include\Poco\ASCIIEncoding.h" /> <ClInclude Include="include\Poco\ASCIIEncoding.h" />
<ClInclude Include="include\Poco\AsyncChannel.h" /> <ClInclude Include="include\Poco\AsyncChannel.h" />
<ClInclude Include="include\Poco\AsyncObserver.h" />
<ClInclude Include="include\Poco\AsyncNotificationCenter.h" />
<ClInclude Include="include\Poco\AtomicCounter.h" /> <ClInclude Include="include\Poco\AtomicCounter.h" />
<ClInclude Include="include\Poco\AutoPtr.h" /> <ClInclude Include="include\Poco\AutoPtr.h" />
<ClInclude Include="include\Poco\AutoReleasePool.h" /> <ClInclude Include="include\Poco\AutoReleasePool.h" />

View File

@@ -573,6 +573,9 @@
<ClCompile Include="src\AbstractObserver.cpp"> <ClCompile Include="src\AbstractObserver.cpp">
<Filter>Notifications\Source Files</Filter> <Filter>Notifications\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\AsyncNotificationCenter.cpp">
<Filter>Notifications\Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Notification.cpp"> <ClCompile Include="src\Notification.cpp">
<Filter>Notifications\Source Files</Filter> <Filter>Notifications\Source Files</Filter>
</ClCompile> </ClCompile>
@@ -1448,6 +1451,12 @@
<ClInclude Include="include\Poco\NObserver.h"> <ClInclude Include="include\Poco\NObserver.h">
<Filter>Notifications\Header Files</Filter> <Filter>Notifications\Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\Poco\AsyncObserver.h">
<Filter>Notifications\Header Files</Filter>
</ClInclude>
<ClInclude Include="include\Poco\AsyncNotificationCenter.h">
<Filter>Notifications\Header Files</Filter>
</ClInclude>
<ClInclude Include="include\Poco\Notification.h"> <ClInclude Include="include\Poco\Notification.h">
<Filter>Notifications\Header Files</Filter> <Filter>Notifications\Header Files</Filter>
</ClInclude> </ClInclude>

View File

@@ -878,6 +878,7 @@
<ClCompile Include="src\Ascii.cpp" /> <ClCompile Include="src\Ascii.cpp" />
<ClCompile Include="src\ASCIIEncoding.cpp" /> <ClCompile Include="src\ASCIIEncoding.cpp" />
<ClCompile Include="src\AsyncChannel.cpp" /> <ClCompile Include="src\AsyncChannel.cpp" />
<ClCompile Include="src\AsyncNotificationCenter.cpp" />
<ClCompile Include="src\AtomicCounter.cpp" /> <ClCompile Include="src\AtomicCounter.cpp" />
<ClCompile Include="src\Base32Decoder.cpp" /> <ClCompile Include="src\Base32Decoder.cpp" />
<ClCompile Include="src\Base32Encoder.cpp" /> <ClCompile Include="src\Base32Encoder.cpp" />
@@ -2109,6 +2110,8 @@
<ClInclude Include="include\Poco\Ascii.h" /> <ClInclude Include="include\Poco\Ascii.h" />
<ClInclude Include="include\Poco\ASCIIEncoding.h" /> <ClInclude Include="include\Poco\ASCIIEncoding.h" />
<ClInclude Include="include\Poco\AsyncChannel.h" /> <ClInclude Include="include\Poco\AsyncChannel.h" />
<ClInclude Include="include\Poco\AsyncObserver.h" />
<ClInclude Include="include\Poco\AsyncNotificationCenter.h" />
<ClInclude Include="include\Poco\AtomicCounter.h" /> <ClInclude Include="include\Poco\AtomicCounter.h" />
<ClInclude Include="include\Poco\AutoPtr.h" /> <ClInclude Include="include\Poco\AutoPtr.h" />
<ClInclude Include="include\Poco\AutoReleasePool.h" /> <ClInclude Include="include\Poco\AutoReleasePool.h" />

View File

@@ -579,6 +579,9 @@
<ClCompile Include="src\NotificationCenter.cpp"> <ClCompile Include="src\NotificationCenter.cpp">
<Filter>Notifications\Source Files</Filter> <Filter>Notifications\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\AsyncNotificationCenter.cpp">
<Filter>Notifications\Source Files</Filter>
</ClCompile>
<ClCompile Include="src\NotificationQueue.cpp"> <ClCompile Include="src\NotificationQueue.cpp">
<Filter>Notifications\Source Files</Filter> <Filter>Notifications\Source Files</Filter>
</ClCompile> </ClCompile>
@@ -1447,6 +1450,12 @@
<ClInclude Include="include\Poco\AbstractObserver.h"> <ClInclude Include="include\Poco\AbstractObserver.h">
<Filter>Notifications\Header Files</Filter> <Filter>Notifications\Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\Poco\AsyncObserver.h">
<Filter>Notifications\Header Files</Filter>
</ClInclude>
<ClInclude Include="include\Poco\AsyncNotificationCenter.h">
<Filter>Notifications\Header Files</Filter>
</ClInclude>
<ClInclude Include="include\Poco\NObserver.h"> <ClInclude Include="include\Poco\NObserver.h">
<Filter>Notifications\Header Files</Filter> <Filter>Notifications\Header Files</Filter>
</ClInclude> </ClInclude>

View File

@@ -6,7 +6,7 @@
include $(POCO_BASE)/build/rules/global include $(POCO_BASE)/build/rules/global
objects = ArchiveStrategy Ascii ASCIIEncoding AsyncChannel ActiveThreadPool\ objects = ArchiveStrategy Ascii ASCIIEncoding AsyncChannel AsyncNotificationCenter ActiveThreadPool\
Base32Decoder Base32Encoder Base64Decoder Base64Encoder \ Base32Decoder Base32Encoder Base64Decoder Base64Encoder \
BinaryReader BinaryWriter Bugcheck ByteOrder Channel Checksum Clock Configurable ConsoleChannel \ BinaryReader BinaryWriter Bugcheck ByteOrder Channel Checksum Clock Configurable ConsoleChannel \
Condition CountingStream DateTime LocalDateTime DateTimeFormat DateTimeFormatter DateTimeParser \ Condition CountingStream DateTime LocalDateTime DateTimeFormat DateTimeFormatter DateTimeParser \

View File

@@ -37,12 +37,42 @@ public:
AbstractObserver& operator = (const AbstractObserver& observer); AbstractObserver& operator = (const AbstractObserver& observer);
virtual void notify(Notification* pNf) const = 0; virtual void notify(Notification* pNf) const = 0;
virtual bool equals(const AbstractObserver& observer) const = 0; virtual bool equals(const AbstractObserver& observer) const = 0;
virtual bool accepts(Notification* pNf, const char* pName = 0) const = 0;
[[deprecated("use `Poco::Any accepts(Notification*)` instead")]]
virtual bool accepts(Notification* pNf, const char* pName) const = 0;
virtual bool accepts(const Notification::Ptr& pNf) const = 0;
virtual AbstractObserver* clone() const = 0; virtual AbstractObserver* clone() const = 0;
virtual void start();
/// No-op.
/// This method can be implemented by inheriting classes which require
/// explicit start in order to begin processing notifications.
virtual void disable() = 0; virtual void disable() = 0;
virtual int backlog() const;
/// Returns number of queued messages that this Observer has.
/// For non-active (synchronous) observers, always returns zero.
}; };
//
// inlines
//
inline void AbstractObserver::start()
{
}
inline int AbstractObserver::backlog() const
{
return 0;
}
} // namespace Poco } // namespace Poco

View File

@@ -24,13 +24,12 @@
#include <cstddef> #include <cstddef>
#define poco_any_assert(cond) do { if (!(cond)) std::abort(); } while (0)
namespace Poco { namespace Poco {
class Any; class Any;
using namespace std::string_literals;
namespace Dynamic { namespace Dynamic {
class Var; class Var;
@@ -409,12 +408,12 @@ ValueType* AnyCast(Any* operand)
/// to the stored value. /// to the stored value.
/// ///
/// Example Usage: /// Example Usage:
/// MyType* pTmp = AnyCast<MyType*>(pAny). /// MyType* pTmp = AnyCast<MyType>(pAny).
/// Will return NULL if the cast fails, i.e. types don't match. /// Returns nullptr if the types don't match.
{ {
return operand && operand->type() == typeid(ValueType) return operand && operand->type() == typeid(ValueType)
? &static_cast<Any::Holder<ValueType>*>(operand->content())->_held ? &static_cast<Any::Holder<ValueType>*>(operand->content())->_held
: 0; : nullptr;
} }
@@ -424,8 +423,8 @@ const ValueType* AnyCast(const Any* operand)
/// to the stored value. /// to the stored value.
/// ///
/// Example Usage: /// Example Usage:
/// const MyType* pTmp = AnyCast<MyType*>(pAny). /// const MyType* pTmp = AnyCast<MyType>(pAny).
/// Will return NULL if the cast fails, i.e. types don't match. /// Returns nullptr if the types don't match.
{ {
return AnyCast<ValueType>(const_cast<Any*>(operand)); return AnyCast<ValueType>(const_cast<Any*>(operand));
} }
@@ -442,18 +441,19 @@ ValueType AnyCast(Any& operand)
/// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in /// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in
/// these cases. /// these cases.
{ {
typedef typename TypeWrapper<ValueType>::TYPE NonRef; using NonRef = typename TypeWrapper<ValueType>::TYPE;
NonRef* result = AnyCast<NonRef>(&operand); NonRef* result = AnyCast<NonRef>(&operand);
if (!result) if (!result)
{ {
std::string s = "RefAnyCast: Failed to convert between Any types "; std::string s(__func__);
s.append(": Failed to convert between Any types "s);
if (operand.content()) if (operand.content())
{ {
s.append(1, '('); s.append(1, '(');
s.append(operand.content()->type().name()); s.append(Poco::demangle(operand.content()->type().name()));
s.append(" => "); s.append(" => ");
s.append(typeid(ValueType).name()); s.append(Poco::demangle<ValueType>());
s.append(1, ')'); s.append(1, ')');
} }
throw BadCastException(s); throw BadCastException(s);
@@ -473,7 +473,7 @@ ValueType AnyCast(const Any& operand)
/// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in /// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in
/// these cases. /// these cases.
{ {
typedef typename TypeWrapper<ValueType>::TYPE NonRef; using NonRef = typename TypeWrapper<ValueType>::TYPE;
return AnyCast<NonRef&>(const_cast<Any&>(operand)); return AnyCast<NonRef&>(const_cast<Any&>(operand));
} }
@@ -489,13 +489,14 @@ const ValueType& RefAnyCast(const Any & operand)
ValueType* result = AnyCast<ValueType>(const_cast<Any*>(&operand)); ValueType* result = AnyCast<ValueType>(const_cast<Any*>(&operand));
if (!result) if (!result)
{ {
std::string s = "RefAnyCast: Failed to convert between Any types "; std::string s(__func__);
s.append(": Failed to convert between Any types "s);
if (operand.content()) if (operand.content())
{ {
s.append(1, '('); s.append(1, '(');
s.append(operand.content()->type().name()); s.append(Poco::demangle(operand.content()->type().name()));
s.append(" => "); s.append(" => ");
s.append(typeid(ValueType).name()); s.append(Poco::demangle<ValueType>());
s.append(1, ')'); s.append(1, ')');
} }
throw BadCastException(s); throw BadCastException(s);
@@ -514,13 +515,14 @@ ValueType& RefAnyCast(Any& operand)
ValueType* result = AnyCast<ValueType>(&operand); ValueType* result = AnyCast<ValueType>(&operand);
if (!result) if (!result)
{ {
std::string s = "RefAnyCast: Failed to convert between Any types "; std::string s(__func__);
s.append(": Failed to convert between Any types "s);
if (operand.content()) if (operand.content())
{ {
s.append(1, '('); s.append(1, '(');
s.append(operand.content()->type().name()); s.append(Poco::demangle(operand.content()->type().name()));
s.append(" => "); s.append(" => ");
s.append(typeid(ValueType).name()); s.append(Poco::demangle<ValueType>());
s.append(1, ')'); s.append(1, ')');
} }
throw BadCastException(s); throw BadCastException(s);
@@ -553,6 +555,26 @@ const ValueType* UnsafeAnyCast(const Any* operand)
} }
template <typename ValueType>
bool AnyHoldsNullPtr(const Any& any)
/// Returns true if any holds a null pointer.
/// Fails to compile if `ValueType` is not a pointer.
{
poco_static_assert_ptr(ValueType);
return (AnyCast<ValueType>(any) == nullptr);
}
template <typename ValueType>
bool AnyHoldsNullPtr(const Any* pAny)
/// Returns true if the Any pointed to holds a null pointer.
/// Returns false if `pAny` is a null pointer.
{
if (!pAny) return false;
return (AnyHoldsNullPtr<ValueType>(*pAny));
}
} // namespace Poco } // namespace Poco

View File

@@ -0,0 +1,77 @@
//
// 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 d.o.o.,
// 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/Stopwatch.h"
#include "Poco/Debugger.h"
#include "Poco/ErrorHandler.h"
#include "Poco/Format.h"
#include "Poco/RunnableAdapter.h"
#include "Poco/NotificationQueue.h"
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.
{
public:
AsyncNotificationCenter();
/// Creates the AsyncNotificationCenter and starts the notifying thread.
~AsyncNotificationCenter();
/// Stops the notifying thread and destroys the AsyncNotificationCenter.
AsyncNotificationCenter& operator = (const AsyncNotificationCenter&) = delete;
AsyncNotificationCenter(const AsyncNotificationCenter&) = delete;
AsyncNotificationCenter& operator = (AsyncNotificationCenter&&) = delete;
AsyncNotificationCenter(AsyncNotificationCenter&&) = delete;
virtual void postNotification(Notification::Ptr pNotification);
/// Enqueues notification into the notification queue.
virtual int backlog() const;
/// Returns the numbner of notifications in the notification queue.
private:
void start();
void stop();
void dequeue();
using Adapter = RunnableAdapter<AsyncNotificationCenter>;
Thread _thread;
NotificationQueue _nq;
Adapter _ra;
std::atomic<bool> _started;
std::atomic<bool> _done;
};
} // namespace Poco
#endif // Foundation_AsyncNotificationCenter_INCLUDED

View File

@@ -0,0 +1,177 @@
//
// AsyncObserver.h
//
// Library: Foundation
// Package: Notifications
// Module: AsyncObserver
//
// Definition of the AsyncObserver class template.
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// Aleph ONE Software Engineering d.o.o.,
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Foundation_AsyncObserver_INCLUDED
#define Foundation_AsyncObserver_INCLUDED
#include "Poco/Foundation.h"
#include "Poco/NObserver.h"
#include "Poco/Thread.h"
#include "Poco/Stopwatch.h"
#include "Poco/Debugger.h"
#include "Poco/ErrorHandler.h"
#include "Poco/Format.h"
#include "Poco/RunnableAdapter.h"
#include "Poco/NotificationQueue.h"
namespace Poco {
template <class C, class N>
class AsyncObserver: public NObserver<C, N>
/// AsyncObserver notifies subscribers in a dedicated thread (as opposed
/// to (N)Observer classes, which notify subscribers synchronously).
/// In order to become active and process notifications, the start()
/// method must be called.
///
/// This class is meant to be used with the NotificationCenter only.
/// Notification processing thread can be started only once, and copying
/// should be done before `start()` is called.
{
public:
using Type = AsyncObserver<C, N>;
using Matcher = typename NObserver<C, N>::Matcher;
using Handler = typename NObserver<C, N>::Handler;
using NotificationPtr = typename NObserver<C, N>::NotificationPtr;
AsyncObserver() = delete;
AsyncObserver(C& object, Handler handler, Matcher matcher = nullptr):
NObserver<C, N>(object, handler, matcher),
_ra(*this, &AsyncObserver::dequeue),
_started(false),
_done(false)
{
}
AsyncObserver(const AsyncObserver& observer):
NObserver<C, N>(observer),
_ra(*this, &AsyncObserver::dequeue),
_started(false),
_done(false)
{
poco_assert(observer._nq.size() == 0);
}
~AsyncObserver()
{
disable();
}
AsyncObserver& operator = (const AsyncObserver& observer)
{
if (&observer != this)
{
poco_assert(observer._nq.size() == 0);
setObject(observer._pObject);
setHandler(observer._handler);
setMatcher(observer._matcher);
_started = false;
_done =false;
}
return *this;
}
virtual void notify(Notification* pNf) const
{
_nq.enqueueNotification(NotificationPtr(static_cast<N*>(pNf), true));
}
virtual AbstractObserver* clone() const
{
return new AsyncObserver(*this);
}
virtual void start()
{
Poco::ScopedLock l(this->mutex());
if (_started)
{
throw Poco::InvalidAccessException(
Poco::format("thread already started %s", poco_src_loc));
}
_thread.start(_ra);
Poco::Stopwatch sw;
sw.start();
while (!_started)
{
if (sw.elapsedSeconds() > 5)
throw Poco::TimeoutException(poco_src_loc);
Thread::sleep(100);
}
}
virtual void disable()
{
if (!_started.exchange(false)) return;
_nq.wakeUpAll();
while (!_done) Thread::sleep(100);
_thread.join();
NObserver<C, N>::disable();
}
virtual int backlog() const
{
return _nq.size();
}
private:
void dequeue()
{
Notification::Ptr pNf;
_started = true;
_done = false;
while ((pNf = _nq.waitDequeueNotification()))
{
try
{
this->handle(pNf.unsafeCast<N>());
}
catch (Poco::Exception& ex)
{
Poco::ErrorHandler::handle(ex);
}
catch (std::exception& ex)
{
Poco::ErrorHandler::handle(ex);
}
catch (...)
{
Poco::ErrorHandler::handle();
}
}
_done = true;
_started = false;
}
using Adapter = RunnableAdapter<AsyncObserver<C, N>>;
Thread _thread;
mutable NotificationQueue _nq;
Adapter _ra;
std::atomic<bool> _started;
std::atomic<bool> _done;
};
} // namespace Poco
#endif // Foundation_AsyncObserver_INCLUDED

View File

@@ -165,42 +165,16 @@ protected:
#endif #endif
// #define poco_static_assert(B) static_assert(B)
// poco_static_assert
//
// The following was ported from <boost/static_assert.hpp>
//
template <bool x> #define poco_static_assert_ptr(T) \
struct POCO_STATIC_ASSERTION_FAILURE; static_assert(std::is_pointer_v<T> || \
std::is_same_v<T, nullptr_t> || \
std::is_member_pointer_v<T> || \
template <> std::is_member_function_pointer_v<T> || \
struct POCO_STATIC_ASSERTION_FAILURE<true> std::is_member_object_pointer_v<T>, \
{ "not a pointer")
enum
{
value = 1
};
};
template <int x>
struct poco_static_assert_test
{
};
#if defined(__GNUC__) && (__GNUC__ == 3) && ((__GNUC_MINOR__ == 3) || (__GNUC_MINOR__ == 4))
#define poco_static_assert(B) \
typedef char POCO_JOIN(poco_static_assert_typedef_, __LINE__) \
[POCO_STATIC_ASSERTION_FAILURE<(bool) (B)>::value]
#else
#define poco_static_assert(B) \
typedef poco_static_assert_test<sizeof(POCO_STATIC_ASSERTION_FAILURE<(bool) (B)>)> \
POCO_JOIN(poco_static_assert_typedef_, __LINE__) POCO_UNUSED
#endif
#endif // Foundation_Bugcheck_INCLUDED #endif // Foundation_Bugcheck_INCLUDED

View File

@@ -3,7 +3,7 @@
// //
// Library: Foundation // Library: Foundation
// Package: Notifications // Package: Notifications
// Module: NotificationCenter // Module: NObserver
// //
// Definition of the NObserver class template. // Definition of the NObserver class template.
// //
@@ -37,25 +37,39 @@ class NObserver: public AbstractObserver
/// to use this template class. /// to use this template class.
/// ///
/// This class template is quite similar to the Observer class /// This class template is quite similar to the Observer class
/// template. The only difference is that the NObserver /// template. The differences are:
/// expects the callback function to accept a const AutoPtr& ///
/// instead of a plain pointer as argument, thus simplifying memory /// - NObserver expects the callback function to accept a const AutoPtr&
/// management. /// instead of a plain pointer as argument, thus simplifying memory
/// management.
///
/// - In addition to dispatching notifications based on the Notification runtime
/// type, NObserver can also notify subscribers based on the Notification name.
/// To enable this functionality, a matcher function must be provided.
/// Null matcher means no matching is performed and all notificiations
/// of the type subscribed to are dispatched.
{ {
public: public:
typedef AutoPtr<N> NotificationPtr; using Type = NObserver<C, N>;
typedef void (C::*Callback)(const NotificationPtr&); using NotificationPtr = AutoPtr<N>;
using Callback = void (C::*)(const NotificationPtr&);
using Handler = Callback;
using Matcher = bool (C::*)(const std::string&) const;
NObserver(C& object, Callback method): NObserver() = delete;
NObserver(C& object, Handler method, Matcher matcher = nullptr):
_pObject(&object), _pObject(&object),
_method(method) _handler(method),
_matcher(matcher)
{ {
} }
NObserver(const NObserver& observer): NObserver(const NObserver& observer):
AbstractObserver(observer), AbstractObserver(observer),
_pObject(observer._pObject), _pObject(observer._pObject),
_method(observer._method) _handler(observer._handler),
_matcher(observer._matcher)
{ {
} }
@@ -68,54 +82,72 @@ public:
if (&observer != this) if (&observer != this)
{ {
_pObject = observer._pObject; _pObject = observer._pObject;
_method = observer._method; _handler = observer._handler;
_matcher = observer._matcher;
} }
return *this; return *this;
} }
void notify(Notification* pNf) const virtual void notify(Notification* pNf) const
{ {
Poco::Mutex::ScopedLock lock(_mutex); handle(NotificationPtr(static_cast<N*>(pNf), true));
if (_pObject)
{
N* pCastNf = dynamic_cast<N*>(pNf);
if (pCastNf)
{
NotificationPtr ptr(pCastNf, true);
(_pObject->*_method)(ptr);
}
}
} }
bool equals(const AbstractObserver& abstractObserver) const virtual bool equals(const AbstractObserver& abstractObserver) const
{ {
const NObserver* pObs = dynamic_cast<const NObserver*>(&abstractObserver); const NObserver* pObs = dynamic_cast<const NObserver*>(&abstractObserver);
return pObs && pObs->_pObject == _pObject && pObs->_method == _method; return pObs && pObs->_pObject == _pObject && pObs->_handler == _handler && pObs->_matcher == _matcher;
} }
bool accepts(Notification* pNf, const char* pName = 0) const [[deprecated("use `bool accepts(const Notification::Ptr&)` instead")]]
virtual bool accepts(Notification* pNf, const char* pName) const
{ {
return dynamic_cast<N*>(pNf) && (!pName || pNf->name() == pName); return (!pName || pNf->name() == pName) && dynamic_cast<N*>(pNf) != nullptr;
} }
AbstractObserver* clone() const virtual bool accepts(const Notification::Ptr& pNf) const
{
return (match(pNf) && (pNf.template cast<N>() != nullptr));
}
virtual AbstractObserver* clone() const
{ {
return new NObserver(*this); return new NObserver(*this);
} }
void disable() virtual void disable()
{ {
Poco::Mutex::ScopedLock lock(_mutex); Poco::Mutex::ScopedLock lock(_mutex);
_pObject = 0; _pObject = nullptr;
}
protected:
void handle(const NotificationPtr& ptr) const
{
Mutex::ScopedLock lock(_mutex);
if (_pObject)
(_pObject->*_handler)(ptr);
}
bool match(const Notification::Ptr& ptr) const
{
Mutex::ScopedLock l(_mutex);
return _pObject && (!_matcher || (_pObject->*_matcher)(ptr->name()));
}
Mutex& mutex() const
{
return _mutex;
} }
private: private:
NObserver();
C* _pObject; C* _pObject;
Callback _method; Callback _handler;
Matcher _matcher;
mutable Poco::Mutex _mutex; mutable Poco::Mutex _mutex;
}; };

View File

@@ -22,6 +22,7 @@
#include "Poco/Mutex.h" #include "Poco/Mutex.h"
#include "Poco/RefCountedObject.h" #include "Poco/RefCountedObject.h"
#include "Poco/AutoPtr.h" #include "Poco/AutoPtr.h"
#include <memory>
namespace Poco { namespace Poco {
@@ -37,7 +38,7 @@ class Foundation_API Notification: public RefCountedObject
public: public:
using Ptr = AutoPtr<Notification>; using Ptr = AutoPtr<Notification>;
Notification(); Notification(const std::string& name = ""s);
/// Creates the notification. /// Creates the notification.
virtual std::string name() const; virtual std::string name() const;
@@ -46,6 +47,7 @@ public:
protected: protected:
virtual ~Notification(); virtual ~Notification();
std::unique_ptr<std::string> _pName;
}; };

View File

@@ -79,10 +79,11 @@ class Foundation_API NotificationCenter
/// } /// }
{ {
public: public:
NotificationCenter(); NotificationCenter();
/// Creates the NotificationCenter. /// Creates the NotificationCenter.
~NotificationCenter(); virtual ~NotificationCenter();
/// Destroys the NotificationCenter. /// Destroys the NotificationCenter.
void addObserver(const AbstractObserver& observer); void addObserver(const AbstractObserver& observer);
@@ -99,7 +100,7 @@ public:
bool hasObserver(const AbstractObserver& observer) const; bool hasObserver(const AbstractObserver& observer) const;
/// Returns true if the observer is registered with this NotificationCenter. /// Returns true if the observer is registered with this NotificationCenter.
void postNotification(Notification::Ptr pNotification); virtual void postNotification(Notification::Ptr pNotification);
/// Posts a notification to the NotificationCenter. /// Posts a notification to the NotificationCenter.
/// The NotificationCenter then delivers the notification /// The NotificationCenter then delivers the notification
/// to all interested observers. /// to all interested observers.
@@ -120,13 +121,29 @@ public:
std::size_t countObservers() const; std::size_t countObservers() const;
/// Returns the number of registered observers. /// Returns the number of registered observers.
virtual int backlog() const;
/// Returns the sum of queued notifications
/// for all observers (applies only to asynchronous observers,
/// regular observers post notifications syncronously and
/// never have a backlog).
static NotificationCenter& defaultCenter(); static NotificationCenter& defaultCenter();
/// Returns a reference to the default /// Returns a reference to the default
/// NotificationCenter. /// NotificationCenter.
protected:
using AbstractObserverPtr = SharedPtr<AbstractObserver>;
using ObserverList = std::vector<AbstractObserverPtr>;
Mutex& mutex()
{
return _mutex;
}
ObserverList observersToNotify(const Notification::Ptr& pNotification) const;
void notifyObservers(Notification::Ptr& pNotification);
private: private:
typedef SharedPtr<AbstractObserver> AbstractObserverPtr;
typedef std::vector<AbstractObserverPtr> ObserverList;
ObserverList _observers; ObserverList _observers;
mutable Mutex _mutex; mutable Mutex _mutex;

View File

@@ -74,15 +74,10 @@ public:
void notify(Notification* pNf) const void notify(Notification* pNf) const
{ {
Poco::Mutex::ScopedLock lock(_mutex); Poco::Mutex::ScopedLock lock(_mutex);
if (_pObject) if (_pObject)
{ {
N* pCastNf = dynamic_cast<N*>(pNf); pNf->duplicate();
if (pCastNf) (_pObject->*_method)(static_cast<N*>(pNf));
{
pCastNf->duplicate();
(_pObject->*_method)(pCastNf);
}
} }
} }
@@ -92,9 +87,15 @@ public:
return pObs && pObs->_pObject == _pObject && pObs->_method == _method; return pObs && pObs->_pObject == _pObject && pObs->_method == _method;
} }
bool accepts(Notification* pNf, const char* pName = 0) const [[deprecated("use `bool accepts(const Notification::Ptr&)` instead")]]
bool accepts(Notification* pNf, const char* pName) const
{ {
return dynamic_cast<N*>(pNf) && (!pName || pNf->name() == pName); return (!pName || pNf->name() == pName) && (dynamic_cast<N*>(pNf) != nullptr);
}
bool accepts(const Notification::Ptr& pNf) const
{
return (pNf.cast<N>() != nullptr);
} }
AbstractObserver* clone() const AbstractObserver* clone() const

View File

@@ -32,7 +32,7 @@ class RunnableAdapter: public Runnable
/// Usage: /// Usage:
/// RunnableAdapter<MyClass> ra(myObject, &MyObject::doSomething)); /// RunnableAdapter<MyClass> ra(myObject, &MyObject::doSomething));
/// Thread thr; /// Thread thr;
/// thr.Start(ra); /// thr.start(ra);
/// ///
/// For using a freestanding or static member function as a thread /// For using a freestanding or static member function as a thread
/// target, please see the ThreadTarget class. /// target, please see the ThreadTarget class.

View File

@@ -0,0 +1,106 @@
//
// AsyncNotificationCenter.cpp
//
// Library: Foundation
// Package: Notifications
// Module: AsyncNotificationCenter
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// Aleph ONE Software Engineering d.o.o.,
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/AsyncNotificationCenter.h"
namespace Poco {
AsyncNotificationCenter::AsyncNotificationCenter(): _ra(*this, &AsyncNotificationCenter::dequeue),
_started(false),
_done(false)
{
start();
}
AsyncNotificationCenter::~AsyncNotificationCenter()
{
stop();
}
void AsyncNotificationCenter::postNotification(Notification::Ptr pNotification)
{
_nq.enqueueNotification(pNotification);
}
int AsyncNotificationCenter::backlog() const
{
return _nq.size();
}
void AsyncNotificationCenter::start()
{
Poco::ScopedLock l(mutex());
if (_started)
{
throw Poco::InvalidAccessException(
Poco::format("thread already started %s", poco_src_loc));
}
_thread.start(_ra);
Poco::Stopwatch sw;
sw.start();
while (!_started)
{
if (sw.elapsedSeconds() > 5)
throw Poco::TimeoutException(poco_src_loc);
Thread::sleep(100);
}
}
void AsyncNotificationCenter::stop()
{
if (!_started.exchange(false)) return;
_nq.wakeUpAll();
while (!_done) Thread::sleep(100);
_thread.join();
}
void AsyncNotificationCenter::dequeue()
{
Notification::Ptr pNf;
_started = true;
_done = false;
while ((pNf = _nq.waitDequeueNotification()))
{
try
{
notifyObservers(pNf);
}
catch (Poco::Exception& ex)
{
Poco::ErrorHandler::handle(ex);
}
catch (std::exception& ex)
{
Poco::ErrorHandler::handle(ex);
}
catch (...)
{
Poco::ErrorHandler::handle();
}
}
_done = true;
_started = false;
}
} // namespace Poco

View File

@@ -19,7 +19,8 @@
namespace Poco { namespace Poco {
Notification::Notification() Notification::Notification(const std::string& name):
_pName(name.empty() ? nullptr : new std::string(name))
{ {
} }
@@ -31,7 +32,7 @@ Notification::~Notification()
std::string Notification::name() const std::string Notification::name() const
{ {
return typeid(*this).name(); return _pName ? *_pName : typeid(*this).name();
} }

View File

@@ -47,6 +47,7 @@ void NotificationCenter::addObserver(const AbstractObserver& observer)
{ {
Mutex::ScopedLock lock(_mutex); Mutex::ScopedLock lock(_mutex);
_observers.push_back(observer.clone()); _observers.push_back(observer.clone());
_observers.back()->start();
} }
@@ -75,17 +76,34 @@ bool NotificationCenter::hasObserver(const AbstractObserver& observer) const
} }
NotificationCenter::ObserverList NotificationCenter::observersToNotify(const Notification::Ptr& pNotification) const
{
ObserverList ret;
ScopedLock<Mutex> lock(_mutex);
for (auto& o : _observers)
{
if (o->accepts(pNotification))
ret.push_back(o);
}
return ret;
}
void NotificationCenter::postNotification(Notification::Ptr pNotification) void NotificationCenter::postNotification(Notification::Ptr pNotification)
{ {
poco_check_ptr (pNotification); poco_check_ptr (pNotification);
ScopedLockWithUnlock<Mutex> lock(_mutex); notifyObservers(pNotification);
ObserverList observersToNotify(_observers); }
lock.unlock();
for (auto& p: observersToNotify)
{ void NotificationCenter::notifyObservers(Notification::Ptr& pNotification)
{
poco_check_ptr (pNotification);
ObserverList observers = observersToNotify(pNotification);
for (auto& p: observers)
p->notify(pNotification); p->notify(pNotification);
}
} }
@@ -105,6 +123,20 @@ std::size_t NotificationCenter::countObservers() const
} }
int NotificationCenter::backlog() const
{
int cnt = 0;
ScopedLockWithUnlock<Mutex> lock(_mutex);
ObserverList observersToCount(_observers);
lock.unlock();
for (auto& p : observersToCount)
cnt += p->backlog();
return cnt;
}
NotificationCenter& NotificationCenter::defaultCenter() NotificationCenter& NotificationCenter::defaultCenter()
{ {
static NotificationCenter nc; static NotificationCenter nc;

View File

@@ -334,6 +334,111 @@ void AnyTest::testAnyInt()
} }
class A
{
public:
void f() {}
int m;
};
void AnyTest::testAnyPointer()
{
int i = 13;
Any a = &i;
assertTrue (a.type() == typeid(int*));
assertFalse (AnyHoldsNullPtr<int*>(a));
assertFalse (AnyHoldsNullPtr<int*>(&a));
int* p = AnyCast<int*>(&i);
assertTrue (*p == 13);
Any b = a;
assertTrue (b.type() == typeid(int*));
int* cpyI = AnyCast<int*>(b);
assertTrue (*cpyI == *p);
*cpyI = 20;
assertTrue (*cpyI == *p);
std::string* s = AnyCast<std::string>(&a);
assertTrue (s == NULL);
assertTrue (AnyCast<nullptr_t>(&a) == nullptr);
int* POCO_UNUSED tmp = AnyCast<int*>(a);
const Any c = a;
tmp = AnyCast<int*>(a);
Any nullPtr(nullptr);
assertFalse (AnyHoldsNullPtr<nullptr_t>(nullptr));
assertFalse (AnyHoldsNullPtr<void*>(0));
assertTrue (AnyHoldsNullPtr<nullptr_t>(nullPtr));
assertTrue (AnyHoldsNullPtr<nullptr_t>(&nullPtr));
try
{
AnyHoldsNullPtr<void*>(nullPtr);
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}
nullPtr = &i;
try
{
assertFalse (AnyHoldsNullPtr<nullptr_t>(nullPtr));
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}
assertFalse (AnyHoldsNullPtr<int*>(nullPtr));
void* voidPtr = nullptr;
Any nullVoidPtr(voidPtr);
assertTrue (AnyHoldsNullPtr<void*>(nullVoidPtr));
try
{
AnyHoldsNullPtr<nullptr_t>(voidPtr);
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}
using FP = void (AnyTest::*)();
FP fp = nullptr;
Any funcPtr(fp);
assertTrue (AnyHoldsNullPtr<FP>(funcPtr));
try
{
AnyHoldsNullPtr<FP>(voidPtr);
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}
funcPtr = &AnyTest::testAnyPointer;
assertFalse (AnyHoldsNullPtr<FP>(funcPtr));
using OP = decltype(&AnyTest::_dummyObject);
OP op = nullptr;
Any objPtr(op);
assertTrue (AnyHoldsNullPtr<OP>(objPtr));
objPtr = &AnyTest::_dummyObject;
try
{
AnyHoldsNullPtr<OP>(funcPtr);
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}
assertFalse (AnyHoldsNullPtr<OP>(objPtr));
using MP = decltype(&AnyTest::_dummy);
MP mp = nullptr;
Any memPtr(mp);
assertTrue (AnyHoldsNullPtr<MP>(memPtr));
memPtr = &AnyTest::_dummy;
try
{
AnyHoldsNullPtr<MP>(objPtr);
fail ("AnyCast must fail", __LINE__, __FILE__);
}
catch(const Poco::BadCastException&) {}
assertFalse (AnyHoldsNullPtr<MP>(memPtr));
}
void AnyTest::testAnyComplexType() void AnyTest::testAnyComplexType()
{ {
SomeClass str(13,std::string("hello")); SomeClass str(13,std::string("hello"));
@@ -456,6 +561,7 @@ CppUnit::Test* AnyTest::suite()
CppUnit_addTest(pSuite, AnyTest, testAnySwap); CppUnit_addTest(pSuite, AnyTest, testAnySwap);
CppUnit_addTest(pSuite, AnyTest, testAnyEmptyCopy); CppUnit_addTest(pSuite, AnyTest, testAnyEmptyCopy);
CppUnit_addTest(pSuite, AnyTest, testAnyCastToReference); CppUnit_addTest(pSuite, AnyTest, testAnyCastToReference);
CppUnit_addTest(pSuite, AnyTest, testAnyPointer);
CppUnit_addTest(pSuite, AnyTest, testAnyInt); CppUnit_addTest(pSuite, AnyTest, testAnyInt);
CppUnit_addTest(pSuite, AnyTest, testAnyComplexType); CppUnit_addTest(pSuite, AnyTest, testAnyComplexType);
CppUnit_addTest(pSuite, AnyTest, testAnyVector); CppUnit_addTest(pSuite, AnyTest, testAnyVector);

View File

@@ -20,6 +20,8 @@
class AnyTest: public CppUnit::TestCase class AnyTest: public CppUnit::TestCase
{ {
public: public:
class Dummy{};
AnyTest(const std::string& name); AnyTest(const std::string& name);
~AnyTest(); ~AnyTest();
@@ -33,6 +35,7 @@ public:
void testAnyEmptyCopy(); void testAnyEmptyCopy();
void testAnyCastToReference(); void testAnyCastToReference();
void testAnyPointer();
void testAnyInt(); void testAnyInt();
void testAnyComplexType(); void testAnyComplexType();
void testAnyVector(); void testAnyVector();
@@ -42,6 +45,10 @@ public:
void setUp(); void setUp();
void tearDown(); void tearDown();
static CppUnit::Test* suite(); static CppUnit::Test* suite();
private:
int _dummy = 0;
Dummy _dummyObject;
}; };

View File

@@ -377,7 +377,7 @@ void FIFOEventTest::testAsyncNotifyBenchmark()
assertTrue (vresult[i].data() == (i*2)); assertTrue (vresult[i].data() == (i*2));
} }
sw.stop(); sw.stop();
times.push_back(sw.elapsed()/1000); times.push_back(static_cast<int>(sw.elapsed()/1000));
vresult.clear(); vresult.clear();
} }
@@ -451,7 +451,7 @@ void FIFOEventTest::onAsyncBench(const void* pSender, int& i)
int FIFOEventTest::getCount() const int FIFOEventTest::getCount() const
{ {
return _count; return static_cast<int>(_count.load());
} }

View File

@@ -12,24 +12,40 @@
#include "CppUnit/TestCaller.h" #include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h" #include "CppUnit/TestSuite.h"
#include "Poco/NotificationCenter.h" #include "Poco/NotificationCenter.h"
#include "Poco/AsyncNotificationCenter.h"
#include "Poco/Observer.h" #include "Poco/Observer.h"
#include "Poco/NObserver.h" #include "Poco/NObserver.h"
#include "Poco/AsyncObserver.h"
#include "Poco/AutoPtr.h" #include "Poco/AutoPtr.h"
using Poco::NotificationCenter; using Poco::NotificationCenter;
using Poco::AsyncNotificationCenter;
using Poco::Observer; using Poco::Observer;
using Poco::NObserver; using Poco::NObserver;
using Poco::AsyncObserver;
using Poco::Notification; using Poco::Notification;
using Poco::AutoPtr; using Poco::AutoPtr;
class TestNotification: public Notification class TestNotification: public Notification
{ {
public:
TestNotification()
{}
TestNotification(const std::string& name):
Notification(name)
{}
}; };
NotificationCenterTest::NotificationCenterTest(const std::string& name): CppUnit::TestCase(name) NotificationCenterTest::NotificationCenterTest(const std::string& name):
CppUnit::TestCase(name),
_handle1Done(false),
_handleAuto1Done(false),
_handleAsync1Done(false),
_handleAsync2Done(false)
{ {
} }
@@ -39,14 +55,14 @@ NotificationCenterTest::~NotificationCenterTest()
} }
void NotificationCenterTest::test1() void NotificationCenterTest::testNotificationCenter1()
{ {
NotificationCenter nc; NotificationCenter nc;
nc.postNotification(new Notification); nc.postNotification(new Notification);
} }
void NotificationCenterTest::test2() void NotificationCenterTest::testNotificationCenter2()
{ {
NotificationCenter nc; NotificationCenter nc;
Observer<NotificationCenterTest, Notification> o(*this, &NotificationCenterTest::handle1); Observer<NotificationCenterTest, Notification> o(*this, &NotificationCenterTest::handle1);
@@ -64,7 +80,7 @@ void NotificationCenterTest::test2()
} }
void NotificationCenterTest::test3() void NotificationCenterTest::testNotificationCenter3()
{ {
NotificationCenter nc; NotificationCenter nc;
Observer<NotificationCenterTest, Notification> o1(*this, &NotificationCenterTest::handle1); Observer<NotificationCenterTest, Notification> o1(*this, &NotificationCenterTest::handle1);
@@ -88,7 +104,7 @@ void NotificationCenterTest::test3()
} }
void NotificationCenterTest::test4() void NotificationCenterTest::testNotificationCenter4()
{ {
NotificationCenter nc; NotificationCenter nc;
Observer<NotificationCenterTest, Notification> o1(*this, &NotificationCenterTest::handle1); Observer<NotificationCenterTest, Notification> o1(*this, &NotificationCenterTest::handle1);
@@ -119,7 +135,7 @@ void NotificationCenterTest::test4()
} }
void NotificationCenterTest::test5() void NotificationCenterTest::testNotificationCenter5()
{ {
NotificationCenter nc; NotificationCenter nc;
nc.addObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1)); nc.addObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
@@ -137,7 +153,7 @@ void NotificationCenterTest::test5()
} }
void NotificationCenterTest::testAuto() void NotificationCenterTest::testNotificationCenterAuto()
{ {
NotificationCenter nc; NotificationCenter nc;
nc.addObserver(NObserver<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handleAuto)); nc.addObserver(NObserver<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handleAuto));
@@ -148,7 +164,59 @@ void NotificationCenterTest::testAuto()
} }
void NotificationCenterTest::testDefaultCenter() 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 = AsyncObserver<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::testDefaultNotificationCenter()
{ {
NotificationCenter& nc = NotificationCenter::defaultCenter(); NotificationCenter& nc = NotificationCenter::defaultCenter();
nc.addObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1)); nc.addObserver(Observer<NotificationCenterTest, Notification>(*this, &NotificationCenterTest::handle1));
@@ -159,11 +227,38 @@ void NotificationCenterTest::testDefaultCenter()
} }
void NotificationCenterTest::testMixedObservers()
{
using AObserverT = AsyncObserver<NotificationCenterTest, TestNotification>::Type;
AsyncNotificationCenter nc;
nc.addObserver(Observer<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(Observer<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(Poco::Notification* pNf) void NotificationCenterTest::handle1(Poco::Notification* pNf)
{ {
Poco::Mutex::ScopedLock l(_mutex);
poco_check_ptr (pNf); poco_check_ptr (pNf);
AutoPtr<Notification> nf = pNf; AutoPtr<Notification> nf = pNf;
_set.insert("handle1"); _set.insert("handle1");
_handle1Done = true;
} }
@@ -193,7 +288,31 @@ void NotificationCenterTest::handleTest(TestNotification* pNf)
void NotificationCenterTest::handleAuto(const AutoPtr<Notification>& pNf) void NotificationCenterTest::handleAuto(const AutoPtr<Notification>& pNf)
{ {
Poco::Mutex::ScopedLock l(_mutex);
_set.insert("handleAuto"); _set.insert("handleAuto");
_handleAuto1Done = true;
}
void NotificationCenterTest::handleAsync1(const AutoPtr<TestNotification>& pNf)
{
Poco::Mutex::ScopedLock l(_mutex);
_set.insert("handleAsync1");
_handleAsync1Done = true;
}
void NotificationCenterTest::handleAsync2(const AutoPtr<TestNotification>& pNf)
{
Poco::Mutex::ScopedLock l(_mutex);
_set.insert("handleAsync2");
_handleAsync2Done = true;
}
bool NotificationCenterTest::matchAsync(const std::string& name) const
{
return name.find("asyncNotification") == 0;
} }
@@ -212,13 +331,16 @@ CppUnit::Test* NotificationCenterTest::suite()
{ {
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("NotificationCenterTest"); CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("NotificationCenterTest");
CppUnit_addTest(pSuite, NotificationCenterTest, test1); CppUnit_addTest(pSuite, NotificationCenterTest, testNotificationCenter1);
CppUnit_addTest(pSuite, NotificationCenterTest, test2); CppUnit_addTest(pSuite, NotificationCenterTest, testNotificationCenter2);
CppUnit_addTest(pSuite, NotificationCenterTest, test3); CppUnit_addTest(pSuite, NotificationCenterTest, testNotificationCenter3);
CppUnit_addTest(pSuite, NotificationCenterTest, test4); CppUnit_addTest(pSuite, NotificationCenterTest, testNotificationCenter4);
CppUnit_addTest(pSuite, NotificationCenterTest, test5); CppUnit_addTest(pSuite, NotificationCenterTest, testNotificationCenter5);
CppUnit_addTest(pSuite, NotificationCenterTest, testAuto); CppUnit_addTest(pSuite, NotificationCenterTest, testNotificationCenterAuto);
CppUnit_addTest(pSuite, NotificationCenterTest, testDefaultCenter); CppUnit_addTest(pSuite, NotificationCenterTest, testAsyncObserver);
CppUnit_addTest(pSuite, NotificationCenterTest, testAsyncNotificationCenter);
CppUnit_addTest(pSuite, NotificationCenterTest, testDefaultNotificationCenter);
CppUnit_addTest(pSuite, NotificationCenterTest, testMixedObservers);
return pSuite; return pSuite;
} }

View File

@@ -18,6 +18,7 @@
#include "CppUnit/TestCase.h" #include "CppUnit/TestCase.h"
#include "Poco/Notification.h" #include "Poco/Notification.h"
#include "Poco/AutoPtr.h" #include "Poco/AutoPtr.h"
#include "Poco/Mutex.h"
#include <set> #include <set>
@@ -30,13 +31,16 @@ public:
NotificationCenterTest(const std::string& name); NotificationCenterTest(const std::string& name);
~NotificationCenterTest(); ~NotificationCenterTest();
void test1(); void testNotificationCenter1();
void test2(); void testNotificationCenter2();
void test3(); void testNotificationCenter3();
void test4(); void testNotificationCenter4();
void test5(); void testNotificationCenter5();
void testAuto(); void testNotificationCenterAuto();
void testDefaultCenter(); void testAsyncObserver();
void testAsyncNotificationCenter();
void testDefaultNotificationCenter();
void testMixedObservers();
void setUp(); void setUp();
void tearDown(); void tearDown();
@@ -49,9 +53,17 @@ protected:
void handle3(Poco::Notification* pNf); void handle3(Poco::Notification* pNf);
void handleTest(TestNotification* pNf); void handleTest(TestNotification* pNf);
void handleAuto(const Poco::AutoPtr<Poco::Notification>& pNf); void handleAuto(const Poco::AutoPtr<Poco::Notification>& pNf);
void handleAsync1(const Poco::AutoPtr<TestNotification>& pNf);
void handleAsync2(const Poco::AutoPtr<TestNotification>& pNf);
bool matchAsync(const std::string& name) const;
private: private:
std::set<std::string> _set; std::set<std::string> _set;
std::atomic<bool> _handle1Done;
std::atomic<bool> _handleAuto1Done;
std::atomic<bool> _handleAsync1Done;
std::atomic<bool> _handleAsync2Done;
Poco::Mutex _mutex;
}; };

View File

@@ -1997,12 +1997,12 @@ void VarTest::testLimitsFloat()
{ {
if (std::numeric_limits<double>::max() != std::numeric_limits<float>::max()) if (std::numeric_limits<double>::max() != std::numeric_limits<float>::max())
{ {
double iMin = -1 * std::numeric_limits<float>::max(); constexpr double iMin = -1 * std::numeric_limits<float>::max();
Var da = iMin * 10; Var da = iMin * 10;
try { float POCO_UNUSED f; f = da; fail("must throw", __LINE__, __FILE__); } try { float POCO_UNUSED f; f = da; fail("must throw", __LINE__, __FILE__); }
catch (RangeException&) {} catch (RangeException&) {}
double iMax = std::numeric_limits<float>::max(); constexpr double iMax = std::numeric_limits<float>::max();
da = iMax * 10; da = iMax * 10;
try { float POCO_UNUSED f; f = da; fail("must throw", __LINE__, __FILE__); } try { float POCO_UNUSED f; f = da; fail("must throw", __LINE__, __FILE__); }
catch (RangeException&) {} catch (RangeException&) {}
@@ -2027,13 +2027,13 @@ void VarTest::testLimitsFloat()
float f = 0.f; float f = 0.f;
try { f = anyInt.convert<float>(); fail("must throw", __LINE__, __FILE__); } try { f = anyInt.convert<float>(); fail("must throw", __LINE__, __FILE__); }
catch (Poco::RangeException&) {} catch (Poco::RangeException&) {}
i = f; i = static_cast<int64_t>(f);
assertTrue (0 == i); assertTrue (0 == i);
double d = 0.; double d = 0.;
try { d = anyInt.convert<double>(); fail("must throw", __LINE__, __FILE__); } try { d = anyInt.convert<double>(); fail("must throw", __LINE__, __FILE__); }
catch (Poco::RangeException&) {} catch (Poco::RangeException&) {}
i = d; i = static_cast<int64_t>(d);
assertTrue (0 == i); assertTrue (0 == i);
} }