Post on 16-Apr-2017
Some remarks about the code in these slides● The namespace names are omitted
● Sometimes the const references are omitted
● The example code has poor design for simplicity purposes
● The example classes only have methods that relate to signals
● TypePtr is a typedef for shared_ptr<Type>
Two major problems1. No natural way to connect to a signal and get the object’s state without a race
condition
2. The “disconnect” method does not guarantee that the execution flow is out of the
signal handler right after disconnecting
Two major problems1. No natural way to connect to a signal and get the object’s state without a race
condition
2. The “disconnect” method does not guarantee that the execution flow is out of the
signal handler right after disconnecting
The ProblemLet’s implement a StorageManager class that manages removable storages (USB sticks,
DVD disks, etc.)
Drawbacks● A redundant interface with many degrees of freedom
● The StorageManager object is not actually thread-safe
● Mostly copy-pasted code to connect to any similar signal
Two major problems1. No natural way to connect to a signal and get the object’s state without a race
condition
2. The “disconnect” method does not guarantee that the execution flow is out of the
signal handler right after disconnecting
Trying to solve itBoost suggests using slot_type::track() method, but we need a shared_ptr to the
tracked object
WAT?● Too much code for nothing but creating of a MediaScanner object
● The interaction between MediaScanner and StorageManager objects spread
outside of the MediaScanner class
● Client code is forced to own the MediaScanner object via shared_ptr
● Again, all this code doesn’t make much sense, and is almost the same for all
similar cases
Drawbacks● Requires an internal Impl class
● Still a lot of “almost copy-pasted” code to connect to a signal
Trying to get rid of the Impl classBut it does not work in the constructor, so we need a separate public method
Drawbacks● Client code is forced to own the MediaScanner object via shared_ptr
● Client code is responsible for invoking MediaScanner::ConnectToSignals
● Still a lot of “almost copy-pasted” code to connect to a signal
Drawbacks● Client code is forced to own the MediaScanner object via shared_ptr
● Still a lot of “almost copy-pasted” code to connect to a signal
Designing better signalsProblem:
No natural way to connect to a signal and get the object’s state without a race
condition
Solution:
The mutex and the collection were pretty good, but all that code might be hidden
inside the “connect” method
Designing better signalsProblem:
No natural way to connect to a signal and get the object’s state without a race
condition
Solution:
The mutex and the collection were pretty good, but all that code might be hidden
inside the “connect” method
Also:
The way we obtain the object state depends on the state nature. It is different for a
collection state and for a single object state. Thus, we need an abstraction here
Problem:
The “disconnect” method does not guarantee that the execution flow is out of the
signal handler right after disconnecting
Solution:
We need a way to mark the handler as “dying” and wait for the end of its
execution if necessary
Designing better signals
Problem:
The “disconnect” method does not guarantee that the execution flow is out of the
signal handler right after disconnecting
Solution:
We need a way to mark the handler as “dying” and wait for the end of its
execution if necessary
Also:
The SignalConnection destructor should wait on some object, and the handler
should lock that object for the execution time
Designing better signals
The LifeTokenLifeToken
an object that controls the handler lifetime, and has a blocking Release method
LifeToken::Checker
stored inside the handler info and references the LifeToken
LifeToken::Checker::ExecutionGuard
a local object that locks the LifeToken for the handler execution time
What is inside the LifeToken?● Some OS primitive for waiting (I use Mutex in these slides)
● Alive flag
● Lock counter (omitted here to make the code simpler)
● ExecutionGuard constructor should block the OS primitive
● ExecutionGuard destructor should unblock the OS primitive
● LifeToken destructor should wait until the OS primitive is unblocked
SummaryThere are two key features that result in much clearer client code:
1. Populators send the object state via the same handler that is used for further
updates tracking
2. A blocking disconnect method results in better incapsulation, without a need for a
nested Impl class or factory methods
The wigwag library● A header-only library
● Fast and lightweight signals implementation
● Template policies for threading, exception handling and everything
● Async handlers
● Listeners
https://github.com/koplyarov/wigwag
Wigwag vs boost comparisonCPU:
Intel Core i7-3517U @ 1.90GHz
Operating system:
Ubuntu 15.10
https://github.com/koplyarov/wigwag
Wigwag vs boost comparisonui_signal:
The most lightweight wigwag signal version. No threading, no populators, no
handler life control. Great for UI components.
signal:
A default wigwag signal configuration. Suits most developers.
boost:
Signals from boost::signals2 library. Both tracking and non-tracking slot versions
are used in handler comparison.
https://github.com/koplyarov/wigwag