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