base-signal connect and disconnect in O(1)
[platform/core/uifw/dali-core.git] / dali / public-api / signals / base-signal.h
index ddfe915..5420952 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_BASE_SIGNAL_H
 
 /*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  */
 
+// EXTERNAL INCLUDES
+#include <stdint.h> // for uint32_t
+
 // INTERNAL INCLUDES
 #include <dali/public-api/common/dali-common.h>
-#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/common/list-wrapper.h>
 #include <dali/public-api/signals/callback.h>
 #include <dali/public-api/signals/connection-tracker-interface.h>
 #include <dali/public-api/signals/signal-slot-connections.h>
@@ -96,7 +99,10 @@ public:
    * @SINCE_1_0.0
    * @return True if there are any slots connected to the signal.
    */
-  bool Empty() const;
+  bool Empty() const
+  {
+    return (0 == GetConnectionCount());
+  }
 
   /**
    * @brief Queries the number of slots.
@@ -104,7 +110,10 @@ public:
    * @SINCE_1_0.0
    * @return The number of slots connected to this signal
    */
-  std::size_t GetConnectionCount() const;
+  std::size_t GetConnectionCount() const
+  {
+    return mSignalConnections.size() - mNullConnections;
+  }
 
   // Templated Emit functions for the Signal implementations
 
@@ -141,22 +150,15 @@ public:
   };
 
   /**
-   * @brief Emits a signal with no parameters.
-   *
-   * @SINCE_1_0.0
-   * @pre Cannot be called from inside the same Signal's Emit methods.
-   */
-  void Emit();
-
-  /**
-   * @brief Emits a signal with no parameters.
+   * @brief Emits a signal with parameter pack.
    *
-   * @SINCE_1_0.0
+   * @SINCE_1_9.33
+   * @param[in] args The parameter pack
    * @return The value returned by the last callback
    * @pre Cannot be called from inside the same Signal's Emit methods.
    */
-  template<typename Ret>
-  Ret EmitReturn()
+  template<typename Ret, typename... Args>
+  Ret EmitReturn(Args... args)
   {
     Ret returnVal = Ret();
 
@@ -167,117 +169,43 @@ public:
       return returnVal;
     }
 
-    // If more connections are added by callbacks, these are ignore until the next Emit()
-    // Note that count cannot be reduced while iterating
-    const std::size_t initialCount(mSignalConnections.Count());
-
-    for(std::size_t i = 0; i < initialCount; ++i)
-    {
-      CallbackBase* callback(GetCallback(i));
-
-      // Note that connections will be set to NULL when disconnected
-      // This is preferable to reducing the connection count while iterating
-      if(callback)
-      {
-        returnVal = CallbackBase::ExecuteReturn<Ret>(*callback);
-      }
-    }
-
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-
-    return returnVal;
-  }
-
-  /**
-   * @brief Emits a signal with 1 parameter.
-   *
-   * @SINCE_1_0.0
-   * @param[in] arg0 The first parameter
-   * @pre Cannot be called from inside the same Signal's Emit methods.
-   */
-  template<typename Arg0>
-  void Emit(Arg0 arg0)
-  {
-    // Guards against nested Emit() calls
-    EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
-    if(guard.ErrorOccurred())
-    {
-      return;
-    }
+    // Guards against calling CleanupConnections if the signal is deleted during emission
+    bool signalDeleted{false};
+    mSignalDeleted = &signalDeleted;
 
     // If more connections are added by callbacks, these are ignore until the next Emit()
     // Note that count cannot be reduced while iterating
-    const std::size_t initialCount(mSignalConnections.Count());
-
-    for(std::size_t i = 0; i < initialCount; ++i)
+    auto count = mSignalConnections.size();
+    auto iter  = mSignalConnections.begin();
+    while(count--)
     {
-      CallbackBase* callback(GetCallback(i));
-
-      // Note that connections will be set to NULL when disconnected
-      // This is preferable to reducing the connection count while iterating
+      CallbackBase* callback((*iter) ? iter->GetCallback() : nullptr);
+      ++iter;
       if(callback)
       {
-        CallbackBase::Execute<Arg0>(*callback, arg0);
+        returnVal = CallbackBase::ExecuteReturn<Ret, Args...>(*callback, args...);
       }
     }
 
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-  }
-
-  /**
-   * @brief Emits a signal with 1 parameter.
-   *
-   * @SINCE_1_0.0
-   * @param[in] arg0 The first parameter
-   * @return The value returned by the last callback
-   * @pre Cannot be called from inside the same Signal's Emit methods.
-   */
-  template<typename Ret, typename Arg0>
-  Ret EmitReturn(Arg0 arg0)
-  {
-    Ret returnVal = Ret();
-
-    // Guards against nested Emit() calls
-    EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
-    if(guard.ErrorOccurred())
-    {
-      return returnVal;
-    }
-
-    // If more connections are added by callbacks, these are ignore until the next Emit()
-    // Note that count cannot be reduced while iterating
-    const std::size_t initialCount(mSignalConnections.Count());
-
-    for(std::size_t i = 0; i < initialCount; ++i)
+    if(!signalDeleted)
     {
-      CallbackBase* callback(GetCallback(i));
-
-      // Note that connections will be set to NULL when disconnected
-      // This is preferable to reducing the connection count while iterating
-      if(callback)
-      {
-        returnVal = CallbackBase::ExecuteReturn<Ret, Arg0>(*callback, arg0);
-      }
+      // Cleanup NULL values from Connection container
+      CleanupConnections();
+      mSignalDeleted = nullptr;
     }
 
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-
     return returnVal;
   }
 
   /**
-   * @brief Emits a signal with 2 parameters.
+   * @brief Emits a signal with  parameter pack.
    *
-   * @SINCE_1_0.0
-   * @param[in] arg0 The first parameter
-   * @param[in] arg1 The second parameter
+   * @SINCE_1_9.33
+   * @param[in] args The parameter pack
    * @pre Cannot be called from inside the same Signal's Emit methods.
    */
