palacaze / sigslot

A simple C++14 signal-slots implementation

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Library is not thread-safe: deadlock can happen

sergey-shambir opened this issue · comments

Here library calls foreign callbacks under mutex lock:

template <typename... A>
    void operator()(A && ... a) {
        lock_type lock(m_mutex);
        slot_ptr *prev = nullptr;
        slot_ptr *curr = m_slots ? &m_slots : nullptr;

        while (curr) {
            // call non blocked, non connected slots
            if ((*curr)->connected()) {
                if (!m_block && !(*curr)->blocked())
                    (*curr)->operator()(std::forward<A>(a)...);
                prev = curr;
                curr = (*curr)->next ? &((*curr)->next) : nullptr;
            }
            // remove slots marked as disconnected
            else {
                if (prev) {
                    (*prev)->next = (*curr)->next;
                    curr = (*prev)->next ? &((*prev)->next) : nullptr;
                }
                else
                    curr = (*curr)->next ? &((*curr)->next) : nullptr;
            }
        }
}

Imagine that one thread connects to "signal1" own function "slot1" which fires "signal2". First, it locks "signal1" mutex, then attempts to lock "signal2" mutex.

Another thread connects to "signal2" own function "slot2" which fires "signal1". First, it locks "signal2" mutex, then attempts to lock "signal1" mutex.

In some cases, threads will enter into deadlock: thread1 always waits "signal2" mutex, while thread2 always waits "signal1" mutex.

Illustration:
default

You are absolutely right.

I think the right think to do may be to just collect the callbacks to be called in a first pass, then release the lock and finally loop over the collected slots and call them one after another.

I worry that doing so may need some dynamic allocation to hold the list of slots, but the good news is that the contention on the lock is likely to drop a lot.

I pushed a fix and a unit test for this issue.
Let me know if you think this is not sufficient.