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