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");
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++)
53 auto& connection = mSignalConnections[i];
55 // Note that values are set to NULL in DeleteConnection
58 connection.Disconnect(this);
63 void BaseSignal::OnConnect(CallbackBase* callback)
65 DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Connect()");
67 int32_t index = FindCallback(callback);
69 // Don't double-connect the same callback
70 if(INVALID_CALLBACK_INDEX == index)
72 // create a new signal connection object, to allow the signal to track the connection.
73 //SignalConnection* connection = new SignalConnection(callback);
75 mSignalConnections.push_back(SignalConnection(callback));
84 void BaseSignal::OnDisconnect(CallbackBase* callback)
86 DALI_ASSERT_ALWAYS(nullptr != callback && "Invalid member function pointer passed to Disconnect()");
88 int32_t index = FindCallback(callback);
90 if(index > INVALID_CALLBACK_INDEX)
92 DeleteConnection(index);
95 // call back is a temporary created to find which slot should be disconnected.
99 void BaseSignal::OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback)
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()");
104 int32_t index = FindCallback(callback);
106 // Don't double-connect the same callback
107 if(INVALID_CALLBACK_INDEX == index)
109 // create a new signal connection object, to allow the signal to track the connection.
110 //SignalConnection* connection = new SignalConnection(tracker, callback);
112 mSignalConnections.push_back({tracker, callback});
114 // Let the connection tracker know that a connection between a signal and a slot has been made.
115 tracker->SignalConnected(this, callback);
124 void BaseSignal::OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback)
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()");
129 int32_t index = FindCallback(callback);
131 if(index > INVALID_CALLBACK_INDEX)
133 // temporary pointer to disconnected callback
134 CallbackBase* disconnectedCallback = mSignalConnections[index].GetCallback();
136 // close the signal side connection first.
137 DeleteConnection(index);
139 // close the slot side connection
140 tracker->SignalDisconnected(this, disconnectedCallback);
143 // call back is a temporary created to find which slot should be disconnected.
147 // for SlotObserver::SlotDisconnected
148 void BaseSignal::SlotDisconnected(CallbackBase* callback)
150 const std::size_t count(mSignalConnections.size());
151 for(std::size_t i = 0; i < count; ++i)
153 const CallbackBase* connectionCallback = GetCallback(i);
155 // Pointer comparison i.e. SignalConnection contains pointer to same callback instance
156 if(connectionCallback &&
157 connectionCallback == callback)
161 // Disconnection complete
166 DALI_ABORT("Callback lost in SlotDisconnected()");
169 int32_t BaseSignal::FindCallback(CallbackBase* callback) const noexcept
171 int32_t index(INVALID_CALLBACK_INDEX);
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)
178 const CallbackBase* connectionCallback = GetCallback(i);
180 // Note that values are set to NULL in DeleteConnection
181 if(connectionCallback && (*connectionCallback == *callback))
183 index = static_cast<int>(i); // only 2,147,483,647 connections supported, no error check
191 void BaseSignal::DeleteConnection(std::size_t connectionIndex)
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};
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);
209 void BaseSignal::CleanupConnections()
211 if(!mSignalConnections.empty())
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());
219 mNullConnections = 0;
222 // BaseSignal::EmitGuard
224 BaseSignal::EmitGuard::EmitGuard(bool& flag)
234 // mFlag is NULL when Emit() is called during Emit()
235 DALI_LOG_ERROR("Cannot call Emit() from inside Emit()\n");
239 BaseSignal::EmitGuard::~EmitGuard()
247 bool BaseSignal::EmitGuard::ErrorOccurred()
249 // mFlag is NULL when Emit() is called during Emit()
250 return (nullptr == mFlag);