fix multi-threaded spinning (#867)
* replace global spinmutex with SpinnerMonitor
Using a single recursive mutex disables to run several spinners in
parallel (started from different threads) - even if they operate on
different callback queues.
The SpinnerMonitor keeps a list of spinning callback queues, thus
making the monitoring local to callback queues.
* always start spinning, regardless of error
* making the error fatal
* fixup! replace global spinmutex with SpinnerMonitor
* activate unittest for spinners
- moved test/test_roscpp/test/test_spinners.cpp -> test/test_roscpp/test/src/spinners.cpp
- created rostest test/test_roscpp/test/launch/spinners.xml
- use thrown exception to evaluate error conditions
* throw a std::run_time exception when spinner couldn't be started
This correctly indicates the fatality of the error but allows for graceful quitting too.
* correctly count number of started multi-threaded spinners
fixes unittest Spinners.async
* addressed comments
* bug fix: it wasn't reassigned correctly
* removed redundant line of code
* turn sanity check into assertion
* turn error into warning
* rollback strict error checking for backwards compatibility
Previously, we could have the following situations:
1. `S1` (single-threaded spinner started in `thread 1`): will block `thread 1` until shutdown. Any further spinners in different threads were not allowed (with an error message).
2. `M1` (multi-threaded spinner started in `thread 1`): Further spinners started from _different_ threads were not allowed (with an error message).
3. `M1 ... M1 S1` (multi-threaded spinners started in `thread 1` and afterwards a single-threaded one started): This was accepted without any errors. But the new behavior is to reject `S1`!
Restrictions of case 1 + 2 are relaxed with this PR: Other spinners are allowed as long as the operate on a different queue. Thread doesn't matter.
The tricky part is case 3, which - although nonsense - was perfectly valid code before.
In order to maintain the old behavior, I need to remember, which thread the first M-spinner was started in, using the new variable `initial_tid`.
* allow spinning of a single-threaded spinner after some multi-threaded ones, as long as they are started from the same thread
* don't throw exceptions
* disabled corresponding unittests
* code simplification + improved comments
Allow multiple single-threaded spinners (in same thread)
and count their number.
Thus single-threaded and multi-threaded spinners are handled similarly.