poll/select). ioready_dispatcher provides an implementation of this interface that allows waiting (with timeout) for and fetching the required information from the operating system and notifying registered receivers. The implementations in this library provide strong thread-safety guarantees, are reentrant, deadlock-free and provide strong consistency guarantees. See section Concurrency and reentrancy below for an exact definition of the guarantees.
class Observable1 { public: tscb::signal<void(int, int)> on_value_change; void change_value(int new_value) { int old_value=value; value=new_value; on_value_change(old_value, new_value); } protected: int value; }; class Observable2 { public: tscb::signal_proxy<void(int, int)> &on_value_change(void) throw() { return value_change; } void change_value(int new_value) { int old_value=value; value=new_value; value_change(old_value, new_value); } protected: int value; tscb::signal<void(int, int)> value_change; };
Both classes allow an observer to register callbacks (via Observable1::on_value_change::connect(...) or Observable2::on_value_change()connect(...), respectively), but Observable1 also exports the capability to notify registered callbacks to external classes (which is probably unintended).
The second reason is to decouple "consumers" of notifications from the exact mechanism used for providing them. Classes only interested in receiving notification should depend on posix_reactor_service instead of posix_reactor: The former allows the implementation to be supplanted by a "bridge class" that delegates to services provided by other frameworks such as Qt or Gtk. (FIXME: Qt/Glib support split out into dependent libraries, not sure if worth maintaining in separate repository).
All implementations provide the following concurrency guarantees:
All implementations provide the following reentrancy guarantee: From within a callback A registered to service X, the following operations may be performed:
Finally, the implementation provides the folliwing consistency guarantee: If a callback is deregistered it will not be invoked "subsequently" from the same or other threads. For the same thread, "subsequently" refers to the normal flow of execution after the disconnect operation returns. For other threads this means that if
These goals are achieved by employing synchronization mechanisms that generally allow readers to access shared data structures without any locking, with carefully designed access protocols using appropriate atomic operations to ensure consistency. "Critical" operations that might affect concurrent readers are split up into "safe" modifications that may be performed at any time (e.g. modifying a forward-traversable linked list) and "unsafe" modifications that must be deferred to quiescent periods where no reader is active (e.g. freeing memory of objects accessed without locking). In almost all fast-path cases the overhead over a highly optimized single-threaded implementation is just two atomic operations and is thus very close to the theoretical optimum.
The basic access idiom to shared data structures for readers is:
Writers must adhere to the following protocol:
Note during the step marked (*) above, it is permissible to initiate nested read or write acesses using the same synchronization idiom without access or conflicts or potential for deadlock.
The library relies on the C++0x atomic datatypes, an implementation of the required functional subset is provided for gcc on various target machines in order to make the library useful with older compilers and systems.
| Implementation | call single callback | call 10 callbacks | connect+disconnect | comments
|
|---|---|---|---|---|
| open-coded ( std::list of function pointers) | 16 | 150 | 92 | not thread-safe |
| open-coded ( std::list of boost::function objects) | 33 | 320 | 254 | not thread-safe |
tscb::signal | 120 | 436 | 1286 | thread-safe |
sigc::signal | 280 | 400 | 1216 | not thread-safe |
boost::signal | 432 | 1310 | 3362 | not thread-safe |
boost::signals2 | 593 | 2803 | 2146 | thread-safe |
Debian Linux 5.0, gcc-4.3.2, DEC Alpha EV6 @500MHz
| Implementation | call single callback | call 10 callbacks | connect+disconnect | comments
|
|---|---|---|---|---|
| open-coded ( std::list of function pointers) | 16 | 141 | 458 | not thread-safe |
| open-coded ( std::list of boost::function objects) | 32 | 333 | 576 | not thread-safe |
tscb::signal | 157 | 472 | 2164 | thread-safe |
sigc::signal | 576 | 812 | 2885 | not thread-safe |
boost::signal | 796 | 1810 | 11241 | not thread-safe |
n pipe pairs, n handler functions that read a token out of one pipe and write it into the next one. Numbers indicate clock cycles per single dispatch operation (one forwarding of the token to the next pipe).Debian Linux 5.0, gcc-4.3.2, Intel Celeron @2GHz
| Implementation | 32 pipe pairs | 64 pipe pairs | 128 pipe pairs |
|---|---|---|---|
| open-coded ( epoll_wait+read+write) | 2425 | 2439 | 2477 |
ACE | 3469 | 3460 | 3494 |
| tscb::posix_reactor | 3292 | 3308 | 3344 |
boost::asio | 11406 | 11426 | 11536 |
1.5.6