SObjectizer-5 Extra
Loading...
Searching...
No Matches
first_last_subscriber_notification.hpp
Go to the documentation of this file.
1/*!
2 * \file
3 * \brief Implementation of mbox that informs about the first and the
4 * last subscriptions.
5 *
6 * \since v.1.5.2
7 */
8
9#pragma once
10
11#include <so_5/version.hpp>
12
13#if SO_5_VERSION < SO_5_VERSION_MAKE(5u, 8u, 0u)
14#error "SObjectizer-5.8.0 of newest is required"
15#endif
16
17#include <so_5_extra/error_ranges.hpp>
18
19#include <so_5/impl/msg_tracing_helpers.hpp>
20#include <so_5/impl/local_mbox_basic_subscription_info.hpp>
21
22#include <so_5/details/sync_helpers.hpp>
23#include <so_5/details/invoke_noexcept_code.hpp>
24
25#include <so_5/mbox.hpp>
26#include <so_5/send_functions.hpp>
27
28#include <string_view>
29
30namespace so_5 {
31
32namespace extra {
33
34namespace mboxes {
35
37
38namespace errors {
39
40/*!
41 * \brief An attempt to use a message type that differs from mbox's message
42 * type.
43 *
44 * Type of message to be used with first_last_subscriber_notification_mbox
45 * is fixed as part of first_last_subscriber_notification_mbox type.
46 * An attempt to use different message type (for subscription, delivery or
47 * setting delivery filter) will lead to an exception with this error code.
48 *
49 * \since v.1.5.2
50 */
53
54/*!
55 * \brief An attempt to add a new subscriber for MPSC mbox when another
56 * subscriber already exists.
57 *
58 * When an instance of first_last_subscriber_notification_mbox is created as
59 * MPSC mbox then only one subscriber can be added. An attempt to add another
60 * subscriber will lead to this error.
61 *
62 * \since v.1.5.2
63 */
66
67} /* namespace errors */
68
69/*!
70 * \brief Signal to be sent when the first subscriber arrives.
71 *
72 * Usage example:
73 * \code
74 * namespace mbox_ns = so_5::extra::mboxes::first_last_subscriber_notification;
75 *
76 * class my_producer final : public so_5::agent_t
77 * {
78 * public:
79 * // Message with published data.
80 * struct msg_data final : public so_5::message_t {...};
81 *
82 * private:
83 * state_t st_no_consumers{ this };
84 * state_t st_consumers_waiting{ this };
85 * ...
86 * const so_5::mbox_t publishing_mbox_;
87 * ...
88 * public:
89 * my_producer( context_t ctx )
90 * : so_5::agent_t{ std::move(ctx) }
91 * // New mbox for publishing produced data has to be created.
92 * , publishing_mbox_{ mbox_ns::make<msg_data>(
93 * so_environment(),
94 * // agent's direct mbox as the target for notifications.
95 * so_direct_mbox(),
96 * so_5::mbox_type_t::multi_producer_multi_consumer )
97 * }
98 * {...}
99 *
100 * void so_define_agent() override
101 * {
102 * st_consumers_waiting
103 * .on_enter{ turn_data_acquisition_on(); }
104 * .on_exit{ turn_data_acquisition_off(); }
105 * .event( &my_producer::evt_last_subscriber )
106 * ;
107 *
108 * st_no_consumers
109 * .event( &my_producer::evt_first_subscriber )
110 * ;
111 * ...
112 *
113 * st_no_consumers.activate();
114 * }
115 *
116 * ...
117 * private:
118 * void evt_first_subscriber( mhood_t< msg_first_subscriber > )
119 * {
120 * st_consumers_waiting.activate();
121 * }
122 *
123 * void evt_last_subscriber( mhood_t< msg_last_subscriber > )
124 * {
125 * st_no_consumers.activate();
126 * }
127 * ...
128 * };
129 * \endcode
130 *
131 * \since v.1.5.2
132 */
133struct msg_first_subscriber final : public so_5::signal_t {};
134
135/*!
136 * \brief Signal to be sent when the last subscriber gone.
137 *
138 * See msg_first_subscriber for usage example.
139 *
140 * \since v.1.5.2
141 */
142struct msg_last_subscriber final : public so_5::signal_t {};
143
144namespace details {
145
146/*!
147 * \brief An information block about one subscriber.
148 *
149 * \since v.1.5.2
150 */
153
154//
155// template_independent_mbox_data_t
156//
157/*!
158 * \brief A mixin with actual data which is necessary for implementation
159 * of actual mbox.
160 *
161 * This data type doesn't depend on any template parameters.
162 *
163 * \since v.1.5.2
164 */
166 {
167 //! A special coparator for sinks with respect to
168 //! sink's priority.
170 {
171 bool operator()(
172 abstract_message_sink_t * a,
173 abstract_message_sink_t * b ) const noexcept
174 {
175 return abstract_message_sink_t::special_sink_ptr_compare( a, b );
176 }
177 };
178
179 //! Type of subscribers map.
184 >;
185
186 //! SObjectizer Environment to work in.
188
189 //! ID of the mbox.
191
192 //! Mbox for notifications about the first/last subscribers.
194
195 //! Type of this mbox (MPMC or MPSC).
197
198 //! Subscribers.
199 /*!
200 * Can be empty.
201 */
203
204 //! Number of actual subscriptions.
205 /*!
206 * \note
207 * There could be cases when a delivery filter is set, but subscription
208 * isn't made yet. Such cases shouldn't be treated as subscriptions.
209 * So we have to store the number of actual subscriptions separately
210 * and don't rely on the size of m_subscribers.
211 */
213
215 environment_t & env,
216 mbox_id_t id,
217 mbox_t notification_mbox,
218 mbox_type_t mbox_type )
219 : m_env{ env }
220 , m_id{ id }
223 {}
224 };
225
226//
227// actual_mbox_t
228//
229
230/*!
231 * \brief An actual implementation of first/last subscriber message mbox.
232 *
233 * \tparam Msg_Type type of message to be used with this mbox.
234 *
235 * \tparam Lock_Type type of lock object to be used for thread safety.
236 *
237 * \tparam Tracing_Base base class with implementation of message
238 * delivery tracing methods.
239 *
240 * \since v.1.5.2
241 */
242template<
243 typename Msg_Type,
244 typename Lock_Type,
245 typename Tracing_Base >
246class actual_mbox_t final
247 : public abstract_message_box_t
248 , private Tracing_Base
249 {
250 public:
251 /*!
252 * \brief Initializing constructor.
253 *
254 * \tparam Tracing_Args parameters for Tracing_Base constructor
255 * (can be empty list if Tracing_Base have only the default constructor).
256 */
257 template< typename... Tracing_Args >
259 //! SObjectizer Environment to work in.
260 environment_t & env,
261 //! ID of this mbox.
262 mbox_id_t id,
263 //! Mbox for notifications about the first/last subscriber.
264 mbox_t notification_mbox,
265 //! Type of this mbox (MPSC or MPMC).
266 mbox_type_t mbox_type,
267 //! Optional parameters for Tracing_Base's constructor.
268 Tracing_Args &&... args )
269 : Tracing_Base{ std::forward< Tracing_Args >(args)... }
270 , m_data{ env, id, std::move(notification_mbox), mbox_type }
271 {
272 // Use of mutable message type for MPMC mbox should be prohibited.
273 if constexpr( is_mutable_message< Msg_Type >::value )
274 {
275 switch( mbox_type )
276 {
277 case mbox_type_t::multi_producer_multi_consumer:
278 SO_5_THROW_EXCEPTION(
279 so_5::rc_mutable_msg_cannot_be_delivered_via_mpmc_mbox,
280 "an attempt to make MPMC mbox for mutable message, "
281 "msg_type=" + std::string(typeid(Msg_Type).name()) );
282 break;
283
284 case mbox_type_t::multi_producer_single_consumer:
285 break;
286 }
287 }
288 }
289
291 id() const override
292 {
293 return this->m_data.m_id;
294 }
295
296 void
298 const std::type_index & msg_type,
299 abstract_message_sink_t & subscriber ) override
300 {
301 ensure_expected_msg_type(
302 msg_type,
303 "an attempt to subscribe with different message type" );
304
305 insert_or_modify_subscriber(
306 subscriber,
307 [] {
308 return subscriber_info_t{
309 subscriber_info_t::subscription_present_t{}
310 };
311 },
312 []( subscriber_info_t & info ) {
313 info.subscription_defined();
314 },
315 [this]() {
316 this->m_data.m_subscriptions_count += 1u;
317 } );
318 }
319
320 void
322 const std::type_index & msg_type,
323 abstract_message_sink_t & subscriber ) noexcept override
324 {
325 // Since v.1.6.0 we can't throw. So perform the main
326 // action only if types are the same.
327 if( msg_type == typeid(Msg_Type) )
328 {
329 modify_and_remove_subscriber_if_needed(
330 subscriber,
331 []( subscriber_info_t & info ) {
332 info.subscription_dropped();
333 },
334 [this]() {
335 this->m_data.m_subscriptions_count -= 1u;
336 } );
337 }
338 }
339
340 std::string
341 query_name() const override
342 {
343 std::ostringstream s;
344 s << "<mbox:type=FIRST_LAST_SUBSCR_NOTIFY";
345
346 switch( this->m_data.m_mbox_type )
347 {
348 case mbox_type_t::multi_producer_multi_consumer:
349 s << "(MPMC)";
350 break;
351
352 case mbox_type_t::multi_producer_single_consumer:
353 s << "(MPSC)";
354 break;
355 }
356
357 s << ":id=" << this->m_data.m_id << ">";
358
359 return s.str();
360 }
361
363 type() const override
364 {
365 return this->m_data.m_mbox_type;
366 }
367
368 void
370 message_delivery_mode_t delivery_mode,
371 const std::type_index & msg_type,
372 const message_ref_t & message,
373 unsigned int redirection_deep ) override
374 {
375 ensure_expected_msg_type(
376 msg_type,
377 "an attempt to deliver with different message type" );
378
379 typename Tracing_Base::deliver_op_tracer tracer{
380 *this, // as Tracing_base
381 *this, // as abstract_message_box_t
382 "deliver_message",
383 delivery_mode,
384 msg_type, message, redirection_deep };
385
386 // NOTE: we don't check for message mutability because
387 // it's impossible to create MPMC mbox for mutable message.
388 // If MPMC mbox was created for immutable message, but a user
389 // tries to send a mutable message then it will be a message
390 // of a different type and the corresponding exception will
391 // be thrown earlier.
392 do_deliver_message_impl(
393 tracer,
394 delivery_mode,
395 msg_type,
396 message,
397 redirection_deep );
398 }
399
400 void
402 const std::type_index & msg_type,
403 const delivery_filter_t & filter,
404 abstract_message_sink_t & subscriber ) override
405 {
406 ensure_expected_msg_type(
407 msg_type,
408 "an attempt to set delivery_filter with "
409 "different message type" );
410
411 insert_or_modify_subscriber(
412 subscriber,
413 [&filter] {
414 return subscriber_info_t{ filter };
415 },
416 [&filter]( subscriber_info_t & info ) {
417 info.set_filter( filter );
418 },
419 []() { /* nothing to do */ } );
420 }
421
422 void
424 const std::type_index & msg_type,
425 abstract_message_sink_t & subscriber ) noexcept override
426 {
427 ensure_expected_msg_type(
428 msg_type,
429 "an attempt to drop delivery_filter with "
430 "different message type" );
431
432 modify_and_remove_subscriber_if_needed(
433 subscriber,
434 []( subscriber_info_t & info ) {
435 info.drop_filter();
436 },
437 []() { /* nothing to do */ } );
438 }
439
441 environment() const noexcept override
442 {
443 return this->m_data.m_env;
444 }
445
446 private :
447 //! Data of this message mbox.
449
450 //! Object lock.
451 Lock_Type m_lock;
452
453 /*!
454 * Throws an error if msg_type differs from Config::msg_type.
455 */
456 static void
458 const std::type_index & msg_type,
459 std::string_view error_description )
460 {
461 if( msg_type != typeid(Msg_Type) )
462 SO_5_THROW_EXCEPTION(
463 errors::rc_different_message_type,
464 error_description );
465 }
466
467 template<
468 typename Info_Maker,
469 typename Info_Changer,
470 typename Post_Action >
471 void
473 abstract_message_sink_t & subscriber,
474 Info_Maker maker,
475 Info_Changer changer,
476 Post_Action post_action )
477 {
478 std::lock_guard< Lock_Type > lock( m_lock );
479
480 auto it_subscriber = this->m_data.m_subscribers.find(
481 std::addressof(subscriber) );
482 if( it_subscriber == this->m_data.m_subscribers.end() )
483 {
484 // There is no subscriber yet. It must be added if
485 // it's possible.
486 ensure_new_item_can_be_added_to_subscribers();
487
488 this->m_data.m_subscribers.emplace(
489 std::addressof(subscriber), maker() );
490 }
491 else
492 // Subscriber is known. It must be updated.
493 changer( it_subscriber->second );
494
495 // All following actions shouldn't throw.
496 so_5::details::invoke_noexcept_code( [this, &post_action]()
497 {
498 // post_action can increment number of actual subscribers so
499 // we have to store the old value before calling post_action.
500 const auto old_subscribers_count =
501 this->m_data.m_subscriptions_count;
502 post_action();
503
504 if( old_subscribers_count < this->m_data.m_subscriptions_count &&
505 1u == this->m_data.m_subscriptions_count )
506 {
507 // We've got the first subscriber.
508 so_5::send< msg_first_subscriber >(
509 this->m_data.m_notification_mbox );
510 }
511 } );
512 }
513
514 template<
515 typename Info_Changer,
516 typename Post_Action >
517 void
519 abstract_message_sink_t & subscriber,
520 Info_Changer changer,
521 Post_Action post_action )
522 {
523 std::lock_guard< Lock_Type > lock( m_lock );
524
525 auto it_subscriber = this->m_data.m_subscribers.find(
526 std::addressof(subscriber) );
527 if( it_subscriber != this->m_data.m_subscribers.end() )
528 {
529 // Subscriber is found and must be modified.
530 changer( it_subscriber->second );
531
532 // If info about subscriber becomes empty after
533 // modification then subscriber info must be removed.
534 if( it_subscriber->second.empty() )
535 this->m_data.m_subscribers.erase( it_subscriber );
536
537 // All following actions shouldn't throw.
538 so_5::details::invoke_noexcept_code( [this, &post_action]()
539 {
540 // post_action can increment number of actual
541 // subscribers so we have to store the old value before
542 // calling post_action.
543 const auto old_subscribers_count =
544 this->m_data.m_subscriptions_count;
545 post_action();
546
547 if( old_subscribers_count > this->m_data.m_subscriptions_count &&
548 0u == this->m_data.m_subscriptions_count )
549 {
550 // We've lost the last subscriber.
551 so_5::send< msg_last_subscriber >(
552 this->m_data.m_notification_mbox );
553 }
554 } );
555 }
556 }
557
558 void
560 typename Tracing_Base::deliver_op_tracer const & tracer,
561 message_delivery_mode_t delivery_mode,
562 const std::type_index & msg_type,
563 const message_ref_t & message,
564 unsigned int redirection_deep )
565 {
566 std::lock_guard< Lock_Type > lock( m_lock );
567
568 auto & subscribers = this->m_data.m_subscribers;
569 if( !subscribers.empty() )
570 for( const auto & kv : subscribers )
571 do_deliver_message_to_subscriber(
572 *(kv.first),
573 kv.second,
574 tracer,
575 delivery_mode,
576 msg_type,
577 message,
578 redirection_deep );
579 else
580 tracer.no_subscribers();
581 }
582
583 void
585 abstract_message_sink_t & subscriber,
586 const subscriber_info_t & subscriber_info,
587 typename Tracing_Base::deliver_op_tracer const & tracer,
588 message_delivery_mode_t delivery_mode,
589 const std::type_index & msg_type,
590 const message_ref_t & message,
591 unsigned int redirection_deep ) const
592 {
593 const auto delivery_status =
594 subscriber_info.must_be_delivered(
595 subscriber,
596 message,
597 []( const message_ref_t & msg ) -> message_t & {
598 return *msg;
599 } );
600
601 if( delivery_possibility_t::must_be_delivered == delivery_status )
602 {
603 using namespace so_5::message_limit::impl;
604
605 subscriber.push_event(
606 this->id(),
607 delivery_mode,
608 msg_type,
609 message,
610 redirection_deep,
611 tracer.overlimit_tracer() );
612 }
613 else
614 tracer.message_rejected(
615 std::addressof(subscriber), delivery_status );
616 }
617
618 void
620 {
621 // If this mbox is MPSC mbox then a new item can be
622 // added to subscribers container only if that container
623 // is empty.
624 // This is true even if new item will hold only delivery_filter,
625 // but not a subscription. It's because there is no sense
626 // to have a delivery_filter for MPSC mbox without having
627 // a subscription.
628 if( (mbox_type_t::multi_producer_single_consumer ==
629 this->m_data.m_mbox_type) &&
630 !this->m_data.m_subscribers.empty() )
631 {
632 SO_5_THROW_EXCEPTION(
633 errors::rc_subscriber_already_exists_for_mpsc_mbox,
634 "subscriber already exists for MPSC mbox, new "
635 "subscriber can't be added" );
636 }
637 }
638 };
639
640} /* namespace details */
641
642//
643// make_mbox
644//
645/*!
646 * \brief Create an instance of first_last_subscriber_notification mbox.
647 *
648 * Usage examples:
649 *
650 * Create a MPMC mbox with std::mutex as Lock_Type (this mbox can safely be
651 * used in multi-threaded environments):
652 * \code
653 * namespace mbox_ns = so_5::extra::mboxes::first_last_subscriber_notification;
654 * so_5::environment_t & env = ...;
655 * auto notification_mbox = env.create_mbox();
656 * auto mbox = mbox_ns::make_mbox<my_message>(
657 * env,
658 * notification_mbox,
659 * so_5::mbox_type_t::multi_producer_multi_consumer);
660 * \endcode
661 *
662 * Create a MPSC mbox with std::mutex as Lock_Type (this mbox can safely be
663 * used in multi-threaded environments):
664 * \code
665 * namespace mbox_ns = so_5::extra::mboxes::first_last_subscriber_notification;
666 * so_5::environment_t & env = ...;
667 * auto notification_mbox = env.create_mbox();
668 * auto mbox = mbox_ns::make_mbox<my_message>(
669 * env,
670 * notification_mbox,
671 * so_5::mbox_type_t::multi_producer_single_consumer);
672 * \endcode
673 *
674 * Create a MPMC mbox with so_5::null_mutex_t as Lock_Type (this mbox can only
675 * be used in single-threaded environments):
676 * \code
677 * namespace mbox_ns = so_5::extra::mboxes::first_last_subscriber_notification;
678 * so_5::environment_t & env = ...;
679 * auto notification_mbox = env.create_mbox();
680 * auto mbox = mbox_ns::make_mbox<my_message, so_5::null_mutex_t>(
681 * env,
682 * notification_mbox,
683 * so_5::mbox_type_t::multi_producer_multi_consumer);
684 * \endcode
685 *
686 * \attention
687 * This type of mbox terminates the whole application if an attempt
688 * to send a notification (in form of msg_first_subscriber and msg_last_subscriber
689 * signals) throws.
690 *
691 * \tparam Msg_Type type of message to be used with a new mbox.
692 *
693 * \tparam Lock_Type type of lock to be used for thread safety. It can be
694 * std::mutex or so_5::null_mutex_t (or any other type which can be used
695 * with std::lock_quard).
696 *
697 * \since v.1.5.2
698 */
699template<
700 typename Msg_Type,
701 typename Lock_Type = std::mutex >
702[[nodiscard]]
703mbox_t
705 //! SObjectizer Environment to work in.
707 //! Mbox for notifications about the first/last subscriber.
709 //! Type of this mbox (MPSC or MPMC).
711 {
712 return env.make_custom_mbox(
714 {
716
718 {
719 using T = details::actual_mbox_t<
720 Msg_Type,
721 Lock_Type,
723
724 result = mbox_t{ new T{
725 data.m_env.get(),
726 data.m_id,
728 mbox_type,
730 } };
731 }
732 else
733 {
734 using T = details::actual_mbox_t<
735 Msg_Type,
736 Lock_Type,
738 result = mbox_t{ new T{
739 data.m_env.get(),
740 data.m_id,
743 } };
744 }
745
746 return result;
747 } );
748 }
749
750//
751// make_multi_consumer_mbox
752//
753/*!
754 * \brief Create an instance of first_last_subscriber_notification MPMC mbox.
755 *
756 * Usage examples:
757 *
758 * Create a MPMC mbox with std::mutex as Lock_Type (this mbox can safely be
759 * used in multi-threaded environments):
760 * \code
761 * namespace mbox_ns = so_5::extra::mboxes::first_last_subscriber_notification;
762 * so_5::environment_t & env = ...;
763 * auto notification_mbox = env.create_mbox();
764 * auto mbox = mbox_ns::make_multi_consumer_mbox<my_message>(
765 * env,
766 * notification_mbox);
767 * \endcode
768 *
769 * \note
770 * It's just a thin wrapper around make_mbox() template function.
771 *
772 * \sa make_mbox
773 *
774 * \since v.1.5.2
775 */
776template<
777 typename Msg_Type,
778 typename Lock_Type = std::mutex >
779[[nodiscard]]
780mbox_t
782 //! SObjectizer Environment to work in.
784 //! Mbox for notifications about the first/last subscriber.
786{
787 return make_mbox< Msg_Type, Lock_Type >(
788 env,
791}
792
793//
794// make_single_consumer_mbox
795//
796/*!
797 * \brief Create an instance of first_last_subscriber_notification MPSC mbox.
798 *
799 * Usage examples:
800 *
801 * Create a MPSC mbox with std::mutex as Lock_Type (this mbox can safely be
802 * used in multi-threaded environments):
803 * \code
804 * namespace mbox_ns = so_5::extra::mboxes::first_last_subscriber_notification;
805 * so_5::environment_t & env = ...;
806 * auto notification_mbox = env.create_mbox();
807 * auto mbox = mbox_ns::make_single_consumer_mbox<my_message>(
808 * env,
809 * notification_mbox);
810 * \endcode
811 *
812 * \note
813 * It's just a thin wrapper around make_mbox() template function.
814 *
815 * \sa make_mbox
816 *
817 * \since v.1.5.2
818 */
819template<
820 typename Msg_Type,
821 typename Lock_Type = std::mutex >
822[[nodiscard]]
823mbox_t
825 //! SObjectizer Environment to work in.
827 //! Mbox for notifications about the first/last subscriber.
829{
830 return make_mbox< Msg_Type, Lock_Type >(
831 env,
834}
835
836} /* namespace first_last_subscriber_notification */
837
838} /* namespace mboxes */
839
840} /* namespace extra */
841
842} /* namespace so_5 */
static void ensure_expected_msg_type(const std::type_index &msg_type, std::string_view error_description)
void set_delivery_filter(const std::type_index &msg_type, const delivery_filter_t &filter, abstract_message_sink_t &subscriber) override
void unsubscribe_event_handler(const std::type_index &msg_type, abstract_message_sink_t &subscriber) noexcept override
void modify_and_remove_subscriber_if_needed(abstract_message_sink_t &subscriber, Info_Changer changer, Post_Action post_action)
void do_deliver_message(message_delivery_mode_t delivery_mode, const std::type_index &msg_type, const message_ref_t &message, unsigned int redirection_deep) override
void drop_delivery_filter(const std::type_index &msg_type, abstract_message_sink_t &subscriber) noexcept override
void do_deliver_message_to_subscriber(abstract_message_sink_t &subscriber, const subscriber_info_t &subscriber_info, typename Tracing_Base::deliver_op_tracer const &tracer, message_delivery_mode_t delivery_mode, const std::type_index &msg_type, const message_ref_t &message, unsigned int redirection_deep) const
void subscribe_event_handler(const std::type_index &msg_type, abstract_message_sink_t &subscriber) override
actual_mbox_t(environment_t &env, mbox_id_t id, mbox_t notification_mbox, mbox_type_t mbox_type, Tracing_Args &&... args)
Initializing constructor.
void do_deliver_message_impl(typename Tracing_Base::deliver_op_tracer const &tracer, message_delivery_mode_t delivery_mode, const std::type_index &msg_type, const message_ref_t &message, unsigned int redirection_deep)
void insert_or_modify_subscriber(abstract_message_sink_t &subscriber, Info_Maker maker, Info_Changer changer, Post_Action post_action)
const int mboxes_first_last_subscriber_notification_errors
Starting point for errors of mboxes::first_last_subscriber_notification submodule.
const int rc_subscriber_already_exists_for_mpsc_mbox
An attempt to add a new subscriber for MPSC mbox when another subscriber already exists.
const int rc_different_message_type
An attempt to use a message type that differs from mbox's message type.
mbox_t make_single_consumer_mbox(environment_t &env, mbox_t notification_mbox)
Create an instance of first_last_subscriber_notification MPSC mbox.
mbox_t make_multi_consumer_mbox(environment_t &env, mbox_t notification_mbox)
Create an instance of first_last_subscriber_notification MPMC mbox.
mbox_t make_mbox(environment_t &env, mbox_t notification_mbox, mbox_type_t mbox_type)
Create an instance of first_last_subscriber_notification mbox.
Ranges for error codes of each submodules.
Definition details.hpp:13
template_independent_mbox_data_t(environment_t &env, mbox_id_t id, mbox_t notification_mbox, mbox_type_t mbox_type)