-  template<typename Arg0, typename Arg1>
-  void Emit(Arg0 arg0, Arg1 arg1)
+  template<typename... Args>
+  void Emit(Args... args)
   {
     // Guards against nested Emit() calls
     EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
@@ -286,150 +214,30 @@ public:
       return;
     }
 
-    // If more connections are added by callbacks, these are ignore until the next Emit()
-    // Note that count cannot be reduced while iterating
-    const std::size_t initialCount(mSignalConnections.Count());
-
-    for(std::size_t i = 0; i < initialCount; ++i)
-    {
-      CallbackBase* callback(GetCallback(i));
-
-      // Note that connections will be set to NULL when disconnected
-      // This is preferable to reducing the connection count while iterating
-      if(callback)
-      {
-        CallbackBase::Execute<Arg0, Arg1>(*callback, arg0, arg1);
-      }
-    }
-
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-  }
-
-  /**
-   * @brief Emits a signal with 2 parameters.
-   *
-   * @SINCE_1_0.0
-   * @param[in] arg0 The first parameter
-   * @param[in] arg1 The second parameter
-   * @return The value returned by the last callback
-   * @pre Cannot be called from inside the same Signal's Emit methods.
-   */
-  template<typename Ret, typename Arg0, typename Arg1>
-  Ret EmitReturn(Arg0 arg0, Arg1 arg1)
-  {
-    Ret returnVal = Ret();
-
-    // Guards against nested Emit() calls
-    EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
-    if(guard.ErrorOccurred())
-    {
-      return returnVal;
-    }
+    // Guards against calling CleanupConnections if the signal is deleted during emission
+    bool signalDeleted{false};
+    mSignalDeleted = &signalDeleted;
 
     // If more connections are added by callbacks, these are ignore until the next Emit()
     // Note that count cannot be reduced while iterating
-    const std::size_t initialCount(mSignalConnections.Count());
-
-    for(std::size_t i = 0; i < initialCount; ++i)
+    auto count = mSignalConnections.size();
+    auto iter  = mSignalConnections.begin();
+    while(count--)
     {
-      CallbackBase* callback(GetCallback(i));
-
-      // Note that connections will be set to NULL when disconnected
-      // This is preferable to reducing the connection count while iterating
+      CallbackBase* callback((*iter) ? iter->GetCallback() : nullptr);
+      ++iter;
       if(callback)
       {
-        returnVal = CallbackBase::ExecuteReturn<Ret, Arg0, Arg1>(*callback, arg0, arg1);
+        CallbackBase::Execute<Args...>(*callback, args...);
       }
     }
 
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-
-    return returnVal;
-  }
-
-  /**
-   * @brief Emits a signal with 3 parameters.
-   *
-   * @SINCE_1_0.0
-   * @param[in] arg0 The first parameter
-   * @param[in] arg1 The second parameter
-   * @param[in] arg2 The third parameter
-   * @pre Cannot be called from inside the same Signal's Emit methods.
-   */
-  template<typename Arg0, typename Arg1, typename Arg2>
-  void Emit(Arg0 arg0, Arg1 arg1, Arg2 arg2)
-  {
-    // Guards against nested Emit() calls
-    EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
-    if(guard.ErrorOccurred())
-    {
-      return;
-    }
-
-    // If more connections are added by callbacks, these are ignore until the next Emit()
-    // Note that count cannot be reduced while iterating
-    const std::size_t initialCount(mSignalConnections.Count());
-
-    for(std::size_t i = 0; i < initialCount; ++i)
+    if(!signalDeleted)
     {
-      CallbackBase* callback(GetCallback(i));
-
-      // Note that connections will be set to NULL when disconnected
-      // This is preferable to reducing the connection count while iterating
-      if(callback)
-      {
-        CallbackBase::Execute<Arg0, Arg1, Arg2>(*callback, arg0, arg1, arg2);
-      }
+      // Cleanup NULL values from Connection container
+      CleanupConnections();
+      mSignalDeleted = nullptr;
     }
-
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-  }
-
-  /**
-   * @brief Emits a signal with 3 parameters.
-   *
-   * @SINCE_1_0.0
-   * @param[in] arg0 The first parameter
-   * @param[in] arg1 The second parameter
-   * @param[in] arg2 The third parameter
-   * @return The value returned by the last callback
-   * @pre Cannot be called from inside the same Signal's Emit methods.
-   */
-  template<typename Ret, typename Arg0, typename Arg1, typename Arg2>
-  Ret EmitReturn(Arg0 arg0, Arg1 arg1, Arg2 arg2)
-  {
-    Ret returnVal = Ret();
-
-    // Guards against nested Emit() calls
-    EmitGuard guard(mEmittingFlag); // Guards against nested Emit() calls
-    if(guard.ErrorOccurred())
-    {
-      return returnVal;
-    }
-
-    // If more connections are added by callbacks, these are ignore until the next Emit()
-    // Note that count cannot be reduced while iterating
-    const std::size_t initialCount(mSignalConnections.Count());
-
-    for(std::size_t i = 0; i < initialCount; ++i)
-    {
-      CallbackBase* callback(GetCallback(i));
-
-      // Note that connections will be set to NULL when disconnected
-      // This is preferable to reducing the connection count while iterating
-      if(callback)
-      {
-        returnVal = CallbackBase::ExecuteReturn<Ret, Arg0, Arg1, Arg2>(*callback, arg0, arg1, arg2);
-      }
-    }
-
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-
-    return returnVal;
   }
 
   // Connect / Disconnect function for use by Signal implementations
