On Monday 19 February 2007 02:55 am, Timmo Stange wrote:
We could have the connection body dispense some kind of reference-counted token. The slot iterator would grab a copy of the token when it has iterated onto a slot that it might execute, and destroy it when it iterates past the slot or destructs. disconnect()/block() could check the reference count to decide when it is safe to return, either spinning or waiting on a condition variable in the meantime. Since we only want the disconnect() to wait if the slot is running in a different thread, the token would have to maintain both a global and a thread-specific reference count. disconnect()/block() could return when they are equal.
If you don't let disconnect() return until all calls to the slot are through, you're back to square one with regard to potential deadlocks. Keep in mind that disconnect() may be called (in practice often is) from a slot call context.
Yes, I think what I outlined is essentially amounts to a recursive read-write mutex.
I don't think there is a clean solution to this which covers all problems. It would be possible to have different threading policies for either behavior (one being dead-lock-safe, the other with strong disconnect() guarantees), but that defeats the whole abstraction idea.
Yes, I think the strong disconnect() guarantee will also guarantee the deadlock you outlined earlier with two slots attempting to disconnect each other will always be possible.
Alternatively, disconnect() could return a bool if the disconnected slot is currently running or throw an exception. That could be optional through an argument (disconnect(true)) or a different function (checked_disconnect()). A connection::wait() function could wait for all pending calls to finish, but couldn't be safely called from a signal invocation context.
I like connection::wait() better. a return value from disconnect still wouldn't be able to tell you when the slot is really gone, only that it wasn't gone when disconnect returned. -- Frank