Re: Free lightweight C++ signals and slots library

Discussion in 'C++' started by Marcel Müller, Aug 12, 2012.

  1. On 11.08.12 14.32, Leigh Johnston wrote:
    > I present "neosigslot" a new, free to use/modify, lightweight signals
    > and slots library that has the following features:
    >
    > * Automatic deregistration of slots from signals when a slot is
    > destroyed.
    > * Fully re-entrant: signals and slots can be destroyed, added and
    > signals triggered whilst a slot is being notified of a signal.
    > * Support for "slots with keys" to allow one signal to be used for
    > more than one event (e.g. GUI menu command IDs).
    > * Support for up to 10 signal arguments (if you need more see a doctor
    > or edit the source).
    > * Threading policy (by default mutex locking is turned on ensuring
    > thread safety but a no locking policy can also be specified).
    >
    > http://i42.co.uk/stuff/neosigslot.htm


    Hmm, I wrote a similar set of classes some time ago.

    You spent quite much effort to make it re-entrant. But from what I can
    see each invocation of trigger copies a notification list. This is a
    high price. But, maybe I did not catch your implementation correctly.

    Furthermore, I think the mutex solution may deadlock, because the mutex
    is held until the event handlers have completed. While this is no
    problem when calling myself, it might become a problem if two signals
    are involved and if thread 1 calls signal2.trigger() in the handler of
    signal1 and thread 2 vice versa.
    Of course, without this mutex the handling of slot destruction while a
    signal handler is active becomes a tricky task. But it is always
    critical to call handlers from some internal, synchronized context.

    I have no real clean solution for the mutex problem. I have run into the
    same problem and decided to use some static mutex together with
    cond-vars to handle the destructors. So I need to synchronize access to
    the notification list only when advancing to the next slot in trigger.

    Another solution instead of your notification lists might use a revision
    counter. It is incremented at each invocation of add/remove. The counter
    is part of signal_base and it is copied into the registered slots.
    Trigger samples the revision counter and takes care of it during
    invocation of the handlers.
    Additionally you need a reference counter on each registered slot. It is
    incremented at add and decremented at remove. And it is incremented at
    the start of trigger for the current content of the slot list and
    decremented after a slot has been handled by trigger. As usual as soon
    as it counts to zero, the entry could be removed from the map.
    To avoid ABA problems you need to use a multi_map or put the counter
    into the key, because different revisions of the same slot may coexist
    for short times.

    A even more sophisticated task might be to do most of the operations
    lock free, because dealing with several thousands of mutexes of several
    thousand signals could be a serious problem on some platforms.
    I have gone this way to some degree, but the price is that remove is now
    O(n) instead of O(log(n)) of your implementation. Most likely a lock
    free skip list could improve that, but I did not spent much time in this
    topic because more that two dozen registrations per signal are very
    uncommon in my use case.


    Marcel
    Marcel Müller, Aug 12, 2012
    #1
    1. Advertising

  2. On 12.08.2012 16:12, Leigh Johnston wrote:
    >> Furthermore, I think the mutex solution may deadlock, because the mutex
    >> is held until the event handlers have completed. While this is no
    >> problem when calling myself, it might become a problem if two signals
    >> are involved and if thread 1 calls signal2.trigger() in the handler of
    >> signal1 and thread 2 vice versa.
    >> Of course, without this mutex the handling of slot destruction while a
    >> signal handler is active becomes a tricky task. But it is always
    >> critical to call handlers from some internal, synchronized context.

    >
    > Not sure if it will currently deadlock as you describe as you cannot
    > send signals between threads (i.e. signal2 handler would still be
    > thread1).


    No, I am talking about two asynchronous signals created by independent
    threads. It's quite simple to get that.

    Think of a change notification of two dependent objects. E.g. a page and
    a document. Changing a page may also change some aggregate properties of
    the document (e.g. the number of words). Changing document properties
    like the footer area may also introduce changes to the pages. If these
    two action happen concurrently than thread 1 may lock the mutex of the
    page while thread 2 locks the mutex of the document. None of the threads
    will ever be able to acquire the second mutex to dispatch the dependency
    change events.


    Marcel
    Marcel Müller, Aug 12, 2012
    #2
    1. Advertising

  3. On 13.08.2012 00:29, Leigh Johnston wrote:
    > After further thought yes I agree deadlocks you mention are possible
    > however thread 1 should not be triggering signals which are also handled
    > by thread 2 and vice-versa; if you keep this in mind there should be no
    > problems.


    OK, this means that an event handler (that is generally from another
    object) must not fire other events, because it cannot ensure that it is
    in the same thread from what the events are normally fired. This
    secondary event has to be dispatched to the thread that "owns" the
    corresponding signal class. You need to post the event in an
    asynchronous queue of the target thread. (No serious problem in GUI
    applications with message queues, but inconvenient in others.)
    Is this that the inter-thread signalling feature, you mentioned?

    One should aware of this restriction.


    Marcel
    Marcel Müller, Aug 13, 2012
    #3
  4. On 13.08.2012 16:26, Leigh Johnston wrote:
    > An event handler can fire other events no problem if the programmer
    > knows that it is the same thread (which is almost certainly the case if
    > the programmer is in control of the software design).


    If an event handler can safely fire other events (with the restriction),
    it must always be called from the same thread. And furthermore the event
    that it fires must also always be called by the same thread only. In
    fact that means that your application must be single threaded with
    respect to event processing.

    But if there is a strict ordering of all events, i.e. event A can call
    ebent B but never the other way around, then none of the restrictions apply.

    Marcel
    Marcel Müller, Aug 15, 2012
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. tin gherdanarra

    slots? SLOTS?

    tin gherdanarra, Oct 12, 2005, in forum: Python
    Replies:
    2
    Views:
    2,305
    Peter Hansen
    Oct 13, 2005
  2. Alex M.
    Replies:
    4
    Views:
    472
    Rolf Magnus
    Aug 2, 2010
  3. lolveley
    Replies:
    6
    Views:
    179
    Stefano Crocco
    May 13, 2009
  4. Jorgen Grahn
    Replies:
    11
    Views:
    649
    Jorgen Grahn
    Aug 21, 2012
  5. Rui Maciel
    Replies:
    0
    Views:
    366
    Rui Maciel
    Aug 11, 2012
Loading...

Share This Page