SObjectizer-5 Extra
Loading...
Searching...
No Matches
pub.hpp
Go to the documentation of this file.
1/*!
2 * \file
3 * \brief Implementation of revocable timers
4 *
5 * \since
6 * v.1.2.0
7 */
8
9#pragma once
10
11#include <so_5/version.hpp>
12
13#if SO_5_VERSION < SO_5_VERSION_MAKE(5, 8, 1)
14#error "SObjectizer v.5.8.1 or above required"
15#endif
16
17#include <so_5_extra/revocable_msg/pub.hpp>
18
19#include <so_5_extra/error_ranges.hpp>
20
21#include <so_5/timers.hpp>
22#include <so_5/enveloped_msg.hpp>
23#include <so_5/send_functions.hpp>
24
25#include <atomic>
26
27namespace so_5 {
28
29namespace extra {
30
31namespace revocable_timer {
32
33namespace details {
34
35//
36// envelope_t
37//
38/*!
39 * \brief A special envelope to be used for revocable timer messages.
40 *
41 * Just a synonim for so_5::extra::revocable_msg::details::envelope_t.
42 *
43 * \since
44 * v.1.2.0
45 */
46using envelope_t = so_5::extra::revocable_msg::details::envelope_t;
47
48} /* namespace details */
49
50namespace impl {
51
52// Just forward declaration. Definition will be below definition of timer_id_t.
53struct timer_id_maker_t;
54
55} /* namespace impl */
56
57//
58// timer_id_t
59//
60/*!
61 * \brief The ID of revocable timer message/signal.
62 *
63 * This type plays the same role as so_5::timer_id_t. But provide
64 * guaranteed revocation of delayed/periodic message/signal.
65 *
66 * There are several implementations of send_delayed() and send_periodic()
67 * functions in so_5::extra::revocable_timer namespace. They all return
68 * instances of timer_id_t.
69 *
70 * An instance of timer_id_t returned from send_delayed/send_periodic need
71 * to be store somewhere. Otherwise the timer message will be revoked
72 * just after completion of send_delayed/send_periodic function. It is
73 * because the destructor of timer_id_t will be called and that destructor
74 * revokes the timer message.
75 *
76 * An instance of timer_id_t can be used for revocation of a timer message.
77 * Revocation can be performed by two ways:
78 *
79 * 1. Destructor of timer_id_t automatically revokes the timer message.
80 * 2. Method timer_id_t::release() or timer_id_t::revoke() is called
81 * by an user.
82 *
83 * For example:
84 * \code
85 * namespace timer_ns = so_5::extra::revocable_timer;
86 * void demo(so_5::mchain_t work_queue) {
87 * // Send a delayed demand to work queue and store the ID returned.
88 * auto id = timer_ns::send_delayed<flush_data>(work_queue, 10s, ...);
89 * ... // Do some work.
90 * if(some_condition)
91 * // Our previous message should be revoked if it is not delivered yet.
92 * id.release();
93 * ...
94 * // Message will be automatically revoked here because ID is destroyed
95 * // on leaving the scope.
96 * }
97 * \endcode
98 *
99 * \note
100 * The timer_id_t is Movable, not Copyable.
101 *
102 * \attention
103 * This is not a thread-safe class. It means that it is dangerous to
104 * call methods of that class (like revoke() or is_active()) from
105 * different threads at the same time.
106 *
107 * \since
108 * v.1.2.0
109 */
110class timer_id_t final
111 {
113
114 private :
115 //! The envelope that was sent.
116 /*!
117 * \note Can be nullptr if default constructor was used.
118 */
120
121 //! Timer ID for the envelope.
123
125 ::so_5::intrusive_ptr_t< details::envelope_t > envelope,
126 ::so_5::timer_id_t actual_id )
129 {}
130
131 public :
132 timer_id_t() = default;
133 /*!
134 * \note The destructor automatically revokes the message if it is
135 * not delivered yet.
136 */
137 ~timer_id_t() noexcept
138 {
139 release();
140 }
141
142 // This class is not copyable.
143 timer_id_t( const timer_id_t & ) = delete;
144 timer_id_t & operator=( const timer_id_t & ) = delete;
145
146 // But this class is moveable.
147 timer_id_t( timer_id_t && ) noexcept = default;
148 timer_id_t & operator=( timer_id_t && ) noexcept = default;
149
150 friend void
151 swap( timer_id_t & a, timer_id_t & b ) noexcept
152 {
153 using std::swap;
154 swap( a.m_envelope, b.m_envelope );
155 swap( a.m_actual_id, b.m_actual_id );
156 }
157
158 //! Is message delivery still in progress?
159 /*!
160 * \note Please take care when using this method.
161 * Message delivery in SObjectizer is asynchronous operation.
162 * It means that you can receve \a true from is_active() but
163 * this value will already be obsolete because the message
164 * can be delivered just before return from is_active().
165 * The return value of is_active() can be useful in that context:
166 * \code
167 * namespace timer_ns = so_5::extra::revocable_timer;
168 * void demo(so_5::mchain_t work_queue) {
169 * auto id = timer_ns::send_delayed(work_queue, 10s, ...);
170 * ... // Do some work.
171 * if(some_condition)
172 * id.revoke();
173 * ... // Do some more work.
174 * if(another_condition)
175 * id.revoke();
176 * ...
177 * if(id.is_active()) {
178 * // No previous calls to revoke().
179 * ...
180 * }
181 * }
182 * \endcode
183 */
184 bool
185 is_active() const noexcept
186 {
187 return m_actual_id.is_active();
188 }
189
190 //! Revoke the message and release the timer.
191 /*!
192 * \note
193 * It is safe to call release() for already revoked message.
194 */
195 void
196 release() noexcept
197 {
198 if( m_envelope )
199 {
200 m_envelope->revoke();
201 m_actual_id.release();
202
203 m_envelope.reset();
204 }
205 }
206
207 //! Revoke the message and release the timer.
208 /*!
209 * Just a synonym for release() method.
210 */
211 void
212 revoke() noexcept { release(); }
213 };
214
215namespace impl {
216
217/*
218 * This is helper for creation of initialized timer_id objects.
219 */
221 {
222 template< typename... Args >
223 [[nodiscard]] static auto
224 make( Args && ...args )
225 {
226 return ::so_5::extra::revocable_timer::timer_id_t{
227 std::forward<Args>(args)... };
228 }
229 };
230
231/*
232 * Helper function for actual sending of periodic message.
233 */
234[[nodiscard]]
235inline so_5::extra::revocable_timer::timer_id_t
237 const so_5::mbox_t & to,
238 const std::type_index & msg_type,
239 message_ref_t payload,
240 std::chrono::steady_clock::duration pause,
241 std::chrono::steady_clock::duration period )
242 {
243 using envelope_t = ::so_5::extra::revocable_timer::details::envelope_t;
244
245 ::so_5::intrusive_ptr_t< envelope_t > envelope{
246 std::make_unique< envelope_t >( std::move(payload) ) };
247
248 auto actual_id = ::so_5::low_level_api::schedule_timer(
249 msg_type,
250 envelope,
251 to,
252 pause,
253 period );
254
255 return timer_id_maker_t::make(
256 std::move(envelope), std::move(actual_id) );
257 }
258
259/*
260 * This is helpers for send_delayed and send_periodic implementation.
261 */
262
263template< class Message, bool Is_Signal >
265 {
266 template< typename... Args >
267 [[nodiscard]] static ::so_5::extra::revocable_timer::timer_id_t
269 const ::so_5::mbox_t & to,
270 std::chrono::steady_clock::duration pause,
271 std::chrono::steady_clock::duration period,
272 Args &&... args )
273 {
274 // Since SO-5.8.1 mutability is handled by make_message_instance.
277 std::forward< Args >( args )...)
278 };
279
281 to,
283 std::move(payload),
284 pause,
285 period );
286 }
287 };
288
289template< class Message >
290struct instantiator_and_sender_base< Message, true >
291 {
292 //! Type of signal to be delivered.
294
295 [[nodiscard]] static so_5::extra::revocable_timer::timer_id_t
297 const so_5::mbox_t & to,
298 std::chrono::steady_clock::duration pause,
299 std::chrono::steady_clock::duration period )
300 {
302 to,
305 pause,
306 period );
307 }
308 };
309
310template< class Message >
316
317} /* namespace impl */
318
319/*!
320 * \brief A utility function for creating and delivering a periodic message
321 * to the specified destination.
322 *
323 * Agent, mbox or mchain can be used as \a target.
324 *
325 * \note
326 * Message chains with overload control must be used for periodic messages
327 * with additional care because exceptions can't be thrown during
328 * dispatching messages from timer.
329 *
330 * Usage example 1:
331 * \code
332 * namespace timer_ns = so_5::extra::revocable_timer;
333 * class my_agent : public so_5::agent_t {
334 * timer_ns::timer_id_t timer_;
335 * ...
336 * void so_evt_start() override {
337 * ...
338 * // Initiate a periodic message to self.
339 * timer_ = timer_ns::send_periodic<do_some_task>(*this, 1s, 1s, ...);
340 * ...
341 * }
342 * ...
343 * };
344 * \endcode
345 *
346 * Usage example 2:
347 * \code
348 * so_5::wrapped_env_t sobj; // SObjectizer is started here.
349 * // Create a worker and get its mbox.
350 * so_5::mbox_t worker_mbox = sobj.environment().introduce_coop(
351 * [&](so_5::coop_t & coop) {
352 * auto worker = coop.make_agent<worker_agent>(...);
353 * return worker->so_direct_mbox();
354 * });
355 * // Send revocable periodic message to the worker.
356 * auto timer_id = so_5::extra::revocable_timer::send_periodic<tell_status>(
357 * worker_mbox(),
358 * 1s, 1s,
359 * ... );
360 * ... // Do some work.
361 * // Revoke the tell_status message.
362 * timer_id.release();
363 * \endcode
364 *
365 * \note
366 * The return value of that function must be stored somewhere. Otherwise
367 * the periodic timer will be cancelled automatically just right after
368 * send_periodic returns.
369 *
370 * \attention
371 * Values of \a pause and \a period should be non-negative.
372 *
373 * \tparam Message type of message or signal to be sent.
374 * \tparam Target can be so_5::agent_t, so_5::mbox_t or so_5::mchain_t.
375 * \tparam Args list of arguments for Message's constructor.
376 *
377 * \since
378 * v.1.2.0
379 */
380template< typename Message, typename Target, typename... Args >
381[[nodiscard]] timer_id_t
383 //! A destination for the periodic message.
384 Target && target,
385 //! Pause for message delaying.
386 std::chrono::steady_clock::duration pause,
387 //! Period of message repetitions.
388 std::chrono::steady_clock::duration period,
389 //! Message constructor parameters.
390 Args&&... args )
391 {
394 pause,
395 period,
396 std::forward< Args >( args )... );
397 }
398
399/*!
400 * \brief A utility function for delivering a periodic
401 * from an existing message hood.
402 *
403 * \attention Message must not be a mutable message if \a period is not 0.
404 * Otherwise an exception will be thrown.
405 *
406 * \tparam Message a type of message to be redirected (it can be
407 * in form of Msg, so_5::immutable_msg<Msg> or so_5::mutable_msg<Msg>).
408 *
409 * Usage example:
410 * \code
411 namespace timer_ns = so_5::extra::revocable_timer;
412 class redirector : public so_5::agent_t {
413 ...
414 void on_some_immutable_message(mhood_t<first_msg> cmd) {
415 timer_id = timer_ns::send_periodic(
416 another_mbox,
417 std::chrono::seconds(1),
418 std::chrono::seconds(15),
419 cmd);
420 ...
421 }
422
423 void on_some_mutable_message(mhood_t<mutable_msg<second_msg>> cmd) {
424 timer_id = timer_ns::send_periodic(
425 another_mbox,
426 std::chrono::seconds(1),
427 std::chrono::seconds(20),
428 std::move(cmd));
429 // Note: cmd is nullptr now, it can't be used anymore.
430 ...
431 }
432 };
433 * \endcode
434 *
435 * \note
436 * The return value of that function must be stored somewhere. Otherwise
437 * the periodic timer will be cancelled automatically just right after
438 * send_periodic returns.
439 *
440 * \attention
441 * Values of \a pause and \a period should be non-negative.
442 *
443 * \since
444 * v.1.2.0
445 */
446template< typename Message >
447[[nodiscard]]
448typename std::enable_if<
449 !::so_5::is_signal< Message >::value,
452 //! Mbox for the message to be sent to.
453 const ::so_5::mbox_t & to,
454 //! Pause for message delaying.
456 //! Period of message repetitions.
458 //! Existing message hood for message to be sent.
459 ::so_5::mhood_t< Message > mhood )
460 {
462 to,
465 pause,
466 period );
467 }
468
469/*!
470 * \brief A utility function for periodic redirection of a signal
471 * from existing message hood.
472 *
473 * \tparam Message a type of signal to be redirected (it can be
474 * in form of Sig or so_5::immutable_msg<Sig>).
475 *
476 * Usage example:
477 * \code
478 class redirector : public so_5::agent_t {
479 ...
480 void on_some_immutable_signal(mhood_t<some_signal> cmd) {
481 timer_id = so_5::extra::revocable_timer::send_periodic(
482 another_mbox,
483 std::chrono::seconds(1),
484 std::chrono::seconds(10),
485 cmd);
486 ...
487 }
488 };
489 * \endcode
490 *
491 * \note
492 * The return value of that function must be stored somewhere. Otherwise
493 * the periodic timer will be cancelled automatically just right after
494 * send_periodic returns.
495 *
496 * \attention
497 * Values of \a pause and \a period should be non-negative.
498 *
499 * \since
500 * v.1.2.0
501 */
502template< typename Message >
503[[nodiscard]]
504typename std::enable_if<
508 //! Mbox for the message to be sent to.
509 const ::so_5::mbox_t & to,
510 //! Pause for message delaying.
512 //! Period of message repetitions.
514 //! Existing message hood for message to be sent.
515 ::so_5::mhood_t< Message > /*mhood*/ )
516 {
518 to,
521 pause,
522 period );
523 }
524
525/*!
526 * \brief A helper function for redirection of a message/signal as a periodic
527 * message/signal.
528 *
529 * This function can be used if \a target is a reference to agent or if
530 * \a target is a mchain
531 *
532 * Example usage:
533 * \code
534 * namespace timer_ns = so_5::extra::revocable_timer;
535 * class my_agent : public so_5::agent_t {
536 * ...
537 * so_5::mchain_t target_mchain_;
538 * timer_ns::timer_id_t periodic_msg_id_;
539 * ...
540 * void on_some_msg(mhood_t<some_msg> cmd) {
541 * if( ... )
542 * // Message should be resend as a periodic message.
543 * periodic_msg_id_ = timer_ns::send_periodic(target_mchain_, 10s, 20s, std::move(cmd));
544 * }
545 * \endcode
546 *
547 * \note
548 * The return value of that function must be stored somewhere. Otherwise
549 * the periodic timer will be cancelled automatically just right after
550 * send_periodic returns.
551 *
552 * \attention
553 * Values of \a pause and \a period should be non-negative.
554 *
555 * \since
556 * v.1.2.0
557 */
558template< typename Message, typename Target >
561 //! A target for periodic message/signal.
562 //! It can be a reference to a target agent or a mchain_t.
563 Target && target,
564 //! Pause for the first occurence of the message/signal.
566 //! Period of message repetitions.
568 //! Existing message hood for message/signal to be sent.
569 ::so_5::mhood_t< Message > mhood )
570 {
573 pause,
574 period,
575 std::move(mhood) );
576 }
577
578/*!
579 * \brief A utility function for creating and delivering a delayed message
580 * to the specified destination.
581 *
582 * Agent, mbox or mchain can be used as \a target.
583 *
584 * \note
585 * Message chains with overload control must be used for periodic messages
586 * with additional care because exceptions can't be thrown during
587 * dispatching messages from timer.
588 *
589 * Usage example 1:
590 * \code
591 * namespace timer_ns = so_5::extra::revocable_timer;
592 * class my_agent : public so_5::agent_t {
593 * timer_ns::timer_id_t timer_;
594 * ...
595 * void so_evt_start() override {
596 * ...
597 * // Initiate a delayed message to self.
598 * timer_ = timer_ns::send_periodic<kill_youself>(*this, 60s, ...);
599 * ...
600 * }
601 * ...
602 * };
603 * \endcode
604 *
605 * Usage example 2:
606 * \code
607 * so_5::wrapped_env_t sobj; // SObjectizer is started here.
608 * // Create a worker and get its mbox.
609 * so_5::mbox_t worker_mbox = sobj.environment().introduce_coop(
610 * [&](so_5::coop_t & coop) {
611 * auto worker = coop.make_agent<worker_agent>(...);
612 * worker_mbox = worker->so_direct_mbox();
613 * });
614 * // Send revocable delayed message to the worker.
615 * auto timer_id = so_5::extra::revocable_timer::send_periodic<kill_yourself>(
616 * worker_mbox(),
617 * 60s,
618 * ... );
619 * ... // Do some work.
620 * // Revoke the kill_yourself message.
621 * timer_id.release();
622 * \endcode
623 *
624 * \note
625 * The return value of that function must be stored somewhere. Otherwise
626 * the delayed timer will be cancelled automatically just right after
627 * send_delayed returns.
628 *
629 * \attention
630 * Value of \a pause should be non-negative.
631 *
632 * \tparam Message type of message or signal to be sent.
633 * \tparam Target can be so_5::agent_t, so_5::mbox_t or so_5::mchain_t.
634 * \tparam Args list of arguments for Message's constructor.
635 *
636 * \since
637 * v.1.2.0
638 */
639template< typename Message, typename Target, typename... Args >
642 //! A destination for the periodic message.
643 Target && target,
644 //! Pause for message delaying.
646 //! Message constructor parameters.
647 Args&&... args )
648 {
651 pause,
653 std::forward<Args>(args)... );
654 }
655
656/*!
657 * \brief A helper function for redirection of existing message/signal
658 * as delayed message.
659 *
660 * Usage example:
661 * \code
662 * namespace timer_ns = so_5::extra::revocable_timer;
663 * class my_agent : public so_5::agent_t {
664 * const so_5::mbox_t another_worker_;
665 * timer_ns::timer_id_t timer_;
666 * ...
667 * void on_some_msg(mhood_t<some_message> cmd) {
668 * // Redirect this message to another worker with delay in 250ms.
669 * timer_ = timer_ns::send_delayed(
670 * another_worker_,
671 * std::chrono::milliseconds(250),
672 * std::move(cmd));
673 * ...
674 * }
675 * };
676 * \endcode
677 *
678 * \note
679 * The return value of that function must be stored somewhere. Otherwise
680 * the delayed timer will be cancelled automatically just right after
681 * send_delayed returns.
682 *
683 * \attention
684 * Value of \a pause should be non-negative.
685 *
686 * \tparam Message type of message or signal to be sent.
687 *
688 * \since
689 * v.1.2.0
690 */
691template< typename Message >
694 //! Mbox for the message to be sent to.
695 const so_5::mbox_t & to,
696 //! Pause for message delaying.
698 //! Message to redirect.
699 ::so_5::mhood_t< Message > cmd )
700 {
702 to,
703 pause,
705 std::move(cmd) );
706 }
707
708/*!
709 * \brief A helper function for redirection of existing message/signal
710 * as delayed message.
711 *
712 * Agent or mchain can be used as \a target.
713 *
714 * Usage example:
715 * \code
716 * namespace timer_ns = so_5::extra::revocable_timer;
717 * class my_agent : public so_5::agent_t {
718 * const so_5::mchain_t work_queue_;
719 * timer_ns::timer_id_t timer_;
720 * ...
721 * void on_some_msg(mhood_t<some_message> cmd) {
722 * // Redirect this message to another worker with delay in 250ms.
723 * timer_ = timer_ns::send_delayed(work_queue_,
724 * std::chrono::milliseconds(250),
725 * std::move(cmd));
726 * ...
727 * }
728 * };
729 * \endcode
730 *
731 * \note
732 * The return value of that function must be stored somewhere. Otherwise
733 * the delayed timer will be cancelled automatically just right after
734 * send_delayed returns.
735 *
736 * \attention
737 * Value of \a pause should be non-negative.
738 *
739 * \tparam Message type of message or signal to be sent.
740 *
741 * \since
742 * v.1.2.0
743 */
744template< typename Message, typename Target >
747 //! A destination for the periodic message.
748 Target && target,
749 //! Pause for message delaying.
751 //! Message to redirect.
752 ::so_5::mhood_t< Message > cmd )
753 {
756 pause,
758 std::move(cmd) );
759 }
760
761} /* namespace revocable_timer */
762
763} /* namespace extra */
764
765} /* namespace so_5 */
timer_id_t & operator=(timer_id_t &&) noexcept=default
void release() noexcept
Revoke the message and release the timer.
Definition pub.hpp:196
::so_5::timer_id_t m_actual_id
Timer ID for the envelope.
Definition pub.hpp:122
timer_id_t(::so_5::intrusive_ptr_t< details::envelope_t > envelope, ::so_5::timer_id_t actual_id)
Definition pub.hpp:124
timer_id_t(timer_id_t &&) noexcept=default
bool is_active() const noexcept
Is message delivery still in progress?
Definition pub.hpp:185
timer_id_t & operator=(const timer_id_t &)=delete
friend void swap(timer_id_t &a, timer_id_t &b) noexcept
Definition pub.hpp:151
void revoke() noexcept
Revoke the message and release the timer.
Definition pub.hpp:212
::so_5::intrusive_ptr_t< details::envelope_t > m_envelope
The envelope that was sent.
Definition pub.hpp:119
timer_id_t(const timer_id_t &)=delete
so_5::extra::revocable_timer::timer_id_t make_envelope_and_initiate_timer(const so_5::mbox_t &to, const std::type_index &msg_type, message_ref_t payload, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period)
Definition pub.hpp:236
timer_id_t send_periodic(Target &&target, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period, Args &&... args)
A utility function for creating and delivering a periodic message to the specified destination.
Definition pub.hpp:382
Ranges for error codes of each submodules.
Definition details.hpp:13
static so_5::extra::revocable_timer::timer_id_t send_periodic(const so_5::mbox_t &to, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period)
Definition pub.hpp:296
static ::so_5::extra::revocable_timer::timer_id_t send_periodic(const ::so_5::mbox_t &to, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period, Args &&... args)
Definition pub.hpp:268