@@ -476,30 +284,21 @@ private: // SlotObserver interface, to be told when a slot disconnects
 
 private:
   /**
-   * @brief Returns a callback given an index in to the connection array.
-   *
-   * @SINCE_1_0.0
-   * @param[in] connectionIndex The index of the callback
-   * @return The callback, or NULL if the connection has been deleted
-   */
-  CallbackBase* GetCallback(std::size_t connectionIndex) const;
-
-  /**
    * @brief Helper to find whether a callback is connected.
    *
-   * @SINCE_1_0.0
+   * @SINCE_2_1.22
    * @param[in] callback The call back object
    * @return A valid index if the callback is connected
    */
-  int32_t FindCallback(CallbackBase* callback);
+  std::list<SignalConnection>::iterator FindCallback(CallbackBase* callback) noexcept;
 
   /**
    * @brief Deletes a connection object from the list of connections.
    *
-   * @SINCE_1_0.0
-   * @param[in] connectionIndex The index of the callback
+   * @SINCE_2_1.22
+   * @param[in] iter The index of the callback
    */
-  void DeleteConnection(std::size_t connectionIndex);
+  void DeleteConnection(std::list<SignalConnection>::iterator iter);
 
   /**
    * @brief Helper to remove NULL items from mSignalConnections, which is only safe at the end of Emit()
@@ -514,9 +313,14 @@ private:
   BaseSignal& operator=(BaseSignal&&) = delete;      ///< Deleted move assignment operator. @SINCE_1_9.25
 
 private:
-  Dali::Vector<SignalConnection*> mSignalConnections; ///< Array of connections
+  struct DALI_INTERNAL Impl;
+  Impl*                mCacheImpl; ///< Private internal extra data.
 
-  bool mEmittingFlag; ///< Used to guard against nested Emit() calls
+private:
+  std::list<SignalConnection> mSignalConnections{};    ///< List of connections
+  uint32_t                    mNullConnections{0};     ///< Empty Connections in the array.
+  bool                        mEmittingFlag{false};    ///< Used to guard against nested Emit() calls.
+  bool*                       mSignalDeleted{nullptr}; ///< Used to guard against deletion during Emit() calls.
 };
 
 /**