Skip to content

Commit

Permalink
AsyncObserver (#4444)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
aleks-f committed Feb 16, 2024
1 parent 30a0a06 commit 88be669
Show file tree
Hide file tree
Showing 24 changed files with 882 additions and 139 deletions.
4 changes: 4 additions & 0 deletions Foundation/Foundation_vs160.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,7 @@
<ClCompile Include="src\Ascii.cpp" />
<ClCompile Include="src\ASCIIEncoding.cpp" />
<ClCompile Include="src\AsyncChannel.cpp" />
<ClCompile Include="src\AsyncNotificationCenter.cpp" />
<ClCompile Include="src\AtomicCounter.cpp" />
<ClCompile Include="src\Base32Decoder.cpp" />
<ClCompile Include="src\Base32Encoder.cpp" />
Expand Down Expand Up @@ -1493,6 +1494,7 @@
<ClInclude Include="include\Poco\AccessExpireStrategy.h" />
<ClInclude Include="include\Poco\ActiveDispatcher.h" />
<ClInclude Include="include\Poco\ActiveMethod.h" />
<ClInclude Include="include\Poco\AsyncObserver.h" />
<ClInclude Include="include\Poco\ActiveResult.h" />
<ClInclude Include="include\Poco\ActiveRunnable.h" />
<ClInclude Include="include\Poco\ActiveStarter.h" />
Expand All @@ -1502,6 +1504,8 @@
<ClInclude Include="include\Poco\Ascii.h" />
<ClInclude Include="include\Poco\ASCIIEncoding.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\AutoPtr.h" />
<ClInclude Include="include\Poco\AutoReleasePool.h" />
Expand Down
9 changes: 9 additions & 0 deletions Foundation/Foundation_vs160.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,9 @@
<ClCompile Include="src\AbstractObserver.cpp">
<Filter>Notifications\Source Files</Filter>
</ClCompile>
<ClCompile Include="src\AsyncNotificationCenter.cpp">
<Filter>Notifications\Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Notification.cpp">
<Filter>Notifications\Source Files</Filter>
</ClCompile>
Expand Down Expand Up @@ -1448,6 +1451,12 @@
<ClInclude Include="include\Poco\NObserver.h">
<Filter>Notifications\Header Files</Filter>
</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">
<Filter>Notifications\Header Files</Filter>
</ClInclude>
Expand Down
3 changes: 3 additions & 0 deletions Foundation/Foundation_vs170.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,7 @@
<ClCompile Include="src\Ascii.cpp" />
<ClCompile Include="src\ASCIIEncoding.cpp" />
<ClCompile Include="src\AsyncChannel.cpp" />
<ClCompile Include="src\AsyncNotificationCenter.cpp" />
<ClCompile Include="src\AtomicCounter.cpp" />
<ClCompile Include="src\Base32Decoder.cpp" />
<ClCompile Include="src\Base32Encoder.cpp" />
Expand Down Expand Up @@ -2109,6 +2110,8 @@
<ClInclude Include="include\Poco\Ascii.h" />
<ClInclude Include="include\Poco\ASCIIEncoding.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\AutoPtr.h" />
<ClInclude Include="include\Poco\AutoReleasePool.h" />
Expand Down
9 changes: 9 additions & 0 deletions Foundation/Foundation_vs170.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,9 @@
<ClCompile Include="src\NotificationCenter.cpp">
<Filter>Notifications\Source Files</Filter>
</ClCompile>
<ClCompile Include="src\AsyncNotificationCenter.cpp">
<Filter>Notifications\Source Files</Filter>
</ClCompile>
<ClCompile Include="src\NotificationQueue.cpp">
<Filter>Notifications\Source Files</Filter>
</ClCompile>
Expand Down Expand Up @@ -1447,6 +1450,12 @@
<ClInclude Include="include\Poco\AbstractObserver.h">
<Filter>Notifications\Header Files</Filter>
</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">
<Filter>Notifications\Header Files</Filter>
</ClInclude>
Expand Down
2 changes: 1 addition & 1 deletion Foundation/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

include $(POCO_BASE)/build/rules/global

objects = ArchiveStrategy Ascii ASCIIEncoding AsyncChannel ActiveThreadPool\
objects = ArchiveStrategy Ascii ASCIIEncoding AsyncChannel AsyncNotificationCenter ActiveThreadPool\
Base32Decoder Base32Encoder Base64Decoder Base64Encoder \
BinaryReader BinaryWriter Bugcheck ByteOrder Channel Checksum Clock Configurable ConsoleChannel \
Condition CountingStream DateTime LocalDateTime DateTimeFormat DateTimeFormatter DateTimeParser \
Expand Down
32 changes: 31 additions & 1 deletion Foundation/include/Poco/AbstractObserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,42 @@ class Foundation_API AbstractObserver
AbstractObserver& operator = (const AbstractObserver& observer);

virtual void notify(Notification* pNf) 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 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 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

Expand Down
60 changes: 41 additions & 19 deletions Foundation/include/Poco/Any.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@
#include <cstddef>


#define poco_any_assert(cond) do { if (!(cond)) std::abort(); } while (0)


namespace Poco {

class Any;

using namespace std::string_literals;

namespace Dynamic {

class Var;
Expand Down Expand Up @@ -409,12 +408,12 @@ ValueType* AnyCast(Any* operand)
/// to the stored value.
///
/// Example Usage:
/// MyType* pTmp = AnyCast<MyType*>(pAny).
/// Will return NULL if the cast fails, i.e. types don't match.
/// MyType* pTmp = AnyCast<MyType>(pAny).
/// Returns nullptr if the types don't match.
{
return operand && operand->type() == typeid(ValueType)
? &static_cast<Any::Holder<ValueType>*>(operand->content())->_held
: 0;
: nullptr;
}


Expand All @@ -424,8 +423,8 @@ const ValueType* AnyCast(const Any* operand)
/// to the stored value.
///
/// Example Usage:
/// const MyType* pTmp = AnyCast<MyType*>(pAny).
/// Will return NULL if the cast fails, i.e. types don't match.
/// const MyType* pTmp = AnyCast<MyType>(pAny).
/// Returns nullptr if the types don't match.
{
return AnyCast<ValueType>(const_cast<Any*>(operand));
}
Expand All @@ -442,18 +441,19 @@ ValueType AnyCast(Any& operand)
/// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in
/// these cases.
{
typedef typename TypeWrapper<ValueType>::TYPE NonRef;
using NonRef = typename TypeWrapper<ValueType>::TYPE;

NonRef* result = AnyCast<NonRef>(&operand);
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())
{
s.append(1, '(');
s.append(operand.content()->type().name());
s.append(Poco::demangle(operand.content()->type().name()));
s.append(" => ");
s.append(typeid(ValueType).name());
s.append(Poco::demangle<ValueType>());
s.append(1, ')');
}
throw BadCastException(s);
Expand All @@ -473,7 +473,7 @@ ValueType AnyCast(const Any& operand)
/// Some compilers will accept this code although a copy is returned. Use the RefAnyCast in
/// these cases.
{
typedef typename TypeWrapper<ValueType>::TYPE NonRef;
using NonRef = typename TypeWrapper<ValueType>::TYPE;

return AnyCast<NonRef&>(const_cast<Any&>(operand));
}
Expand All @@ -489,13 +489,14 @@ const ValueType& RefAnyCast(const Any & operand)
ValueType* result = AnyCast<ValueType>(const_cast<Any*>(&operand));
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())
{
s.append(1, '(');
s.append(operand.content()->type().name());
s.append(Poco::demangle(operand.content()->type().name()));
s.append(" => ");
s.append(typeid(ValueType).name());
s.append(Poco::demangle<ValueType>());
s.append(1, ')');
}
throw BadCastException(s);
Expand All @@ -514,13 +515,14 @@ ValueType& RefAnyCast(Any& operand)
ValueType* result = AnyCast<ValueType>(&operand);
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())
{
s.append(1, '(');
s.append(operand.content()->type().name());
s.append(Poco::demangle(operand.content()->type().name()));
s.append(" => ");
s.append(typeid(ValueType).name());
s.append(Poco::demangle<ValueType>());
s.append(1, ')');
}
throw BadCastException(s);
Expand Down Expand Up @@ -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


Expand Down
77 changes: 77 additions & 0 deletions Foundation/include/Poco/AsyncNotificationCenter.h
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 88be669

Please sign in to comment.