base-signal connect and disconnect in O(1)
[platform/core/uifw/dali-core.git] / dali / public-api / signals / base-signal.h
index 02e15a5..5420952 100644 (file)
@@ -1,8 +1,8 @@
-#ifndef __DALI_BASE_SIGNAL_H__
-#define __DALI_BASE_SIGNAL_H__
+#ifndef DALI_BASE_SIGNAL_H
+#define DALI_BASE_SIGNAL_H
 
 /*
- * Copyright (c) 2015 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>
@@ -69,16 +72,15 @@ namespace Dali
  * <tr><td> %Callback 2 </td><td> Slot Observer 2  </td></tr>
  * </table>
  *
- * When the connection tracker is destroyed, SlotDisconnected() is called on every slot observer ( signal )
- * Signals implement the Slot Observer interface, to be told when a slot has disconnected
- * Connection tracker implements the Signal Observer interface, to be told when a signal has disconnected (died)
+ * When the connection tracker is destroyed, SlotDisconnected() is called on every slot observer ( signal ).
+ * Signals implement the Slot Observer interface, to be told when a slot has disconnected.
+ * Connection tracker implements the Signal Observer interface, to be told when a signal has disconnected (died).
  *
  * @SINCE_1_0.0
  */
-class DALI_IMPORT_API BaseSignal : public SlotObserver
+class DALI_CORE_API BaseSignal : public SlotObserver
 {
 public:
-
   /**
    * @brief Constructor.
    * @SINCE_1_0.0
@@ -89,23 +91,29 @@ public:
    * @brief Virtual destructor.
    * @SINCE_1_0.0
    */
-  virtual ~BaseSignal();
+  ~BaseSignal() override;
 
   /**
-   * @brief Query whether there are any connected slots.
+   * @brief Queries whether there are any connected slots.
    *
    * @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 Query the number of slots.
+   * @brief Queries the number of slots.
    *
    * @SINCE_1_0.0
-   * @return The number of slots connected to this signal.
+   * @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
 
@@ -113,15 +121,15 @@ public:
    * @brief Used to guard against nested Emit() calls.
    * @SINCE_1_0.0
    */
-  struct EmitGuard
+  struct DALI_CORE_API EmitGuard
   {
     /**
-     * @brief Create the guard.
+     * @brief Creates the guard.
      *
      * @SINCE_1_0.0
-     * @param[in,out] flag This flag will be set to true during Emit() calls.
+     * @param[in,out] flag This flag will be set to true during Emit() calls
      */
-    EmitGuard( bool& flag );
+    EmitGuard(bool& flag);
 
     /**
      * @brief Non-virtual destructor.
@@ -131,7 +139,7 @@ public:
     ~EmitGuard();
 
     /**
-     * @brief  Determine if an error occured.
+     * @brief Determines if an error occurred.
      *
      * @SINCE_1_0.0
      * @return True if an error occurred i.e. if Emit() was called during Emit()
@@ -142,367 +150,155 @@ public:
   };
 
   /**
-   * @brief Emit 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.
    */
-  void Emit();
-
-  /**
-   * @brief Emit a signal with no parameters.
-   *
-   * @SINCE_1_0.0
-   * @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();
 
     // Guards against nested Emit() calls
-    EmitGuard guard( mEmittingFlag );
-    if( guard.ErrorOccurred() )
+    EmitGuard guard(mEmittingFlag);
+    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>( *callback );
-      }
-    }
-
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-
-    return returnVal;
-  }
-
-  /**
-   * @brief Emit 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
-      if( callback )
+      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 Emit 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 Emit 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
-    if( guard.ErrorOccurred() )
+    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 )
-    {
-      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 Emit 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;
-    }
-
-    // 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>( *callback, arg0, arg1 );
-      }
-    }
-
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-
-    return returnVal;
-  }
-
-  /**
-   * @brief Emit 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;
-    }
+    // 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
-      if( callback )
+      CallbackBase* callback((*iter) ? iter->GetCallback() : nullptr);
+      ++iter;
+      if(callback)
       {
-        CallbackBase::Execute<Arg0,Arg1,Arg2>( *callback, arg0, arg1, arg2 );
+        CallbackBase::Execute<Args...>(*callback, args...);
       }
     }
 
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-  }
-
-  /**
-   * @brief Emit 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 )
+    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,Arg1,Arg2>( *callback, arg0, arg1, arg2 );
-      }
+      // Cleanup NULL values from Connection container
+      CleanupConnections();
+      mSignalDeleted = nullptr;
     }
-
-    // Cleanup NULL values from Connection container
-    CleanupConnections();
-
-    return returnVal;
   }
 
   // Connect / Disconnect function for use by Signal implementations
 
   /**
-   * @brief Called by Signal implementations, when the user calls Signal.Connect( ... )
+   * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
    *
    * @SINCE_1_0.0
-   * @param[in] callback A newly allocated callback object (takes ownership).
+   * @param[in] callback A newly allocated callback object (takes ownership)
    */
-  void OnConnect( CallbackBase* callback );
+  void OnConnect(CallbackBase* callback);
 
   /**
-   * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... )
+   * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
    *
    * @SINCE_1_0.0
-   * @param[in] callback A newly allocated callback object (takes ownership).
+   * @param[in] callback A newly allocated callback object (takes ownership)
    */
-  void OnDisconnect( CallbackBase* callback );
+  void OnDisconnect(CallbackBase* callback);
 
   /**
-   * @brief Called by Signal implementations, when the user calls Signal.Connect( ... )
+   * @brief Called by Signal implementations, when the user calls Signal.Connect( ... ).
    *
    * @SINCE_1_0.0
-   * @param[in] tracker The connection tracker.
-   * @param[in] callback A newly allocated callback object (takes ownership).
+   * @param[in] tracker The connection tracker
+   * @param[in] callback A newly allocated callback object (takes ownership)
    */
-  void OnConnect( ConnectionTrackerInterface* tracker, CallbackBase* callback );
+  void OnConnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
 
   /**
-   * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... )
+   * @brief Called by Signal implementations, when the user calls Signal.Disconnect( ... ).
    *
    * @SINCE_1_0.0
-   * @param[in] tracker The connection tracker.
-   * @param[in] callback A newly allocated callback object (takes ownership).
+   * @param[in] tracker The connection tracker
+   * @param[in] callback A newly allocated callback object (takes ownership)
    */
-  void OnDisconnect( ConnectionTrackerInterface* tracker, CallbackBase* callback );
+  void OnDisconnect(ConnectionTrackerInterface* tracker, CallbackBase* callback);
 
 private: // SlotObserver interface, to be told when a slot disconnects
-
   /**
    * @copydoc SlotObserver::SlotDisconnected
    */
-  virtual void SlotDisconnected( CallbackBase* callback );
+  void SlotDisconnected(CallbackBase* callback) override;
 
 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
-   * @param[in] callback The call back object.
-   * @return A valid index if the callback is connected.
+   * @SINCE_2_1.22
+   * @param[in] callback The call back object
+   * @return A valid index if the callback is connected
    */
-  int 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()
@@ -511,14 +307,20 @@ private:
    */
   void CleanupConnections();
 
-  BaseSignal( const BaseSignal& );                   ///< undefined copy constructor, signals don't support copying. @SINCE_1_0.0
-  BaseSignal& operator=( const BaseSignal& );        ///< undefined assignment operator @SINCE_1_0.0
+  BaseSignal(const BaseSignal&) = delete;            ///< Deleted copy constructor, signals don't support copying. @SINCE_1_0.0
+  BaseSignal(BaseSignal&&)      = delete;            ///< Deleted move constructor, signals don't support moving. @SINCE_1_9.25
+  BaseSignal& operator=(const BaseSignal&) = delete; ///< Deleted copy assignment operator. @SINCE_1_0.0
+  BaseSignal& operator=(BaseSignal&&) = delete;      ///< Deleted move assignment operator. @SINCE_1_9.25
 
 private:
+  struct DALI_INTERNAL Impl;
+  Impl*                mCacheImpl; ///< Private internal extra data.
 
-  Dali::Vector< SignalConnection* > mSignalConnections;   ///< Array of connections
-
-  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.
 };
 
 /**
@@ -526,4 +328,4 @@ private:
  */
 } // namespace Dali
 
-#endif // __DALI_BASE_SIGNAL_H__
+#endif // DALI_BASE_SIGNAL_H