4764ed79dca9f9c39065d40057f97a7ff38669b4
[platform/core/uifw/dali-core.git] / dali / internal / event / common / notification-manager.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/common/notification-manager.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/devel-api/common/owner-container.h>
23 #include <dali/devel-api/threading/mutex.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/integration-api/trace.h>
26 #include <dali/internal/common/message.h>
27 #include <dali/internal/event/common/complete-notification-interface.h>
28 #include <dali/internal/event/common/property-notification-impl.h>
29 #include <dali/public-api/common/dali-common.h>
30
31 namespace Dali
32 {
33 namespace Internal
34 {
35 namespace
36 {
37 typedef Dali::Vector<CompleteNotificationInterface*> InterfaceContainer;
38
39 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
40
41 /**
42  * helper to move elements from one container to another
43  * @param from where to move
44  * @param to move target
45  */
46 void MoveElements(InterfaceContainer& from, InterfaceContainer& to)
47 {
48   // check if there's something in from
49   const InterfaceContainer::SizeType fromCount = from.Count();
50   if(fromCount > 0u)
51   {
52     // check if to has some elements
53     const InterfaceContainer::SizeType toCount = to.Count();
54     if(toCount == 0u)
55     {
56       // to is empty so we can swap with from
57       to.Swap(from);
58     }
59     else
60     {
61       to.Reserve(toCount + fromCount);
62       for(InterfaceContainer::SizeType i = 0; i < fromCount; ++i)
63       {
64         to.PushBack(from[i]);
65       }
66       from.Clear();
67     }
68   }
69 }
70 } // namespace
71
72 using MessageQueueMutex = Dali::Mutex;
73 using MessageContainer  = OwnerContainer<MessageBase*>;
74
75 struct NotificationManager::Impl
76 {
77   Impl()
78   {
79     // reserve space on the vectors to avoid reallocs
80     // applications typically have up-to 20-30 notifications at startup
81     updateCompletedMessageQueue.Reserve(32);
82     updateWorkingMessageQueue.Reserve(32);
83     eventMessageQueue.Reserve(32);
84
85     // only a few manager objects get complete notifications (animation, render list, property notifications, ...)
86     updateCompletedInterfaceQueue.Reserve(4);
87     updateWorkingInterfaceQueue.Reserve(4);
88     eventInterfaceQueue.Reserve(4);
89   }
90
91   ~Impl() = default;
92
93   // queueMutex must be locked whilst accessing queue
94   MessageQueueMutex queueMutex;
95   // three queues for objects owned by notification manager
96   MessageContainer updateCompletedMessageQueue;
97   MessageContainer updateWorkingMessageQueue;
98   MessageContainer eventMessageQueue;
99   // three queues for objects referenced by notification manager
100   InterfaceContainer updateCompletedInterfaceQueue;
101   InterfaceContainer updateWorkingInterfaceQueue;
102   InterfaceContainer eventInterfaceQueue;
103 };
104
105 NotificationManager::NotificationManager()
106 {
107   mImpl = new Impl();
108 }
109
110 NotificationManager::~NotificationManager()
111 {
112   delete mImpl;
113 }
114
115 void NotificationManager::QueueCompleteNotification(CompleteNotificationInterface* instance)
116 {
117   // queueMutex must be locked whilst accessing queues
118   MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
119
120   mImpl->updateWorkingInterfaceQueue.PushBack(instance);
121 }
122
123 void NotificationManager::QueueMessage(MessageBase* message)
124 {
125   DALI_ASSERT_DEBUG(NULL != message);
126
127   // queueMutex must be locked whilst accessing queues
128   MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
129
130   mImpl->updateWorkingMessageQueue.PushBack(message);
131 }
132
133 void NotificationManager::UpdateCompleted()
134 {
135   // queueMutex must be locked whilst accessing queues
136   MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
137   // Move messages from update working queue to completed queue
138   // note that in theory its possible for update completed to have last frames
139   // events as well still hanging around. we need to keep them as well
140   mImpl->updateCompletedMessageQueue.MoveFrom(mImpl->updateWorkingMessageQueue);
141   // move pointers from interface queue
142   MoveElements(mImpl->updateWorkingInterfaceQueue, mImpl->updateCompletedInterfaceQueue);
143   // finally the lock is released
144 }
145
146 bool NotificationManager::MessagesToProcess()
147 {
148   // queueMutex must be locked whilst accessing queues
149   MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
150
151   return (0u < mImpl->updateCompletedMessageQueue.Count() ||
152           (0u < mImpl->updateCompletedInterfaceQueue.Count()));
153 }
154
155 void NotificationManager::ProcessMessages()
156 {
157   // queueMutex must be locked whilst accessing queues
158   {
159     MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
160
161     // Move messages from update completed queue to event queue
162     // note that in theory its possible for event queue to have
163     // last frames events as well still hanging around so need to keep them
164     mImpl->eventMessageQueue.MoveFrom(mImpl->updateCompletedMessageQueue);
165     MoveElements(mImpl->updateCompletedInterfaceQueue, mImpl->eventInterfaceQueue);
166   }
167   // end of scope, lock is released
168
169   MessageContainer::Iterator       iter = mImpl->eventMessageQueue.Begin();
170   const MessageContainer::Iterator end  = mImpl->eventMessageQueue.End();
171   if(iter != end)
172   {
173     DALI_TRACE_BEGIN(gTraceFilter, "DALI_PROCESS_NOTIFICATION_MESSAGE");
174     for(; iter != end; ++iter)
175     {
176       (*iter)->Process(0u /*ignored*/);
177     }
178     DALI_TRACE_END(gTraceFilter, "DALI_PROCESS_NOTIFICATION_MESSAGE");
179   }
180   // release the processed messages from event side queue
181   mImpl->eventMessageQueue.Clear();
182
183   InterfaceContainer::Iterator       iter2 = mImpl->eventInterfaceQueue.Begin();
184   const InterfaceContainer::Iterator end2  = mImpl->eventInterfaceQueue.End();
185   for(; iter2 != end2; ++iter2)
186   {
187     CompleteNotificationInterface* interface = *iter2;
188     if(interface)
189     {
190       interface->NotifyCompleted();
191     }
192   }
193   // just clear the container, we dont own the objects
194   mImpl->eventInterfaceQueue.Clear();
195 }
196
197 } // namespace Internal
198
199 } // namespace Dali