SObjectizer-5 Extra
Loading...
Searching...
No Matches
shutdowner.hpp
Go to the documentation of this file.
1/*!
2 * \file
3 * \brief Implementation of shutdowner-related stuff.
4 */
5
6#pragma once
7
8#include <so_5_extra/error_ranges.hpp>
9
10#include <so_5/impl/msg_tracing_helpers.hpp>
11
12#include <so_5/details/sync_helpers.hpp>
13
14#include <so_5/mbox.hpp>
15#include <so_5/send_functions.hpp>
16
17#include <so_5/outliving.hpp>
18
19namespace so_5 {
20
21namespace extra {
22
23namespace shutdowner {
24
25namespace errors {
26
27/*!
28 * \brief An attempt to use illegal message type.
29 *
30 * For example: shutdowner mbox allows subscription only to
31 * shutdown_initiated message.
32 */
35
36/*!
37 * \brief Subscription to shutdowner mbox when shutdown in progess
38 * is prohibited.
39 */
42
43} /* namespace errors */
44
45/*!
46 * \brief A message to be used to inform about start of shutdown operation.
47 *
48 * \note
49 * This is a message, not a signal. This message is empty now but it
50 * can be extended in future versions of so_5_extra.
51 */
53
54namespace details {
55
56/*!
57 * \brief Implementation of stop_guard for shutdowner's purposes.
58 *
59 * This implementation sends shutdown_initiated_t message to the specified
60 * mbox.
61 */
63 {
64 //! Mbox to which shutdown_initiated must be sent.
66
67 public :
68 //! Initializing constructor.
69 shutdowner_guard_t( so_5::mbox_t notify_mbox )
71 {}
72
73 virtual void
74 stop() noexcept override
75 {
76 so_5::send< shutdown_initiated_t >( m_notify_mbox );
77 }
78 };
79
80/*!
81 * \brief A signal which is used to limit time of shutdown operation.
82 */
83struct shutdown_time_elapsed_t final : public so_5::message_t {};
84
85/*!
86 * \brief Special mbox to receive and handle a signal about time limit.
87 *
88 * This mbox implements just one method: do_deliver_message(). A std::abort()
89 * is called in this method if shutdown operation is not finished yet.
90 */
93 {
94 public :
95 //! Initializing constructor.
97 //! SObjectizer environment to work in.
98 //! It is necessary to access error_logging before calling std::abort().
100 //! Unique ID of this mbox.
101 mbox_id_t id )
102 : m_env( env )
103 , m_id( id )
104 {}
105
107 id() const override
108 {
109 return m_id;
110 }
111
112 void
114 const std::type_index & /*type_index*/,
115 abstract_message_sink_t & /*subscriber*/ ) override
116 {
118 "subscribe_event_handler is not implemented for "
119 "time_elapsed_mbox" );
120 }
121
122 void
124 const std::type_index & /*type_index*/,
125 abstract_message_sink_t & /*subscriber*/ ) noexcept override
126 {
127 // Can't throw in noexcept method.
128 }
129
130 std::string
132 {
134 ss << "<mbox:type=MPSC:shutdowner_time_elapsed:id=" << m_id << ">";
135 return ss.str();
136 }
137
140 {
142 }
143
144 void
146 message_delivery_mode_t /*delivery_mode*/,
147 const std::type_index & /*msg_type*/,
148 const message_ref_t & /*message*/,
149 unsigned int /*overlimit_reaction_deep*/ ) override
150 {
152 __FILE__, __LINE__,
153 "Time of shutdown operation is elapsed. "
154 "Application will be terminated." );
155 std::abort();
156 }
157
158 void
160 const std::type_index & /*msg_type*/,
161 const delivery_filter_t & /*filter*/,
162 abstract_message_sink_t & /*subscriber*/ ) override
163 {
165 "set_delivery_filter is not implemented for "
166 "time_elapsed_mbox" );
167 }
168
169 void
171 const std::type_index & /*msg_type*/,
172 abstract_message_sink_t & /*subscriber*/ ) noexcept override
173 {
174 /* Nothing to do. */
175 }
176
178 environment() const noexcept override
179 {
180 return m_env.get();
181 }
182
183 private :
184 //! SOEnv to work in.
186 //! Unique ID of that mbox.
188 };
189
190//
191// subscriber_info_t
192//
193/*!
194 * \brief Description of one subscriber.
195 */
197 {
198 //! Actual subscriber.
200 //! Message limit for that subscriber.
201
202 //! Initializing constructor.
204 abstract_message_sink_t & sink )
205 : m_sink(sink)
206 {}
207
208 //! Comparison operator.
209 /*!
210 * Compares only pointers to m_subscriber with respects
211 * to agent's priority.
212 */
213 bool
214 operator<( const subscriber_info_t & o ) const
215 {
216 return abstract_message_sink_t::special_sink_ptr_compare(
217 std::addressof( this->m_sink.get() ),
218 std::addressof( o.m_sink.get() ) );
219 }
220 };
221
222//
223// subscriber_container_t
224//
225/*!
226 * \brief Type of subscriber's container.
227 */
229
230namespace status {
231
232//! Avaliable statuses of shutdown operation.
233enum class value_t
234 {
235 //! Shutdown is not started yet.
237 //! Shutdown is started but there are some pending subscribers.
238 started,
239 //! All subscribers are unsubscribed.
240 //! Shutdown can and should be completed.
242 };
243
247
248//! Which action must be performed after updating status of shutdown operation.
250 {
251 //! No action is required.
253 //! Shutdown action must be completed.
255 };
256
257//! Special object which holds status.
258/*!
259 * Updates for the status are enabled only to instances of updater_t class.
260 */
262 {
263 friend class updater_t;
264
266
267 public :
268 value_t
269 current() const noexcept { return m_status; }
270 };
271
272//! Special object to change the status and detect deferred action to be performed.
274 {
277
278 public :
280 outliving_reference_t< holder_t > status )
281 : m_status( status )
282 {}
283
284 value_t
285 current() const noexcept { return m_status.get().current(); }
286
287 void
296
298 action() const noexcept { return m_action; }
299 };
300
301} /* namespace status */
302
303//
304// notify_mbox_data_t
305//
306/*!
307 * \brief An internal data of notify_mbox.
308 *
309 * It is collected in separate struct to provide ability to mark
310 * object of that struct as mutable. It is necessary because some
311 * of abstract_message_box_t's methods are declared as const by
312 * historical reasons.
313 */
315{
316 //! Status of the shutdown operation.
318
319 //! List of actual subscribers.
321
322 //! Mbox to be used for delayed shutdown_time_elapsed message.
324 //! A time for shutdown operation.
326
327 //! Timer ID for shutdown_time_elapsed message.
328 /*!
329 * Will be used for canceling of delayed message when shutdown is
330 * completed.
331 *
332 * Will receive actual value only when shutdown operation started.
333 */
335
336 //! Initializing constructor.
338 mbox_t time_elapsed_mbox,
339 std::chrono::steady_clock::duration max_shutdown_time )
342 {}
343};
344
345//
346// notify_mbox_t
347//
348/*!
349 * \brief A special mbox which must be used for notification about
350 * shutdown operation.
351 *
352 * \tparam Lock_Type type of lock to be used for thread safety
353 * \tparam Tracing_Base type that implements message tracing stuff
354 * It is expected to be so_5::impl::msg_tracing_helpers::tracing_enabled_base
355 * or so_5::impl::msg_tracing_helpers::tracing_disabled_base
356 */
357template< typename Lock_Type, typename Tracing_Base >
361 , private Tracing_Base
362 {
363 public :
364 template< typename... Tracing_Base_Args >
366 //! SObjectizer Environment to work in.
368 //! A mbox for delayed message shutdown_time_elapsed_t.
370 //! Time for the whole shutdown operation.
372 //! Unique ID of that mbox.
374 //! Params for tracing stuff.
377 , m_env( env )
378 , m_id( id )
379 , m_data( std::move(time_elapsed_mbox), max_shutdown_time )
380 {
381 // Now we can create and install shutdowner_guard to
382 // prevent SObjectizer from shutdown.
383 auto guard = std::make_shared< shutdowner_guard_t >( mbox_t(this) );
386 }
387
389 id() const override
390 {
391 return m_id;
392 }
393
394 void
404
405 void
418
419 std::string
421 {
423 ss << "<mbox:type=MPMC:shutdowner:id=" << m_id << ">";
424 return ss.str();
425 }
426
429 {
431 }
432
433 void
446
447 void
449 const std::type_index & msg_type,
450 const delivery_filter_t & /*filter*/,
451 abstract_message_sink_t & /*subscriber*/ ) override
452 {
455 "unable to set delivery filter to shutdowner mbox" );
456 }
457
458 void
460 const std::type_index & /*msg_type*/,
461 abstract_message_sink_t & /*subscriber*/ ) noexcept override
462 {
463 /* Nothing to do. */
464 }
465
467 environment() const noexcept override
468 {
469 return m_env.get();
470 }
471
472 private :
473 //! SObjectizer Environment to work in.
475
476 //! Stop_guard which prevents SObjectizer from shutdown.
478
479 //! Unique ID of that mbox.
481
482 //! Actual mbox data.
484
485 //! Check for valid type of message to be handled.
486 /*!
487 * \note
488 * Only shutdown_initiated_t message can be handled by that mbox type.
489 */
490 static void
491 ensure_valid_message_type( const std::type_index & type_index )
492 {
493 if( type_index != typeid(shutdown_initiated_t) )
496 "only shutdown_initiated_t message type is allowed to "
497 "be used with shutdowner mbox" );
498 }
499
500 //! Main subscription actions.
501 /*!
502 * May throw if shutdown is in progress or already completed.
503 */
504 void
506 abstract_message_sink_t & subscriber )
507 {
511 "a creation of new subscription is disabled during "
512 "shutdown procedure" );
513
515 auto it = std::lower_bound(
518 info );
520 }
521
522 //! Main unsubscription actions.
523 /*!
524 * Returns the action to be performed (shutdown completion may be
525 * necessary).
526 */
529 abstract_message_sink_t & subscriber )
530 {
533
535 auto it = std::lower_bound(
538 info );
539 if( it != std::end(m_data.m_subscribers) &&
541 {
543
545 {
547 {
550 }
551 }
552 }
553
554 return status_updater.action();
555 }
556
557 //! Do all necessary actions for completion of shutdown.
558 void
565
566 //! Do all necessary actions for start of shutdown operation.
567 /*!
568 * Returns the action to be performed (shutdown completion may be
569 * necessary).
570 */
573 message_delivery_mode_t delivery_mode,
574 const std::type_index & msg_type,
575 const message_ref_t & message )
576 {
579
581 {
583 if( !m_data.m_subscribers.empty() )
584 {
588 }
589 else
590 {
592 }
593 }
594
595 return updater.action();
596 }
597
598 //! Send shutdown_initiated message to all actual subscribers.
599 void
601 message_delivery_mode_t delivery_mode,
602 const std::type_index & msg_type,
603 const message_ref_t & message )
604 {
605 constexpr unsigned int redirection_deep = 0u;
606
608 *this, // as Tracing_Base
609 *this, // as abstract_message_box_t
610 "deliver_message",
613
614 for( const auto & subscriber : m_data.m_subscribers )
615 {
617 this->id(),
619 msg_type,
620 message,
623 }
624 }
625
626 //! Initiate delayed shutdown_time_elapsed message.
627 void
635 };
636
637} /* namespace details */
638
639//
640// layer_t
641//
642/*!
643 * \brief An interface of shutdowner layer.
644 *
645 * This is a public interface of actual layer. An user should use only
646 * that interface when it want to work with shutdown layer.
647 *
648 * For example, to subscribe to shutdown_initiated_t message it is neccessary
649 * to receive a pointer/reference to layer_t and call notify_mbox() method:
650 * \code
651 * // To make a subscription to shutdown notification.
652 * class my_agent final : public so_5::agent_t {
653 * public :
654 * my_agent(context_t ctx) : so_5::agent_t(std::move(ctx)) {
655 * // Long way:
656 * so_5::extra::shutdowner::layer_t * s =
657 * so_environment().query_layer<so_5::extra::shutdowner::layer_t>();
658 * so_subscribe(s->notify_mbox()).event(&my_agent::on_shutdown);
659 *
660 * // Shortest way:
661 * so_subscribe(so_5::extra::shutdowner::layer(so_environment()).notify_mbox())
662 * .event(&my_agent::on_shutdown);
663 * ...
664 * }
665 * ...
666 * private :
667 * void on_shutdown(mhood_t<so_5::extra::shutdowner::shutdown_initiated_t>)
668 * {...}
669 * };
670 * \endcode
671 *
672 * To initiate shutdown it is necessary to call so_5::environment_t::stop()
673 * method:
674 * \code
675 * so_environment().stop();
676 * \endcode
677 */
678class layer_t : public ::so_5::layer_t
679 {
680 public :
681 //! Get a mbox which can be used for subscription to
682 //! shutdown_initiated message.
683 virtual mbox_t
684 notify_mbox() const = 0;
685 };
686
687namespace details {
688
689//
690// layer_template_t
691//
692/*!
693 * \brief A template-based implementation of shutdowner layer.
694 *
695 * \note
696 * It creates all shutdowner-related stuff in start() method.
697 */
698template< typename Lock_Type >
700 {
701 public :
702 //! Initializing constructor.
704 //! Maximum time for the shutdown operation.
705 std::chrono::steady_clock::duration shutdown_time )
707 {}
708
709 virtual mbox_t
710 notify_mbox() const override
711 {
712 return m_notify_mbox;
713 }
714
715 virtual void
716 start() override
717 {
719 [&]( const mbox_creation_data_t & data ) {
721 } );
722
724 [&]( const mbox_creation_data_t & data ) {
726 data,
728 } );
729 }
730
731 private :
732 //! Maximum time for the shutdown operation.
734
735 //! Notification mbox.
736 /*!
737 * Will be created in start() method. Until then it is a null pointer.
738 */
740
741 //! Create mbox for delayed shutdown_time_elapsed message.
742 mbox_t
744 //! Parameters for the new mbox.
745 const mbox_creation_data_t & data )
746 {
749 data.m_id ) );
750 }
751
752 //! Create notification mbox.
753 /*!
754 * A new mbox will be created with respect to message tracing stuff.
755 */
756 mbox_t
758 //! Parameters for the new mbox.
760 //! A mbox to be used for delayed shutdown_time_elapsed message.
762 {
764
766 {
767 using T = details::notify_mbox_t<
768 Lock_Type,
770 result = mbox_t( new T(
774 data.m_id,
775 data.m_tracer ) );
776 }
777 else
778 {
779 using T = details::notify_mbox_t<
780 Lock_Type,
782 result = mbox_t( new T(
786 data.m_id ) );
787 }
788
789 return result;
790 }
791 };
792
793} /* namespace details */
794
795//
796// make_layer
797//
798/*!
799 * \brief Main function to create an instance of shutdowner layer.
800 *
801 * Usage example:
802 * \code
803 * // Creation of layer with default mutex type.
804 * so_5::launch([](so_5::environment_t & env) {...},
805 * [](so_5::environment_params_t & params) {
806 * params.add_layer( so_5::extra::shutdowner::make_layer<>(30s) );
807 * });
808 *
809 * // Creation of layer with user-provided spinlock type.
810 * so_5::launch([](so_5::environment_t & env) {...},
811 * [](so_5::environment_params_t & params) {
812 * params.add_layer( so_5::extra::shutdowner::make_layer<my_spinlock_t>(30s));
813 * });
814 *
815 * // Creation of layer with null_mutex.
816 * // Note: null_mutex must be used only for non thread-safe environments.
817 * so_5::launch([](so_5::environment_t & env) {...},
818 * [](so_5::environment_params_t & params) {
819 * // Use single-threaded and not thread-safe environment.
820 * params.infrastructure_factory(
821 * so_5::env_infrastructures::simple_not_mtsafe::factory());
822 * // Shutdowner layer with null_mutex can be used in that environment.
823 * params.add_layer( so_5::extra::shutdowner::make_layer<so_5::null_mutex_t>(30s));
824 * });
825 * \endcode
826 *
827 * \tparam Lock_Type type of lock to be used for thread safety.
828 */
829template< typename Lock_Type = std::mutex >
832 //! A maximum time for timeout operation.
834 {
838
839 return result;
840 }
841
842//
843// layer
844//
845/*!
846 * \brief A helper function to receive a reference to shutdowner layer.
847 *
848 * Usage example:
849 * \code
850 * // To make a subscription to shutdown notification.
851 * class my_agent final : public so_5::agent_t {
852 * public :
853 * my_agent(context_t ctx) : so_5::agent_t(std::move(ctx)) {
854 * auto & s = so_5::extra::shutdowner::layer( so_environment() );
855 * so_subscribe( s.notify_mbox() ).event( &my_agent::on_shutdown );
856 * ...
857 * }
858 * ...
859 * private :
860 * void on_shutdown(mhood_t<so_5::extra::shutdowner::shutdown_initiated_t>)
861 * {...}
862 * };
863 *
864 * // To initiate shutdown.
865 * so_5::extra::shutdowner::layer(env).initiate_shutdown();
866 * \endcode
867 *
868 * \note May throw an exception if shutdowner layer is not defined.
869 */
870inline ::so_5::extra::shutdowner::layer_t &
871layer( ::so_5::environment_t & env )
872 {
873 return *(env.query_layer< ::so_5::extra::shutdowner::layer_t >());
874 }
875
876} /* namespace shutdowner */
877
878} /* namespace extra */
879
880} /* namespace so_5 */
A template-based implementation of shutdowner layer.
mbox_t do_make_time_elapsed_mbox(const mbox_creation_data_t &data)
Create mbox for delayed shutdown_time_elapsed message.
layer_template_t(std::chrono::steady_clock::duration shutdown_time)
Initializing constructor.
const std::chrono::steady_clock::duration m_shutdown_time
Maximum time for the shutdown operation.
virtual mbox_t notify_mbox() const override
Get a mbox which can be used for subscription to shutdown_initiated message.
mbox_t do_make_notification_mbox(const mbox_creation_data_t &data, mbox_t time_elapsed_mbox)
Create notification mbox.
A special mbox which must be used for notification about shutdown operation.
stop_guard_shptr_t m_shutdowner_guard
Stop_guard which prevents SObjectizer from shutdown.
static void ensure_valid_message_type(const std::type_index &type_index)
Check for valid type of message to be handled.
notify_mbox_t(outliving_reference_t< environment_t > env, mbox_t time_elapsed_mbox, std::chrono::steady_clock::duration max_shutdown_time, mbox_id_t id, Tracing_Base_Args &&...tracing_args)
void send_shutdown_initated_to_all(message_delivery_mode_t delivery_mode, const std::type_index &msg_type, const message_ref_t &message)
Send shutdown_initiated message to all actual subscribers.
status::deferred_action_t do_event_unsubscripton(abstract_message_sink_t &subscriber)
Main unsubscription actions.
void do_event_subscription(abstract_message_sink_t &subscriber)
Main subscription actions.
void complete_shutdown()
Do all necessary actions for completion of shutdown.
void start_shutdown_clock()
Initiate delayed shutdown_time_elapsed message.
status::deferred_action_t do_initiate_shutdown(message_delivery_mode_t delivery_mode, const std::type_index &msg_type, const message_ref_t &message)
Do all necessary actions for start of shutdown operation.
Implementation of stop_guard for shutdowner's purposes.
const so_5::mbox_t m_notify_mbox
Mbox to which shutdown_initiated must be sent.
shutdowner_guard_t(so_5::mbox_t notify_mbox)
Initializing constructor.
Special object to change the status and detect deferred action to be performed.
outliving_reference_t< holder_t > m_status
Special mbox to receive and handle a signal about time limit.
time_elapsed_mbox_t(outliving_reference_t< environment_t > env, mbox_id_t id)
Initializing constructor.
An interface of shutdowner layer.
virtual mbox_t notify_mbox() const =0
Get a mbox which can be used for subscription to shutdown_initiated message.
const int shutdowner_errors
Starting point for errors of shutdowner submodule.
deferred_action_t
Which action must be performed after updating status of shutdown operation.
@ complete_shutdown
Shutdown action must be completed.
constexpr const value_t must_be_completed
value_t
Avaliable statuses of shutdown operation.
@ must_be_completed
All subscribers are unsubscribed. Shutdown can and should be completed.
@ started
Shutdown is started but there are some pending subscribers.
const int rc_subscription_disabled_during_shutdown
Subscription to shutdowner mbox when shutdown in progess is prohibited.
const int rc_illegal_msg_type
An attempt to use illegal message type.
std::unique_ptr< ::so_5::extra::shutdowner::layer_t > make_layer(std::chrono::steady_clock::duration shutdown_max_time)
Main function to create an instance of shutdowner layer.
inline ::so_5::extra::shutdowner::layer_t & layer(::so_5::environment_t &env)
A helper function to receive a reference to shutdowner layer.
Ranges for error codes of each submodules.
Definition details.hpp:13
status::holder_t m_status
Status of the shutdown operation.
notify_mbox_data_t(mbox_t time_elapsed_mbox, std::chrono::steady_clock::duration max_shutdown_time)
Initializing constructor.
mbox_t m_time_elapsed_mbox
Mbox to be used for delayed shutdown_time_elapsed message.
timer_id_t m_shutdown_timer
Timer ID for shutdown_time_elapsed message.
const std::chrono::steady_clock::duration m_max_shutdown_time
A time for shutdown operation.
subscriber_container_t m_subscribers
List of actual subscribers.
bool operator<(const subscriber_info_t &o) const
Comparison operator.
std::reference_wrapper< abstract_message_sink_t > m_sink
Actual subscriber.
subscriber_info_t(abstract_message_sink_t &sink)
Message limit for that subscriber.
A message to be used to inform about start of shutdown operation.