1 /*
2 * SObjectizer-5
3 */
4
5 /*!
6 * \file
7 * \since
8 * v.5.5.1
9 *
10 * \brief Implementation of free functions send/send_delayed.
11 */
12
13 #pragma once
14
15 #include <so_5/environment.hpp>
16
17 #include <so_5/compiler_features.hpp>
18
19 namespace so_5
20 {
21
22 namespace impl
23 {
24
25 /*
26 * This is helpers for so_5::send implementation.
27 */
28
29 template< class Message, bool Is_Signal >
30 struct instantiator_and_sender_base
31 {
32 private :
33 // Helper method for message instance creation and
34 // mutability flag handling.
35 template< typename... Args >
36 static auto
make_instanceso_5::impl::instantiator_and_sender_base37 make_instance( Args &&... args )
38 {
39 // it will be std::unique_ptr<Envelope>, where Envelope
40 // can be a different type. But Envelope is derived from
41 // so_5::message_t.
42 auto msg_instance =
43 so_5::details::make_message_instance< Message >(
44 std::forward< Args >( args )...);
45 so_5::details::mark_as_mutable_if_necessary< Message >(
46 *msg_instance );
47
48 return msg_instance;
49 }
50
51 public :
52 template< typename... Args >
53 static void
sendso_5::impl::instantiator_and_sender_base54 send(
55 const so_5::mbox_t & to,
56 Args &&... args )
57 {
58 so_5::low_level_api::deliver_message(
59 *to,
60 message_payload_type< Message >::subscription_type_index(),
61 make_instance( std::forward<Args>(args)... ) );
62 }
63
64 template< typename... Args >
65 static void
send_delayedso_5::impl::instantiator_and_sender_base66 send_delayed(
67 const so_5::mbox_t & to,
68 std::chrono::steady_clock::duration pause,
69 Args &&... args )
70 {
71 so_5::low_level_api::single_timer(
72 message_payload_type< Message >::subscription_type_index(),
73 message_ref_t{ make_instance( std::forward<Args>(args)... ) },
74 to,
75 pause );
76 }
77
78 template< typename... Args >
79 [[nodiscard]] static timer_id_t
send_periodicso_5::impl::instantiator_and_sender_base80 send_periodic(
81 const so_5::mbox_t & to,
82 std::chrono::steady_clock::duration pause,
83 std::chrono::steady_clock::duration period,
84 Args &&... args )
85 {
86 return so_5::low_level_api::schedule_timer(
87 message_payload_type< Message >::subscription_type_index(),
88 message_ref_t{ make_instance( std::forward<Args>(args)... ) },
89 to,
90 pause,
91 period );
92 }
93 };
94
95 template< class Message >
96 struct instantiator_and_sender_base< Message, true >
97 {
98 //! Type of signal to be delivered.
99 using actual_signal_type = typename message_payload_type< Message >::subscription_type;
100
101 static void
sendso_5::impl::instantiator_and_sender_base102 send( const so_5::mbox_t & to )
103 {
104 using namespace so_5::low_level_api;
105 deliver_signal< actual_signal_type >( *to );
106 }
107
108 static void
send_delayedso_5::impl::instantiator_and_sender_base109 send_delayed(
110 const so_5::mbox_t & to,
111 std::chrono::steady_clock::duration pause )
112 {
113 so_5::low_level_api::single_timer(
114 message_payload_type<Message>::subscription_type_index(),
115 message_ref_t{},
116 to,
117 pause );
118 }
119
120 [[nodiscard]] static timer_id_t
send_periodicso_5::impl::instantiator_and_sender_base121 send_periodic(
122 const so_5::mbox_t & to,
123 std::chrono::steady_clock::duration pause,
124 std::chrono::steady_clock::duration period )
125 {
126 return so_5::low_level_api::schedule_timer(
127 message_payload_type< Message >::subscription_type_index(),
128 message_ref_t{},
129 to,
130 pause,
131 period );
132 }
133 };
134
135 template< class Message >
136 struct instantiator_and_sender
137 : public instantiator_and_sender_base<
138 Message,
139 is_signal< typename message_payload_type< Message >::payload_type >::value >
140 {};
141
142 } /* namespace impl */
143
144 /*!
145 * \since
146 * v.5.5.9
147 *
148 * \brief Implementation details for send-family and request_future/value helper functions.
149 */
150 namespace send_functions_details {
151
152 inline const so_5::mbox_t &
arg_to_mbox(const so_5::mbox_t & mbox)153 arg_to_mbox( const so_5::mbox_t & mbox ) { return mbox; }
154
155 inline const so_5::mbox_t &
arg_to_mbox(const so_5::agent_t & agent)156 arg_to_mbox( const so_5::agent_t & agent ) { return agent.so_direct_mbox(); }
157
158 inline so_5::mbox_t
arg_to_mbox(const so_5::mchain_t & chain)159 arg_to_mbox( const so_5::mchain_t & chain ) { return chain->as_mbox(); }
160
161 inline so_5::environment_t &
arg_to_env(const so_5::mbox_t & mbox)162 arg_to_env( const so_5::mbox_t & mbox ) { return mbox->environment(); }
163
164 inline so_5::environment_t &
arg_to_env(const so_5::agent_t & agent)165 arg_to_env( const so_5::agent_t & agent ) { return agent.so_environment(); }
166
167 inline so_5::environment_t &
arg_to_env(const so_5::mchain_t & chain)168 arg_to_env( const so_5::mchain_t & chain ) { return chain->environment(); }
169
170 } /* namespace send_functions_details */
171
172 /*!
173 * \since
174 * v.5.5.1
175 *
176 * \brief A utility function for creating and delivering a message or a signal.
177 *
178 * \note Since v.5.5.13 can send also a signal.
179 *
180 * \tparam Message type of message to be sent.
181 * \tparam Target identification of request processor. Could be reference to
182 * so_5::mbox_t, to so_5::agent_t (the later case agent's direct
183 * mbox will be used).
184 * \tparam Args arguments for Message's constructor.
185 *
186 * \par Usage samples:
187 * \code
188 struct hello_msg { std::string greeting; std::string who };
189
190 // Send to mbox.
191 so_5::send< hello_msg >( env.create_mbox( "hello" ), "Hello", "World!" );
192
193 // Send to agent.
194 class demo_agent : public so_5::agent_t
195 {
196 public :
197 ...
198 virtual void so_evt_start() override
199 {
200 ...
201 so_5::send< hello_msg >( *this, "Hello", "World!" );
202 }
203 };
204
205 struct turn_on : public so_5::signal_t {};
206
207 // Send to mbox.
208 so_5::send< turn_on >( env.create_mbox( "engine" ) );
209
210 // Send to agent.
211 class engine_agent : public so_5::agent_t
212 {
213 public :
214 ...
215 virtual void so_evt_start() override
216 {
217 ...
218 so_5::send< turn_on >( *this );
219 }
220 };
221 * \endcode
222 */
223 template< typename Message, typename Target, typename... Args >
224 void
send(Target && to,Args &&...args)225 send( Target && to, Args&&... args )
226 {
227 so_5::impl::instantiator_and_sender< Message >::send(
228 send_functions_details::arg_to_mbox( std::forward<Target>(to) ),
229 std::forward<Args>(args)... );
230 }
231
232 /*!
233 * \brief A version of %send function for redirection of a message
234 * from exising message hood.
235 *
236 * \tparam Message a type of message to be redirected (it can be
237 * in form of Msg, so_5::immutable_msg<Msg> or so_5::mutable_msg<Msg>).
238 *
239 * Usage example:
240 * \code
241 class redirector : public so_5::agent_t {
242 ...
243 void on_some_immutable_message(mhood_t<first_msg> cmd) {
244 so_5::send(another_mbox, cmd);
245 ...
246 }
247
248 void on_some_mutable_message(mhood_t<mutable_msg<second_msg>> cmd) {
249 so_5::send(another_mbox, std::move(cmd));
250 // Note: cmd is nullptr now, it can't be used anymore.
251 ...
252 }
253 };
254 * \endcode
255 *
256 * \since
257 * v.5.5.19
258 */
259 template< typename Target, typename Message >
260 typename std::enable_if< !is_signal< Message >::value >::type
send(Target && to,mhood_t<Message> what)261 send( Target && to, mhood_t< Message > what )
262 {
263 using namespace so_5::low_level_api;
264
265 deliver_message(
266 *send_functions_details::arg_to_mbox( std::forward<Target>(to) ),
267 message_payload_type<Message>::subscription_type_index(),
268 what.make_reference() );
269 }
270
271 /*!
272 * \brief A version of %send function for redirection of a signal
273 * from exising message hood.
274 *
275 * \tparam Message a type of signal to be redirected (it can be
276 * in form of Sig or so_5::immutable_msg<Sig>).
277 *
278 * Usage example:
279 * \code
280 class redirector : public so_5::agent_t {
281 ...
282 void on_some_immutable_signal(mhood_t<some_signal> cmd) {
283 so_5::send(another_mbox, cmd);
284 ...
285 }
286 };
287 * \endcode
288 *
289 * \since
290 * v.5.5.19
291 */
292 template< typename Target, typename Message >
293 typename std::enable_if< is_signal< Message >::value >::type
send(Target && to,mhood_t<Message>)294 send( Target && to, mhood_t< Message > /*what*/ )
295 {
296 send_functions_details::arg_to_mbox( std::forward<Target>(to) )->
297 template deliver_signal<
298 typename message_payload_type<Message>::subscription_type >();
299 }
300
301 /*!
302 * \brief A version of %send function for redirection of a message
303 * from exising message_holder instance.
304 *
305 * Usage example:
306 * \code
307 class preallocated_messages_owner final : public so_5::agent_t {
308 so_5::message_holder_t<some_message> first_;
309 so_5::message_holder_t<so_5::mutable_msg<another_message>> second_;
310 ...
311 void on_some_event(mhood_t<some_event>) {
312 // It is time to send preallocated messages.
313
314 // This message will be sent as immutable message.
315 so_5::send(dest, first_);
316
317 // This message will be sent as mutable message.
318 so_5::send(dest, std::move(second_));
319 }
320 };
321 * \endcode
322 *
323 * \attention
324 * An attempt to call this function for empty message_holder object is UB.
325 *
326 * \since
327 * v.5.6.0
328 */
329 template<
330 typename Target,
331 typename Message,
332 message_ownership_t Ownership>
333 void
send(Target && to,message_holder_t<Message,Ownership> what)334 send(
335 //! Destination for the message.
336 Target && to,
337 //! Message to be sent.
338 message_holder_t<Message, Ownership> what )
339 {
340 using namespace so_5::low_level_api;
341
342 deliver_message(
343 *send_functions_details::arg_to_mbox( std::forward<Target>(to) ),
344 message_payload_type<Message>::subscription_type_index(),
345 what.make_reference() );
346 }
347
348 /*!
349 * \brief A utility function for creating and delivering a delayed message
350 * to the specified destination.
351 *
352 * Agent or mchain can be used as \a target.
353 *
354 * \attention
355 * Value of \a pause should be non-negative.
356 *
357 * \tparam Message type of message or signal to be sent.
358 * \tparam Target can be so_5::agent_t or so_5::mchain_t.
359 * \tparam Args list of arguments for Message's constructor.
360 *
361 * \since
362 * v.5.5.19
363 */
364 template< typename Message, typename Target, typename... Args >
365 void
send_delayed(Target && target,std::chrono::steady_clock::duration pause,Args &&...args)366 send_delayed(
367 //! A target for delayed message.
368 Target && target,
369 //! Pause for message delaying.
370 std::chrono::steady_clock::duration pause,
371 //! Message constructor parameters.
372 Args&&... args )
373 {
374 using namespace send_functions_details;
375
376 so_5::impl::instantiator_and_sender< Message >::send_delayed(
377 arg_to_mbox( target ),
378 pause,
379 std::forward< Args >(args)... );
380 }
381
382 /*!
383 * \brief A utility function for delayed redirection of a message
384 * from existing message hood.
385 *
386 * \tparam Target a type of destination of the message. It can be an agent,
387 * a mbox or mchain.
388 * \tparam Message a type of message to be redirected (it can be
389 * in form of Msg, so_5::immutable_msg<Msg> or so_5::mutable_msg<Msg>).
390 *
391 * Usage example:
392 * \code
393 class redirector : public so_5::agent_t {
394 ...
395 void on_some_immutable_message(mhood_t<first_msg> cmd) {
396 so_5::send_delayed(another_mbox, std::chrono::seconds(1), cmd);
397 ...
398 }
399
400 void on_some_mutable_message(mhood_t<mutable_msg<second_msg>> cmd) {
401 so_5::send_delayed(another_mbox, std::chrono::seconds(1), std::move(cmd));
402 // Note: cmd is nullptr now, it can't be used anymore.
403 ...
404 }
405 };
406 * \endcode
407 * A redirection to the direct mbox of some agent can looks like:
408 * \code
409 * void some_agent::on_some_message(mhood_t<some_message> cmd) {
410 * // Redirect to itself but with a pause.
411 * so_5::send_delayed(*this, std::chrono::seconds(2), cmd);
412 * }
413 * // For the case of mutable message.
414 * void another_agent::on_another_msg(mutable_mhood_t<another_msg> cmd) {
415 * // Redirect to itself but with a pause.
416 * so_5::send_delayed(*this, std::chrono::seconds(2), std::move(cmd));
417 * // Note: cmd is nullptr now, it can't be used anymore.
418 * }
419 * \endcode
420 * A redirection to a mchain can looks like:
421 * \code
422 * so_5::mchain_t first = ...;
423 * so_5::mchain_t second = ...;
424 * so_5::receive(so_5::from(first).handle_n(1),
425 * [second](so_5::mhood_t<some_message> cmd) {
426 * so_5::send_delayed(second, std::chrono::seconds(1), cmd);
427 * },
428 * [second](so_5::mutable_mhood_t<another_message> cmd) {
429 * so_5::send_delayed(second, std::chrono::seconds(1), std::move(cmd));
430 * // Note: cmd is nullptr now, it can't be used anymore.
431 * });
432 * \endcode
433 *
434 * \attention
435 * Value of \a pause should be non-negative.
436 *
437 * \since
438 * v.5.5.19
439 */
440 template< typename Target, typename Message >
441 typename std::enable_if< !message_payload_type<Message>::is_signal >::type
send_delayed(Target && to,std::chrono::steady_clock::duration pause,mhood_t<Message> msg)442 send_delayed(
443 //! Destination for the message.
444 Target && to,
445 //! Pause for message delaying.
446 std::chrono::steady_clock::duration pause,
447 //! Message instance owner.
448 mhood_t< Message > msg )
449 {
450 using namespace send_functions_details;
451
452 so_5::low_level_api::single_timer(
453 message_payload_type< Message >::subscription_type_index(),
454 msg.make_reference(),
455 arg_to_mbox( to ),
456 pause );
457 }
458
459 /*!
460 * \brief A utility function for delayed redirection of a signal
461 * from existing message hood.
462 *
463 * \tparam Target a type of destination of the signal. It can be an agent,
464 * a mbox or mchain.
465 * \tparam Message a type of signal to be redirected (it can be
466 * in form of Sig or so_5::immutable_msg<Sig>).
467 *
468 * Usage example:
469 * \code
470 class redirector : public so_5::agent_t {
471 ...
472 void on_some_immutable_signal(mhood_t<some_signal> cmd) {
473 so_5::send_delayed(another_mbox, std::chrono::seconds(1), cmd);
474 ...
475 }
476 };
477 * \endcode
478 * A redirection to the direct mbox of some agent can looks like:
479 * \code
480 * void some_agent::on_some_signal(mhood_t<some_signal> cmd) {
481 * // Redirect to itself but with a pause.
482 * so_5::send_delayed(*this, std::chrono::seconds(2), cmd);
483 * }
484 * \endcode
485 * A redirection to a mchain can looks like:
486 * \code
487 * so_5::mchain_t first = ...;
488 * so_5::mchain_t second = ...;
489 * so_5::receive(so_5::from(first).handle_n(1),
490 * [second](so_5::mhood_t<some_signal> cmd) {
491 * so_5::send_delayed(second, std::chrono::seconds(1), cmd);
492 * });
493 * \endcode
494 *
495 * \attention
496 * Value of \a pause should be non-negative.
497 *
498 * \since
499 * v.5.5.19
500 */
501 template< typename Target, typename Message >
502 typename std::enable_if< message_payload_type<Message>::is_signal >::type
send_delayed(Target && to,std::chrono::steady_clock::duration pause,mhood_t<Message>)503 send_delayed(
504 //! Destination for the message.
505 Target && to,
506 //! Pause for message delaying.
507 std::chrono::steady_clock::duration pause,
508 //! Message instance owner.
509 mhood_t< Message > /*msg*/ )
510 {
511 using namespace send_functions_details;
512
513 so_5::low_level_api::single_timer(
514 message_payload_type< Message >::subscription_type_index(),
515 message_ref_t{},
516 arg_to_mbox( to ),
517 pause );
518 }
519
520 /*!
521 * \brief A version of %send_delayed function for redirection of a message
522 * from exising message_holder instance.
523 *
524 * Usage example:
525 * \code
526 class preallocated_messages_owner final : public so_5::agent_t {
527 so_5::message_holder_t<some_message> first_;
528 so_5::message_holder_t<so_5::mutable_msg<another_message>> second_;
529 ...
530 void on_some_event(mhood_t<some_event>) {
531 // It is time to send preallocated messages.
532
533 // This message will be sent as immutable message.
534 so_5::send_delayed(dest, 15s, first_);
535
536 // This message will be sent as mutable message.
537 so_5::send_delayed(dest, 15s, std::move(second_));
538 }
539 };
540 * \endcode
541 *
542 * \attention
543 * An attempt to call this function for empty message_holder object is UB.
544 *
545 * \since
546 * v.5.6.0
547 */
548 template<
549 typename Target,
550 typename Message,
551 message_ownership_t Ownership >
552 void
send_delayed(Target && to,std::chrono::steady_clock::duration pause,message_holder_t<Message,Ownership> msg)553 send_delayed(
554 //! Destination for the message.
555 Target && to,
556 //! Pause for message delaying.
557 std::chrono::steady_clock::duration pause,
558 //! Message to be sent
559 message_holder_t<Message, Ownership> msg )
560 {
561 using namespace send_functions_details;
562
563 so_5::low_level_api::single_timer(
564 message_payload_type< Message >::subscription_type_index(),
565 msg.make_reference(),
566 arg_to_mbox( to ),
567 pause );
568 }
569
570 /*!
571 * \brief A utility function for creating and delivering a periodic message
572 * to the specified destination.
573 *
574 * Agent or mchain can be used as \a target.
575 *
576 * \note
577 * Message chains with overload control must be used for periodic messages
578 * with additional care: \ref so_5_5_18__overloaded_mchains_and_timers.
579 *
580 * \attention
581 * Values of \a pause and \a period should be non-negative.
582 *
583 * \tparam Message type of message or signal to be sent.
584 * \tparam Target can be so_5::agent_t or so_5::mchain_t.
585 * \tparam Args list of arguments for Message's constructor.
586 *
587 * \since
588 * v.5.5.19
589 */
590 template< typename Message, typename Target, typename... Args >
591 [[nodiscard]] timer_id_t
send_periodic(Target && target,std::chrono::steady_clock::duration pause,std::chrono::steady_clock::duration period,Args &&...args)592 send_periodic(
593 //! A destination for the periodic message.
594 Target && target,
595 //! Pause for message delaying.
596 std::chrono::steady_clock::duration pause,
597 //! Period of message repetitions.
598 std::chrono::steady_clock::duration period,
599 //! Message constructor parameters.
600 Args&&... args )
601 {
602 using namespace send_functions_details;
603
604 return so_5::impl::instantiator_and_sender< Message >::send_periodic(
605 arg_to_mbox( target ),
606 pause,
607 period,
608 std::forward< Args >(args)... );
609 }
610
611 /*!
612 * \brief A utility function for delivering a periodic
613 * from an existing message hood.
614 *
615 * \attention Message must not be a mutable message if \a period is not 0.
616 * Otherwise an exception will be thrown.
617 *
618 * \tparam Target a type of destination of the message. It can be an agent,
619 * a mbox or mchain.
620 * \tparam Message a type of message to be redirected (it can be
621 * in form of Msg, so_5::immutable_msg<Msg> or so_5::mutable_msg<Msg>).
622 *
623 * Usage example:
624 * \code
625 class redirector : public so_5::agent_t {
626 ...
627 void on_some_immutable_message(mhood_t<first_msg> cmd) {
628 timer_id = so_5::send_periodic(another_mbox,
629 std::chrono::seconds(1),
630 std::chrono::seconds(15),
631 cmd);
632 ...
633 }
634
635 void on_some_mutable_message(mhood_t<mutable_msg<second_msg>> cmd) {
636 timer_id = so_5::send_periodic(another_mbox,
637 std::chrono::seconds(1),
638 std::chrono::seconds::zero(), // Note: period is 0!
639 std::move(cmd));
640 // Note: cmd is nullptr now, it can't be used anymore.
641 ...
642 }
643 };
644 * \endcode
645 * A redirection to the direct mbox of some agent can looks like:
646 * \code
647 * void some_agent::on_some_message(mhood_t<some_message> cmd) {
648 * // Redirect to itself but with a pause and a period.
649 * timer_id = so_5::send_periodic(*this,
650 * std::chrono::seconds(2),
651 * std::chrono::seconds(5),
652 * cmd);
653 * }
654 * \endcode
655 * A redirection to a mchain can looks like:
656 * \code
657 * so_5::mchain_t first = ...;
658 * so_5::mchain_t second = ...;
659 * so_5::timer_id_t timer_id;
660 * so_5::receive(so_5::from(first).handle_n(1),
661 * [&](so_5::mhood_t<some_message> cmd) {
662 * timer_id = so_5::send_periodic(
663 * second,
664 * std::chrono::seconds(2),
665 * std::chrono::seconds(5),
666 * cmd);
667 * });
668 * \endcode
669 * \note
670 * Message chains with overload control must be used for periodic messages
671 * with additional care: \ref so_5_5_18__overloaded_mchains_and_timers.
672 *
673 * \attention
674 * Values of \a pause and \a period should be non-negative.
675 *
676 * \since
677 * v.5.5.19
678 */
679 template< typename Target, typename Message >
680 [[nodiscard]] typename std::enable_if< !is_signal< Message >::value, timer_id_t >::type
send_periodic(Target && target,std::chrono::steady_clock::duration pause,std::chrono::steady_clock::duration period,mhood_t<Message> mhood)681 send_periodic(
682 //! A destination for the periodic message.
683 Target && target,
684 //! Pause for message delaying.
685 std::chrono::steady_clock::duration pause,
686 //! Period of message repetitions.
687 std::chrono::steady_clock::duration period,
688 //! Existing message hood for message to be sent.
689 mhood_t< Message > mhood )
690 {
691 using namespace send_functions_details;
692
693 return so_5::low_level_api::schedule_timer(
694 message_payload_type< Message >::subscription_type_index(),
695 mhood.make_reference(),
696 arg_to_mbox( target ),
697 pause,
698 period );
699 }
700
701 /*!
702 * \brief A utility function for periodic redirection of a signal
703 * from existing message hood.
704 *
705 * \tparam Target a type of destination of the signal. It can be an agent,
706 * a mbox or mchain.
707 * \tparam Message a type of signal to be redirected (it can be
708 * in form of Sig or so_5::immutable_msg<Sig>).
709 *
710 * Usage example:
711 * \code
712 class redirector : public so_5::agent_t {
713 ...
714 void on_some_immutable_signal(mhood_t<some_signal> cmd) {
715 timer_id = so_5::send_periodic(another_mbox,
716 std::chrono::seconds(1),
717 std::chrono::seconds(10),
718 cmd);
719 ...
720 }
721 };
722 * \endcode
723 * A redirection to the direct mbox of some agent can looks like:
724 * \code
725 * void some_agent::on_some_message(mhood_t<some_signal> cmd) {
726 * // Redirect to itself but with a pause and a period.
727 * timer_id = so_5::send_periodic(*this,
728 * std::chrono::seconds(2),
729 * std::chrono::seconds(5),
730 * cmd);
731 * }
732 * \endcode
733 * A redirection to a mchain can looks like:
734 * \code
735 * so_5::mchain_t first = ...;
736 * so_5::mchain_t second = ...;
737 * so_5::timer_id_t timer_id;
738 * so_5::receive(so_5::from(first).handle_n(1),
739 * [&](so_5::mhood_t<some_signal> cmd) {
740 * timer_id = so_5::send_periodic(
741 * second,
742 * std::chrono::seconds(2),
743 * std::chrono::seconds(5),
744 * cmd);
745 * });
746 * \endcode
747 * \note
748 * Message chains with overload control must be used for periodic messages
749 * with additional care: \ref so_5_5_18__overloaded_mchains_and_timers.
750 *
751 * \attention
752 * Values of \a pause and \a period should be non-negative.
753 *
754 * \since
755 * v.5.5.19
756 */
757 template< typename Target, typename Message >
758 [[nodiscard]] typename std::enable_if< is_signal< Message >::value, timer_id_t >::type
send_periodic(Target && target,std::chrono::steady_clock::duration pause,std::chrono::steady_clock::duration period,mhood_t<Message>)759 send_periodic(
760 //! A destination for the periodic message.
761 Target && target,
762 //! Pause for message delaying.
763 std::chrono::steady_clock::duration pause,
764 //! Period of message repetitions.
765 std::chrono::steady_clock::duration period,
766 //! Existing message hood for message to be sent.
767 mhood_t< Message > /*mhood*/ )
768 {
769 using namespace send_functions_details;
770
771 return so_5::low_level_api::schedule_timer(
772 message_payload_type< Message >::subscription_type_index(),
773 message_ref_t{},
774 arg_to_mbox( target ),
775 pause,
776 period );
777 }
778
779 /*!
780 * \brief A version of %send_periodic function for redirection of a message
781 * from exising message_holder instance.
782 *
783 * \note
784 * Note that an attempt to call with function with non-zero \a period
785 * argument for a mutable messages will lead to an exception.
786 * It is because mutable message can't be sent as periodic message.
787 *
788 * Usage example:
789 * \code
790 class preallocated_messages_owner final : public so_5::agent_t {
791 so_5::message_holder_t<some_message> msg_;
792 so_5::timer_id_t msg_timer_;
793 ...
794 void on_some_event(mhood_t<some_event>) {
795 // It is time to send preallocated message.
796 // This message will be sent as immutable message.
797 msg_timer_ = so_5::send_periodic(dest, 15s, 20s, msg_);
798 }
799 };
800 * \endcode
801 *
802 * \attention
803 * An attempt to call this function for empty message_holder object is UB.
804 *
805 * \since
806 * v.5.6.0
807 */
808 template<
809 typename Target,
810 typename Message,
811 message_ownership_t Ownership >
812 [[nodiscard]] timer_id_t
send_periodic(Target && target,std::chrono::steady_clock::duration pause,std::chrono::steady_clock::duration period,message_holder_t<Message,Ownership> what)813 send_periodic(
814 //! A destination for the periodic message.
815 Target && target,
816 //! Pause for message delaying.
817 std::chrono::steady_clock::duration pause,
818 //! Period of message repetitions.
819 std::chrono::steady_clock::duration period,
820 //! Message to be sent.
821 message_holder_t<Message, Ownership> what )
822 {
823 using namespace send_functions_details;
824
825 return so_5::low_level_api::schedule_timer(
826 message_payload_type< Message >::subscription_type_index(),
827 what.make_reference(),
828 arg_to_mbox( target ),
829 pause,
830 period );
831 }
832
833 } /* namespace so_5 */
834
835