SObjectizer-5 Extra
retained_msg.hpp
Go to the documentation of this file.
1 /*!
2  * \file
3  * \brief Implementation of mbox which holds last sent message.
4  *
5  * \since
6  * v.1.0.3
7  */
8 
9 #pragma once
10 
11 #include <so_5/version.hpp>
12 
13 #if SO_5_VERSION < SO_5_VERSION_MAKE(5u, 7u, 4u)
14 #error "SObjectizer-5.7.4 of newest is required"
15 #endif
16 
17 #include <so_5_extra/error_ranges.hpp>
18 
19 #include <so_5/impl/agent_ptr_compare.hpp>
20 #include <so_5/impl/message_limit_internals.hpp>
21 #include <so_5/impl/msg_tracing_helpers.hpp>
22 #include <so_5/impl/local_mbox_basic_subscription_info.hpp>
23 
24 #include <so_5/details/sync_helpers.hpp>
25 
26 #include <so_5/mbox.hpp>
27 
28 #include <memory>
29 
30 namespace so_5 {
31 
32 namespace extra {
33 
34 namespace mboxes {
35 
36 namespace retained_msg {
37 
38 namespace errors {
39 
40 //FIXME: has to be removed in v.1.6.0.
41 /*!
42  * \brief An attempt perform service request via retained message mbox.
43  *
44  * \deprecated There is no more such thing as service_request.
45  *
46  * \since
47  * v.1.0.3
48  */
49 [[deprecated]]
52 
53 
54 } /* namespace errors */
55 
56 namespace details {
57 
58 /*!
59  * \brief A helper type which is a collection of type parameters.
60  *
61  * This type is used to simplify code of retained_msg_mbox internals.
62  * Instead of writting something like:
63  * \code
64  * template< typename Traits >
65  * class ... {...};
66  *
67  * template< typename Traits, typename Lock_Type >
68  * class ... {...};
69  * \endcode
70  * this config_type allows to write like that:
71  * \code
72  * template< typename Config_Type >
73  * class ... {...};
74  *
75  * template< typename Config_Type >
76  * class ... {...};
77  * \endcode
78  *
79  * \tparam Traits traits type to be used.
80  *
81  * \tparam Lock_Type type of object to be used for thread-safety (like
82  * std::mutex or so_5::null_mutex_t).
83  *
84  * \since
85  * v.1.0.3
86  */
87 template<
88  typename Traits,
89  typename Lock_Type >
91  {
92  using traits_type = Traits;
93  using lock_type = Lock_Type;
94  };
95 
96 /*!
97  * \name Type extractors for config_type
98  * \{
99  */
100 template< typename Config_Type >
101 using traits_t = typename Config_Type::traits_type;
102 
103 template< typename Config_Type >
104 using lock_t = typename Config_Type::lock_type;
105 /*!
106  * \}
107  */
108 
109 /*!
110  * \brief An information block about one subscriber.
111  *
112  * \since v.1.0.3, v.1.5.1
113  */
115 
116 //
117 // messages_table_item_t
118 //
119 /*!
120  * \brief A type of item of message table for retained message mbox.
121  *
122  * For each message type is necessary to store:
123  * - a list of subscriber for that message;
124  * - the last message sent.
125  *
126  * This type is intended to be used as a container for such data.
127  *
128  * \since
129  * v.1.0.3
130  */
132  {
133  //! A special coparator for agents with respect to
134  //! agent's priority.
136  {
137  bool operator()( agent_t * a, agent_t * b ) const noexcept
138  {
139  return ::so_5::impl::special_agent_ptr_compare( *a, *b );
140  }
141  };
142 
143  //! Type of subscribers map.
144  using subscribers_map_t =
146 
147  //! Subscribers.
148  /*!
149  * Can be empty. This is for case when the first message was sent
150  * when there is no subscribers yet.
151  */
153 
154  //! Retained message.
155  /*!
156  * Can be nullptr. It means that there is no any attempts to send
157  * a message of this type.
158  */
160  };
161 
162 //
163 // template_independent_mbox_data_t
164 //
165 /*!
166  * \brief A mixin with actual data which is necessary for implementation
167  * of retained mbox.
168  *
169  * This data type doesn't depend on any template parameters.
170  *
171  * \since
172  * v.1.0.3
173  */
175  {
176  //! SObjectizer Environment to work in.
178 
179  //! ID of the mbox.
181 
182  //! Type of messages table.
183  using messages_table_t =
185 
186  //! Table of current subscriptions and messages.
188 
190  environment_t & env,
191  mbox_id_t id )
192  : m_env{ env }
193  , m_id{id}
194  {}
195  };
196 
197 //
198 // actual_mbox_t
199 //
200 
201 /*!
202  * \brief An actual implementation of retained message mbox.
203  *
204  * \tparam Config type with main definitions for this message box type.
205  *
206  * \tparam Tracing_Base base class with implementation of message
207  * delivery tracing methods.
208  *
209  * \since
210  * v.1.0.3
211  */
212 template<
213  typename Config,
214  typename Tracing_Base >
216  : public abstract_message_box_t
217  , private Tracing_Base
218  {
219  public:
220  /*!
221  * \brief Initializing constructor.
222  *
223  * \tparam Tracing_Args parameters for Tracing_Base constructor
224  * (can be empty list if Tracing_Base have only the default constructor).
225  */
226  template< typename... Tracing_Args >
228  //! SObjectizer Environment to work in.
229  environment_t & env,
230  //! ID of this mbox.
231  mbox_id_t id,
232  //! Optional parameters for Tracing_Base's constructor.
233  Tracing_Args &&... args )
235  , m_data{ env, id }
236  {}
237 
238  mbox_id_t
239  id() const override
240  {
241  return this->m_data.m_id;
242  }
243 
244  void
246  const std::type_index & msg_type,
247  const so_5::message_limit::control_block_t * limit,
248  agent_t & subscriber ) override
249  {
251  msg_type,
252  subscriber,
253  [&] {
254  return subscriber_info_t{ limit };
255  },
256  [&]( subscriber_info_t & info ) {
257  info.set_limit( limit );
258  } );
259  }
260 
261  void
263  const std::type_index & msg_type,
264  agent_t & subscriber ) override
265  {
267  msg_type,
268  subscriber,
269  []( subscriber_info_t & info ) {
270  info.drop_limit();
271  } );
272  }
273 
274  std::string
275  query_name() const override
276  {
278  s << "<mbox:type=RETAINED_MPMC:id=" << this->m_data.m_id << ">";
279 
280  return s.str();
281  }
282 
284  type() const override
285  {
287  }
288 
289  void
291  const std::type_index & msg_type,
292  const message_ref_t & message,
293  unsigned int overlimit_reaction_deep ) override
294  {
296  *this, // as Tracing_base
297  *this, // as abstract_message_box_t
298  "deliver_message",
300 
302 
304  tracer,
305  msg_type,
306  message,
308  }
309 
310  void
312  const std::type_index & msg_type,
313  const delivery_filter_t & filter,
314  agent_t & subscriber ) override
315  {
317  msg_type,
318  subscriber,
319  [&] {
320  return subscriber_info_t{ &filter };
321  },
322  [&]( subscriber_info_t & info ) {
324  } );
325  }
326 
327  void
329  const std::type_index & msg_type,
330  agent_t & subscriber ) noexcept override
331  {
333  msg_type,
334  subscriber,
335  []( subscriber_info_t & info ) {
336  info.drop_filter();
337  } );
338  }
339 
341  environment() const noexcept override
342  {
343  return this->m_data.m_env;
344  }
345 
346  private :
347  //! Data of this message mbox.
349 
350  //! Object lock.
352 
353  template< typename Info_Maker, typename Info_Changer >
354  void
356  const std::type_index & msg_type,
357  agent_t & subscriber,
358  Info_Maker maker,
359  Info_Changer changer )
360  {
362 
363  // If there is no item for this message type it will be
364  // created automatically.
365  auto & table_item = this->m_data.m_messages_table[ msg_type ];
366 
369  // There is no subscriber yet. It must be added.
371  &subscriber, maker() ).first;
372  else
373  // Subscriber is known. It must be updated.
375 
376  // If there is a retained message then delivery attempt
377  // must be performed.
378  // NOTE: an exception at this stage doesn't remove new subscription.
381  msg_type,
383  subscriber,
385  }
386 
387  template< typename Info_Changer >
388  void
390  const std::type_index & msg_type,
391  agent_t & subscriber,
392  Info_Changer changer )
393  {
395 
397  if( it_table_item != this->m_data.m_messages_table.end() )
398  {
399  auto & table_item = it_table_item->second;
400 
402  &subscriber );
404  {
405  // Subscriber is found and must be modified.
407 
408  // If info about subscriber becomes empty after
409  // modification then subscriber info must be removed.
410  if( it_subscriber->second.empty() )
412  }
413  }
414  }
415 
416  void
418  typename Tracing_Base::deliver_op_tracer const & tracer,
419  const std::type_index & msg_type,
420  const message_ref_t & message,
421  unsigned int overlimit_reaction_deep )
422  {
424 
425  // If there is no item for this message type it will be
426  // created automatically.
427  auto & table_item = this->m_data.m_messages_table[ msg_type ];
428 
429  // Message must be stored as retained.
431 
433  if( !subscribers.empty() )
434  for( const auto & kv : subscribers )
436  *(kv.first),
437  kv.second,
438  tracer,
439  msg_type,
440  message,
442  else
444  }
445 
446  void
448  agent_t & subscriber,
449  const subscriber_info_t & subscriber_info,
450  typename Tracing_Base::deliver_op_tracer const & tracer,
451  const std::type_index & msg_type,
452  const message_ref_t & message,
453  unsigned int overlimit_reaction_deep ) const
454  {
455  const auto delivery_status =
457  subscriber,
458  message,
459  []( const message_ref_t & msg ) -> message_t & {
460  return *msg;
461  } );
462 
464  {
465  using namespace so_5::message_limit::impl;
466 
468  this->m_data.m_id,
469  subscriber,
471  msg_type,
472  message,
475  [&] {
477 
479  subscriber,
481  this->m_data.m_id,
482  msg_type,
483  message );
484  } );
485  }
486  else
489  }
490 
491  /*!
492  * \brief An attempt to deliver retained message to the new subscriber.
493  *
494  * This attempt will be performed only if there is the retained message.
495  */
496  void
498  const std::type_index & msg_type,
499  const message_ref_t & retained_msg,
500  agent_t & subscriber,
501  const subscriber_info_t & subscriber_info )
502  {
503  if( retained_msg )
504  {
505  const unsigned int overlimit_reaction_deep = 0;
506 
508  *this, // as Tracing_base
509  *this, // as abstract_message_box_t
510  "deliver_message_on_subscription",
511  msg_type,
512  retained_msg,
514 
516  subscriber,
518  tracer,
519  msg_type,
520  retained_msg,
522  }
523  }
524 
525  /*!
526  * \brief Ensures that message is an immutable message.
527  *
528  * Checks mutability flag and throws an exception if message is
529  * a mutable one.
530  */
531  void
533  const std::type_index & msg_type,
534  const message_ref_t & what ) const
535  {
540  "an attempt to deliver mutable message via MPMC mbox"
541  ", msg_type=" + std::string(msg_type.name()) );
542  }
543  };
544 
545 } /* namespace details */
546 
547 //
548 //
549 // default_traits_t
550 //
551 /*!
552  * \brief Default traits for retained message mbox.
553  */
555 
556 //
557 // make_mbox
558 //
559 /*!
560  * \brief Create an instance of retained message mbox.
561  *
562  * Simple usage example:
563  * \code
564  * so_5::environment_t & env = ...;
565  * const so_5::mbox_t retained_mbox = so_5::extra::mboxes::retained_msg::make_mbox<>(env);
566  * so_5::send<Some_Message>(retained_mbox, ...);
567  * \endcode
568  * An instance of default implementation retained message mbox will be created.
569  * This instance will be protected by std::mutex.
570  *
571  * If you want to use retained_mbox in a single-threaded environment
572  * without a multithreaded protection then so_5::null_mutex_t (or any
573  * similar null-mutex implementation) can be used:
574  * \code
575  * so_5::environment_t & env = ...
576  * const so_5::mbox_t retained_mbox =
577  * so_5::extra::mboxes::retained_msg::make_mbox<
578  * so_5::extra::mboxes::retained_msg::default_traits_t,
579  * so_5::null_mutex_t>(env);
580  * so_5::send<Some_Message>(retained_mbox, ...);
581  * \endcode
582  *
583  * If you want to use your own mutex-like object (with interface which
584  * allows to use your mutex-like class with std::lock_guard) then you can
585  * do it similar way:
586  * \code
587  * so_5::environment_t & env = ...
588  * const so_5::mbox_t retained_mbox =
589  * so_5::extra::mboxes::retained_msg::make_mbox<
590  * so_5::extra::mboxes::retained_msg::default_traits_t,
591  * Your_Own_Mutex_Class>(env);
592  * so_5::send<Some_Message>(retained_mbox, ...);
593  * \endcode
594  *
595  * \tparam Traits type with traits of mbox implementation.
596  *
597  * \tparam Lock_Type a type of mutex to be used for protection of
598  * retained message mbox content. This must be a DefaultConstructible
599  * type with interface which allows to use Lock_Type with std::lock_guard.
600  *
601  * \since
602  * v.1.0.3
603  */
604 template<
605  typename Traits = default_traits_t,
606  typename Lock_Type = std::mutex >
607 mbox_t
609  {
611 
612  return env.make_custom_mbox(
613  []( const mbox_creation_data_t & data )
614  {
615  mbox_t result;
616 
618  {
619  using T = details::actual_mbox_t<
620  config_type,
622 
623  result = mbox_t{ new T{
625  }
626  };
627  }
628  else
629  {
630  using T = details::actual_mbox_t<
631  config_type,
633  result = mbox_t{ new T{ data.m_env.get(), data.m_id } };
634  }
635 
636  return result;
637  } );
638  }
639 
640 } /* namespace retained_msg */
641 
642 } /* namespace mboxes */
643 
644 } /* namespace extra */
645 
646 } /* namespace so_5 */
A type of item of message table for retained message mbox.
mbox_t make_mbox(environment_t &env)
Create an instance of retained message mbox.
void insert_or_modify_subscriber(const std::type_index &msg_type, agent_t &subscriber, Info_Maker maker, Info_Changer changer)
void try_deliver_retained_message_to(const std::type_index &msg_type, const message_ref_t &retained_msg, agent_t &subscriber, const subscriber_info_t &subscriber_info)
An attempt to deliver retained message to the new subscriber.
template_independent_mbox_data_t m_data
Data of this message mbox.
Ranges for error codes of each submodules.
Definition: details.hpp:13
void unsubscribe_event_handlers(const std::type_index &msg_type, agent_t &subscriber) override
so_5::environment_t & environment() const noexcept override
A mixin with actual data which is necessary for implementation of retained mbox.
void drop_delivery_filter(const std::type_index &msg_type, agent_t &subscriber) noexcept override
messages_table_t m_messages_table
Table of current subscriptions and messages.
An actual implementation of retained message mbox.
A special coparator for agents with respect to agent&#39;s priority.
void modify_and_remove_subscriber_if_needed(const std::type_index &msg_type, agent_t &subscriber, Info_Changer changer)
A helper type which is a collection of type parameters.
void set_delivery_filter(const std::type_index &msg_type, const delivery_filter_t &filter, agent_t &subscriber) override
void do_deliver_message_to_subscriber(agent_t &subscriber, const subscriber_info_t &subscriber_info, typename Tracing_Base::deliver_op_tracer const &tracer, const std::type_index &msg_type, const message_ref_t &message, unsigned int overlimit_reaction_deep) const
void subscribe_event_handler(const std::type_index &msg_type, const so_5::message_limit::control_block_t *limit, agent_t &subscriber) override
Default traits for retained message mbox.
void ensure_immutable_message(const std::type_index &msg_type, const message_ref_t &what) const
Ensures that message is an immutable message.
actual_mbox_t(environment_t &env, mbox_id_t id, Tracing_Args &&... args)
Initializing constructor.
void do_deliver_message_impl(typename Tracing_Base::deliver_op_tracer const &tracer, const std::type_index &msg_type, const message_ref_t &message, unsigned int overlimit_reaction_deep)
environment_t & m_env
SObjectizer Environment to work in.
const int rc_service_request_via_retained_msg_mbox
An attempt perform service request via retained message mbox.
void do_deliver_message(const std::type_index &msg_type, const message_ref_t &message, unsigned int overlimit_reaction_deep) override