Updated all code to new format
[platform/core/uifw/dali-core.git] / dali / public-api / signals / base-signal.cpp
1 /*
2  * Copyright (c) 2021 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 int32_t BaseSignal::FindCallback(CallbackBase* callback) const noexcept
170 {
171   int32_t index(INVALID_CALLBACK_INDEX);
172
173   // A signal can have multiple slots connected to it.
174   // We need to search for the slot which has the same call back function (if it's static)
175   // Or the same object / member function (for non-static)
176   for(auto i = 0u; i < mSignalConnections.size(); ++i)
177   {
178     const CallbackBase* connectionCallback = GetCallback(i);
179
180     // Note that values are set to NULL in DeleteConnection
181     if(connectionCallback && (*connectionCallback == *callback))
182     {
183       index = static_cast<int>(i); // only 2,147,483,647 connections supported, no error check
184       break;
185     }
186   }
187
188   return index;
189 }
190
191 void BaseSignal::DeleteConnection(std::size_t connectionIndex)
192 {
193   if(mEmittingFlag)
194   {
195     // IMPORTANT - do not remove from items from mSignalConnections, reset instead.
196     // Signal Emit() methods require that connection count is not reduced while iterating
197     // i.e. DeleteConnection can be called from within callbacks, while iterating through mSignalConnections.
198     mSignalConnections[connectionIndex] = {nullptr};
199     ++mNullConnections;
200   }
201   else
202   {
203     // If application connects and disconnects without the signal never emitting,
204     // the mSignalConnections vector keeps growing and growing as CleanupConnections() is done from Emit.
205     mSignalConnections.erase(mSignalConnections.begin() + connectionIndex);
206   }
207 }
208
209 void BaseSignal::CleanupConnections()
210 {
211   if(!mSignalConnections.empty())
212   {
213     //Remove Signals that are already markeed nullptr.
214     mSignalConnections.erase(std::remove_if(mSignalConnections.begin(),
215                                             mSignalConnections.end(),
216                                             [](auto& elm) { return (elm) ? false : true; }),
217                              mSignalConnections.end());
218   }
219   mNullConnections = 0;
220 }
221
222 // BaseSignal::EmitGuard
223
224 BaseSignal::EmitGuard::EmitGuard(bool& flag)
225 : mFlag(nullptr)
226 {
227   if(!flag)
228   {
229     mFlag = &flag;
230     flag  = true;
231   }
232   else
233   {
234     // mFlag is NULL when Emit() is called during Emit()
235     DALI_LOG_ERROR("Cannot call Emit() from inside Emit()\n");
236   }
237 }
238
239 BaseSignal::EmitGuard::~EmitGuard()
240 {
241   if(mFlag)
242   {
243     *mFlag = false;
244   }
245 }
246
247 bool BaseSignal::EmitGuard::ErrorOccurred()
248 {
249   // mFlag is NULL when Emit() is called during Emit()
250   return (nullptr == mFlag);
251 }
252
253 } // namespace Dali