Add template keyword for android compiler
[platform/core/uifw/dali-core.git] / dali / public-api / signals / base-signal.cpp
1 /*
2  * Copyright (c) 2020 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/public-api/signals/base-signal.h>
20
21 // EXTERNAL INCLUDES
22 #include <algorithm> // remove_if
23
24 // INTERNAL INCLUDES
25 #include <dali/integration-api/debug.h>
26
27 namespace
28 {
29 const int32_t INVALID_CALLBACK_INDEX = -1;
30
31 } // unnamed namespace
32
33 namespace Dali
34 {
35 BaseSignal::BaseSignal()
36 : mEmittingFlag(false)
37 {
38 }
39
40 BaseSignal::~BaseSignal()
41 {
42   // We can't assert in a destructor
43   if(mEmittingFlag)
44   {
45     DALI_LOG_ERROR("Invalid destruction of Signal during Emit()\n");
46   }
47
48   // The signal is being destroyed. We have to inform any slots
49   // that are connected, that the signal is dead.
50   const std::size_t count(mSignalConnections.size());
51   for(std::size_t i = 0; i < count; i++)
52   {
53     auto& connection = mSignalConnections[i];
54
55     // Note that values are set to NULL in DeleteConnection
56     if(connection)
57     {
58       connection.Disconnect(this);
59     }
60   }
61 }
62
63 void BaseSignal::OnConnect(CallbackBase* callback)
64 {
65   DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Connect()");
66
67   int32_t index = FindCallback(callback);
68
69   // Don't double-connect the same callback
70   if(INVALID_CALLBACK_INDEX == index)
71   {
72     // create a new signal connection object, to allow the signal to track the connection.
73     //SignalConnection* connection = new SignalConnection(callback);
74
75     mSignalConnections.push_back(SignalConnection(callback));
76   }
77   else
78   {
79     // clean-up required
80     delete callback;
81   }
82 }
83
84 void BaseSignal::OnDisconnect(CallbackBase* callback)
85 {
86   DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Disconnect()");
87
88   int32_t index = FindCallback(callback);
89
90   if(index > INVALID_CALLBACK_INDEX)
91   {
92     DeleteConnection(index);
93   }
94
95   // call back is a temporary created to find which slot should be disconnected.
96   delete callback;
97 }
98
99 void BaseSignal::OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback)
100 {
101   DALI_ASSERT_ALWAYS(nullptr != tracker && "Invalid ConnectionTrackerInterface pointer passed to Connect()");
102   DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Connect()");
103
104   int32_t index = FindCallback(callback);
105
106   // Don't double-connect the same callback
107   if(INVALID_CALLBACK_INDEX == index)
108   {
109     // create a new signal connection object, to allow the signal to track the connection.
110     //SignalConnection* connection = new SignalConnection(tracker, callback);
111
112     mSignalConnections.push_back({tracker, callback});
113
114     // Let the connection tracker know that a connection between a signal and a slot has been made.
115     tracker->SignalConnected(this, callback);
116   }
117   else
118   {
119     // clean-up required
120     delete callback;
121   }
122 }
123
124 void BaseSignal::OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback)
125 {
126   DALI_ASSERT_ALWAYS(nullptr != tracker && "Invalid ConnectionTrackerInterface pointer passed to Disconnect()");
127   DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Disconnect()");
128
129   int32_t index = FindCallback(callback);
130
131   if(index > INVALID_CALLBACK_INDEX)
132   {
133     // temporary pointer to disconnected callback
134     CallbackBase* disconnectedCallback = mSignalConnections[index].GetCallback();
135
136     // close the signal side connection first.
137     DeleteConnection(index);
138
139     // close the slot side connection
140     tracker->SignalDisconnected(this, disconnectedCallback);
141   }
142
143   // call back is a temporary created to find which slot should be disconnected.
144   delete callback;
145 }
146
147 // for SlotObserver::SlotDisconnected
148 void BaseSignal::SlotDisconnected(CallbackBase* callback)
149 {
150   const std::size_t count(mSignalConnections.size());
151   for(std::size_t i = 0; i < count; ++i)
152   {
153     const CallbackBase* connectionCallback = GetCallback(i);
154
155     // Pointer comparison i.e. SignalConnection contains pointer to same callback instance
156     if(connectionCallback &&
157        connectionCallback == callback)
158     {
159       DeleteConnection(i);
160
161       // Disconnection complete
162       return;
163     }
164   }
165
166   DALI_ABORT("Callback lost in SlotDisconnected()");
167 }
168
169
170 int32_t BaseSignal::FindCallback(CallbackBase* callback) const noexcept
171 {
172   int32_t index(INVALID_CALLBACK_INDEX);
173
174   // A signal can have multiple slots connected to it.
175   // We need to search for the slot which has the same call back function (if it's static)
176   // Or the same object / member function (for non-static)
177   for(auto i = 0u; i < mSignalConnections.size(); ++i)
178   {
179     const CallbackBase* connectionCallback = GetCallback(i);
180
181     // Note that values are set to NULL in DeleteConnection
182     if(connectionCallback && (*connectionCallback == *callback))
183     {
184       index = static_cast<int>(i); // only 2,147,483,647 connections supported, no error check
185       break;
186     }
187   }
188
189   return index;
190 }
191
192 void BaseSignal::DeleteConnection(std::size_t connectionIndex)
193 {
194
195   if(mEmittingFlag)
196   {
197     // IMPORTANT - do not remove from items from mSignalConnections, reset instead.
198     // Signal Emit() methods require that connection count is not reduced while iterating
199     // i.e. DeleteConnection can be called from within callbacks, while iterating through mSignalConnections.
200     mSignalConnections[connectionIndex] = {nullptr};
201     ++mNullConnections;
202   }
203   else
204   {
205     // If application connects and disconnects without the signal never emitting,
206     // the mSignalConnections vector keeps growing and growing as CleanupConnections() is done from Emit.
207     mSignalConnections.erase(mSignalConnections.begin() + connectionIndex);
208   }
209 }
210
211 void BaseSignal::CleanupConnections()
212 {
213   if(!mSignalConnections.empty())
214   {
215     //Remove Signals that are already markeed nullptr.
216     mSignalConnections.erase(std::remove_if(mSignalConnections.begin(),
217                                             mSignalConnections.end(),
218                                             [](auto& elm) { return (elm) ? false : true; }),
219                              mSignalConnections.end());
220   }
221   mNullConnections = 0;
222 }
223
224 // BaseSignal::EmitGuard
225
226 BaseSignal::EmitGuard::EmitGuard(bool& flag)
227 : mFlag(nullptr)
228 {
229   if(!flag)
230   {
231     mFlag = &flag;
232     flag  = true;
233   }
234   else
235   {
236     // mFlag is NULL when Emit() is called during Emit()
237     DALI_LOG_ERROR("Cannot call Emit() from inside Emit()\n");
238   }
239 }
240
241 BaseSignal::EmitGuard::~EmitGuard()
242 {
243   if(mFlag)
244   {
245     *mFlag = false;
246   }
247 }
248
249 bool BaseSignal::EmitGuard::ErrorOccurred()
250 {
251   // mFlag is NULL when Emit() is called during Emit()
252   return (nullptr == mFlag);
253 }
254
255 } // namespace Dali