// CLASS HEADER
#include <dali/internal/event/common/notification-manager.h>
-// EXTERNAL INCLUDES
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wall"
-
-#include <boost/thread/mutex.hpp>
-
-#pragma clang diagnostic pop
-#else
-
-#include <boost/thread/mutex.hpp>
-
-#endif // __clang__
-
// INTERNAL INCLUDES
-#include <dali/public-api/common/dali-common.h>
+#include <dali/devel-api/common/owner-container.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/integration-api/debug.h>
+#include <dali/integration-api/trace.h>
+#include <dali/internal/common/message.h>
+#include <dali/internal/event/common/complete-notification-interface.h>
#include <dali/internal/event/common/property-notification-impl.h>
-#include <dali/internal/common/message-container.h>
-
-using namespace std;
+#include <dali/public-api/common/dali-common.h>
namespace Dali
{
-
namespace Internal
{
+namespace
+{
+typedef Dali::Vector<CompleteNotificationInterface*> InterfaceContainer;
-typedef boost::mutex MessageQueueMutex;
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
-struct NotificationManager::Impl
+/**
+ * helper to move elements from one container to another
+ * @param from where to move
+ * @param to move target
+ */
+void MoveElements(InterfaceContainer& from, InterfaceContainer& to)
{
- Impl()
- : notificationCount(0)
+ // check if there's something in from
+ const InterfaceContainer::SizeType fromCount = from.Count();
+ if(fromCount > 0u)
{
+ // check if to has some elements
+ const InterfaceContainer::SizeType toCount = to.Count();
+ if(toCount == 0u)
+ {
+ // to is empty so we can swap with from
+ to.Swap(from);
+ }
+ else
+ {
+ to.Reserve(toCount + fromCount);
+ for(InterfaceContainer::SizeType i = 0; i < fromCount; ++i)
+ {
+ to.PushBack(from[i]);
+ }
+ from.Clear();
+ }
}
+}
+} // namespace
- ~Impl()
+using MessageQueueMutex = Dali::Mutex;
+using MessageContainer = OwnerContainer<MessageBase*>;
+
+struct NotificationManager::Impl
+{
+ Impl()
{
+ // reserve space on the vectors to avoid reallocs
+ // applications typically have up-to 20-30 notifications at startup
+ updateCompletedMessageQueue.Reserve(32);
+ updateWorkingMessageQueue.Reserve(32);
+ eventMessageQueue.Reserve(32);
+
+ // only a few manager objects get complete notifications (animation, render list, property notifications, ...)
+ updateCompletedInterfaceQueue.Reserve(4);
+ updateWorkingInterfaceQueue.Reserve(4);
+ eventInterfaceQueue.Reserve(4);
}
- // Used to skip duplicate operations during Notify()
- unsigned int notificationCount;
+ ~Impl() = default;
// queueMutex must be locked whilst accessing queue
MessageQueueMutex queueMutex;
- MessageContainer updateCompletedQueue;
- MessageContainer updateWorkingQueue;
- MessageContainer eventQueue;
+ // three queues for objects owned by notification manager
+ MessageContainer updateCompletedMessageQueue;
+ MessageContainer updateWorkingMessageQueue;
+ MessageContainer eventMessageQueue;
+ // three queues for objects referenced by notification manager
+ InterfaceContainer updateCompletedInterfaceQueue;
+ InterfaceContainer updateWorkingInterfaceQueue;
+ InterfaceContainer eventInterfaceQueue;
};
NotificationManager::NotificationManager()
delete mImpl;
}
-void NotificationManager::QueueMessage( MessageBase* message )
+void NotificationManager::QueueCompleteNotification(CompleteNotificationInterface* instance)
{
- DALI_ASSERT_DEBUG( NULL != message );
+ // queueMutex must be locked whilst accessing queues
+ MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
- // queueMutex must be locked whilst accessing queue
- MessageQueueMutex::scoped_lock lock( mImpl->queueMutex );
+ mImpl->updateWorkingInterfaceQueue.PushBack(instance);
+}
- mImpl->updateWorkingQueue.PushBack( message );
+void NotificationManager::QueueMessage(MessageBase* message)
+{
+ DALI_ASSERT_DEBUG(NULL != message);
+
+ // queueMutex must be locked whilst accessing queues
+ MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
+
+ mImpl->updateWorkingMessageQueue.PushBack(message);
}
void NotificationManager::UpdateCompleted()
{
- // queueMutex must be locked whilst accessing queue
- MessageQueueMutex::scoped_lock lock( mImpl->queueMutex );
- // Swap the queue, original queue ends up empty, then release the lock
- mImpl->updateCompletedQueue.Swap( mImpl->updateWorkingQueue );
+ // queueMutex must be locked whilst accessing queues
+ MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
+ // Move messages from update working queue to completed queue
+ // note that in theory its possible for update completed to have last frames
+ // events as well still hanging around. we need to keep them as well
+ mImpl->updateCompletedMessageQueue.MoveFrom(mImpl->updateWorkingMessageQueue);
+ // move pointers from interface queue
+ MoveElements(mImpl->updateWorkingInterfaceQueue, mImpl->updateCompletedInterfaceQueue);
+ // finally the lock is released
}
bool NotificationManager::MessagesToProcess()
{
- // queueMutex must be locked whilst accessing queue
- MessageQueueMutex::scoped_lock lock( mImpl->queueMutex );
+ // queueMutex must be locked whilst accessing queues
+ MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
- return ( false == mImpl->updateCompletedQueue.IsEmpty() );
+ return (0u < mImpl->updateCompletedMessageQueue.Count() ||
+ (0u < mImpl->updateCompletedInterfaceQueue.Count()));
}
void NotificationManager::ProcessMessages()
{
- // Done before messages are processed, for notification count comparisons
- ++mImpl->notificationCount;
-
- // queueMutex must be locked whilst accessing queue
+ // queueMutex must be locked whilst accessing queues
{
- MessageQueueMutex::scoped_lock lock( mImpl->queueMutex );
+ MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
- // Swap the queue, original queue ends up empty, then release the lock
- mImpl->updateCompletedQueue.Swap( mImpl->eventQueue );
+ // Move messages from update completed queue to event queue
+ // note that in theory its possible for event queue to have
+ // last frames events as well still hanging around so need to keep them
+ mImpl->eventMessageQueue.MoveFrom(mImpl->updateCompletedMessageQueue);
+ MoveElements(mImpl->updateCompletedInterfaceQueue, mImpl->eventInterfaceQueue);
}
// end of scope, lock is released
- MessageContainer::Iterator iter = mImpl->eventQueue.Begin();
- MessageContainer::Iterator end = mImpl->eventQueue.End();
- for( ; iter != end; ++iter )
+ MessageContainer::Iterator iter = mImpl->eventMessageQueue.Begin();
+ const MessageContainer::Iterator end = mImpl->eventMessageQueue.End();
+ if(iter != end)
{
- (*iter)->Process( 0u/*ignored*/ );
+ DALI_TRACE_BEGIN(gTraceFilter, "DALI_NOTIFICATION_PROCESS_MESSAGE");
+ for(; iter != end; ++iter)
+ {
+ (*iter)->Process(0u /*ignored*/);
+ }
+ DALI_TRACE_END(gTraceFilter, "DALI_NOTIFICATION_PROCESS_MESSAGE");
}
-
// release the processed messages from event side queue
- mImpl->eventQueue.Clear();
-}
+ mImpl->eventMessageQueue.Clear();
-unsigned int NotificationManager::GetNotificationCount() const
-{
- return mImpl->notificationCount;
+ InterfaceContainer::Iterator iter2 = mImpl->eventInterfaceQueue.Begin();
+ const InterfaceContainer::Iterator end2 = mImpl->eventInterfaceQueue.End();
+ if(iter2 != end2)
+ {
+ DALI_TRACE_BEGIN(gTraceFilter, "DALI_NOTIFICATION_NOTIFY_COMPLETED");
+ for(; iter2 != end2; ++iter2)
+ {
+ CompleteNotificationInterface* interface = *iter2;
+ if(interface)
+ {
+ interface->NotifyCompleted();
+ }
+ }
+ DALI_TRACE_END(gTraceFilter, "DALI_NOTIFICATION_NOTIFY_COMPLETED");
+ }
+ // just clear the container, we dont own the objects
+ mImpl->eventInterfaceQueue.Clear();
}
} // namespace Internal