| 31 | |
| 32 | TEST_F(test_example_use_case, a_const_shared_ptr_of_timer_is_thread_safe) { |
| 33 | class ExampleOwner : public std::enable_shared_from_this<ExampleOwner> { |
| 34 | struct hidden { }; // private struct ensures that ::create is used for a proper setup |
| 35 | public: |
| 36 | static std::shared_ptr<ExampleOwner> create(boost::asio::io_context& _io) { |
| 37 | auto p = std::make_shared<ExampleOwner>(hidden{}, _io); |
| 38 | // 1. ensures that task is properly set before any actual usage of the timer. |
| 39 | // 2. capture this by weak_ref to avoid the need of manual destruction of the timer. |
| 40 | // Note: This step can not be done in the c'tor as the weak_from_this/shared_from_this call would return a nullptr at this |
| 41 | // point. |
| 42 | // Note: This two step setup is inevitable as the task of the timer and the owner of the timer form an ownership cycle (although |
| 43 | // only temporary due to the usage of weak_from_this). |
| 44 | |
| 45 | // the return can be ignored in this very instance, because the timer had no chance to have been |
| 46 | // started before. The return is given, to highlight that adjusting the timers task at a later |
| 47 | // stage is a dangerous operation and has a chance to fail (because the timer class itself will |
| 48 | // protect against the change of the task while the timer is running). |
| 49 | [[maybe_unused]] bool set_was_success = p->timer_->set_task([weak_ref = p->weak_from_this()] { |
| 50 | if (auto self = weak_ref.lock(); self) { |
| 51 | self->counter_ += 1; |
| 52 | } |
| 53 | return true; |
| 54 | }); |
| 55 | return p; |
| 56 | } |
| 57 | |
| 58 | ExampleOwner([[maybe_unused]] hidden _, boost::asio::io_context& _io) : |
| 59 | // create a timer with a dummy task to ensure the shared_ptr is receiving its memory in the ctor and can therefore be |
| 60 | // used from any thread without further inspection |
| 61 | timer_(timer::create(_io, 12ms, [] { return false; })) { } |
| 62 | |
| 63 | // Because the timer has a thread-safe interface, the shared_ptr to the timer is const, and the task is setup is ensured |
| 64 | // in a dedicated create function there is no need for synchronization primitives when using the timer itself. |
| 65 | void start() { timer_->start(); } |
| 66 | void stop() { timer_->stop(); } |
| 67 | |
| 68 | std::atomic<uint32_t> counter_{0}; |
| 69 | |
| 70 | private: |
| 71 | std::shared_ptr<timer> const timer_; |
| 72 | }; |
| 73 | |
| 74 | auto owner = ExampleOwner::create(dummy_context_); |
| 75 | // start the timer |
nothing calls this directly
no outgoing calls
no test coverage detected