2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/public-api/signals/base-signal.h>
22 #include <algorithm> // remove_if
25 #include <dali/integration-api/debug.h>
29 const int32_t INVALID_CALLBACK_INDEX = -1;
31 } // unnamed namespace
35 BaseSignal::BaseSignal()
36 : mEmittingFlag(false)
40 BaseSignal::~BaseSignal()
42 // We can't assert in a destructor
45 DALI_LOG_ERROR("Invalid destruction of Signal during Emit()\n");
47 // Set the signal deletion flag as well if set
50 *mSignalDeleted = true;
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++)
59 auto& connection = mSignalConnections[i];
61 // Note that values are set to NULL in DeleteConnection
64 connection.Disconnect(this);
69 void BaseSignal::OnConnect(CallbackBase* callback)
71 DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Connect()");
73 int32_t index = FindCallback(callback);
75 // Don't double-connect the same callback
76 if(INVALID_CALLBACK_INDEX == index)
78 // create a new signal connection object, to allow the signal to track the connection.
79 //SignalConnection* connection = new SignalConnection(callback);
81 mSignalConnections.push_back(SignalConnection(callback));
90 void BaseSignal::OnDisconnect(CallbackBase* callback)
92 DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Disconnect()");
94 int32_t index = FindCallback(callback);
96 if(index > INVALID_CALLBACK_INDEX)
98 DeleteConnection(index);
101 // call back is a temporary created to find which slot should be disconnected.
105 void BaseSignal::OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback)
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()");
110 int32_t index = FindCallback(callback);
112 // Don't double-connect the same callback
113 if(INVALID_CALLBACK_INDEX == index)
115 // create a new signal connection object, to allow the signal to track the connection.
116 //SignalConnection* connection = new SignalConnection(tracker, callback);
118 mSignalConnections.push_back({tracker, callback});
120 // Let the connection tracker know that a connection between a signal and a slot has been made.
121 tracker->SignalConnected(this, callback);
130 void BaseSignal::OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback)
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()");
135 int32_t index = FindCallback(callback);
137 if(index > INVALID_CALLBACK_INDEX)
139 // temporary pointer to disconnected callback
140 CallbackBase* disconnectedCallback = mSignalConnections[index].GetCallback();
142 // close the signal side connection first.
143 DeleteConnection(index);
145 // close the slot side connection
146 tracker->SignalDisconnected(this, disconnectedCallback);
149 // call back is a temporary created to find which slot should be disconnected.
153 // for SlotObserver::SlotDisconnected
154 void BaseSignal::SlotDisconnected(CallbackBase* callback)
156 const std::size_t count(mSignalConnections.size());
157 for(std::size_t i = 0; i < count; ++i)
159 const CallbackBase* connectionCallback = GetCallback(i);
161 // Pointer comparison i.e. SignalConnection contains pointer to same callback instance
162 if(connectionCallback &&
163 connectionCallback == callback)
167 // Disconnection complete
172 DALI_ABORT("Callback lost in SlotDisconnected()");
175 int32_t BaseSignal::FindCallback(CallbackBase* callback) const noexcept
177 int32_t index(INVALID_CALLBACK_INDEX);
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)
184 const CallbackBase* connectionCallback = GetCallback(i);
186 // Note that values are set to NULL in DeleteConnection
187 if(connectionCallback && (*connectionCallback == *callback))
189 index = static_cast<int>(i); // only 2,147,483,647 connections supported, no error check
197 void BaseSignal::DeleteConnection(std::size_t connectionIndex)
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};
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);
215 void BaseSignal::CleanupConnections()
217 if(!mSignalConnections.empty())
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());
225 mNullConnections = 0;
228 // BaseSignal::EmitGuard
230 BaseSignal::EmitGuard::EmitGuard(bool& flag)
240 // mFlag is NULL when Emit() is called during Emit()
241 DALI_LOG_ERROR("Cannot call Emit() from inside Emit()\n");
245 BaseSignal::EmitGuard::~EmitGuard()
253 bool BaseSignal::EmitGuard::ErrorOccurred()
255 // mFlag is NULL when Emit() is called during Emit()
256 return (nullptr == mFlag);