
Frank Mori Hess wrote:
We could implement a thread-safe post-disconnect callback by using the existing tracking functionality. That is, the connection body could hold a "cleanup" shared_ptr with a custom deleter. The custom deleter would run the user's cleanup callback. A weak_ptr copy of the cleanup shared_ptr would be kept just like it was any other tracked object. When disconnect() is called, the cleanup shared_ptr would be reset. If the signal is being invoked concurrently when disconnect() is called, the cleanup code won't get run until the last invocation is done with the slot and destroys its copy of the cleanup shared_ptr.
I think we should perhaps first decide on the interface and behavior before we discuss an implementation. That cleanup callback should be optional, without parameters and return value, right? I also think it should be called synchronously directly from disconnect if the slot is not running - otherwise from the executing thread directly after the slot returns. I don't directly see the necessity for a shared_ptr there and I have to express my performance worries again. As nice as shared_ptr's thread-safety is, it doesn't come for free. If you store the callback in a Boost.Function object maintained by a shared pointer, we're talking about a minimum of three heap allocations (2 for the function and 1 for the reference count) - with most allocator implementations, those allocations imply process-global synchro- nization. The smart pointer itself (including all temporary copies and the creation from a weak_ptr) uses atomic reference counting. Both are scalability issues (mostly the synchronization) and exceptionally heavy in simple absolute runtime cost (as much as 50 times the cost of a simple object copy and more, depending on the CPU and system). I really hate to play the optimization freak here, but we should keep an eye on those things, because if the abstraction the library provides comes at a too high runtime cost, the usability is severely limited. I usually avoid heap allocations in library code and if I need to have them, I try to provide an optional allocator template.
BTW, I've added support for a two-parameter version of signals::track() to thread_safe_signals/track.hpp. It seems to work okay, the only thing I don't like about it is it implies there a coupling required between the value and the tracked shared_ptr, when really there isn't.
Could you give a short example of how that would look with using bind and a simple free function expecting a pointer or reference to an object that should be tracked? Regards Timmo Stange