• Advertisement
Sign in to follow this  

signal slot and thread safety

This topic is 1900 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I'm currently working on a very small signal slot implementation and have an initial version released, however, the code will probably fail in any concurrent scenarios.
Basically I'm asking for any helpful information as to what must happen to make the following code thread safe. http://code.google.c...signal_slot.hpp
I'm not sure if the problem can be easily solved with the current implementation as a signal would probably have to be locked during any operation not to mention the destructor of tracked.
Lastly, any constructive criticism is welcome. For those who don't even link:


struct tracked
{
std::shared_ptr<std::function<void(std::uintptr_t)>> erase_callback;
std::unordered_map<std::uintptr_t, std::weak_ptr<std::function<void(std::uintptr_t)>>>
connections;
tracked ( ) : erase_callback(new std::function<void(std::uintptr_t)>)
{
(*erase_callback) = [this] (std::uintptr_t connection)
{
this->connections.erase(connection);
};
}
virtual ~tracked ( )
{
for (auto const& connection : connections)
{
if (connection.second.expired() == false)
{
(*connection.second.lock())(connection.first);
}
}
}
};


template <typename... Args> struct signal { signal ( ) = delete; };
template <typename... Args> struct signal<void(Args...)> : private tracked
{
signal ( ) : tracked ( )
{
(*erase_callback) = [this] (std::uintptr_t connection)
{
this->connections.erase(connection);
this->m_slot.erase(connection);
};
}
//------------------------------------------------------------------------------
template<typename T>
void connect(void(T::*callback)(Args...), T* instance)
{
std::uintptr_t hash = member_hash<T, Args...>()(callback, instance);
m_slot.emplace(hash, make_function<T, Args...>(callback, instance));
derived_tracked<T>(hash, instance);
}
void connect(void(*callback)(Args...))
{
m_free.emplace(reinterpret_cast<std::uintptr_t>(callback), callback);
}
//------------------------------------------------------------------------------
template<typename T>
void disconnect(void(T::*callback)(Args...), T* instance)
{
(*erase_callback)(member_hash<T, Args...>()(callback, instance));
}
void disconnect(void(*callback)(Args...))
{
m_free.erase(reinterpret_cast<std::uintptr_t>(callback));
}
//------------------------------------------------------------------------------
void operator() (Args... args) const
{
for (auto const& connection : m_slot)
{
connection.second(args...);
}
for (auto const& connection : m_free)
{
connection.second(args...);
}
}
private: //-----------------------------------------------------------------
template<typename T>
void derived_tracked(std::uintptr_t hash, typename T::tracked* instance)
{
connections.emplace(hash, instance->erase_callback);
instance->connections.emplace(hash, erase_callback);
}
template<typename T> void derived_tracked(...) { } // sfinae
std::unordered_map<std::uintptr_t, std::function<void(Args...)>> m_slot;
std::unordered_map<std::uintptr_t, std::function<void(Args...)>> m_free;
};

Edit: above code is for context. current code is at the link above. Edited by ApEk

Share this post


Link to post
Share on other sites
Advertisement
Well got around to doing some performance testing against two other implementations, sigc++ and boost::signals.
However, still wondering how the heck to make it thread safe.

nano_signal_slot.jpg
Higher is better. x axis is input size where there are N connections made and N signals fired.

Share this post


Link to post
Share on other sites
AFAIK, Boost::signals is not thread safe, Boost::signals2 is. Did you check it? You can just search where and how it puts mutex around.

Share this post


Link to post
Share on other sites
I have done research on current libraries. All of them make me say why? I'm about to just store a mutex in tracked and lock for any operation. Then instead of weak_ptr::expired do weak_ptr::lock and test the shared_ptr then lock the mutex inside of the erase callback?

Edit: saving thread safety for another time. could probably end up wrapping up the underlying data structure and add locking. Edited by ApEk

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement