Re: Free lightweight C++ signals and slots library
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