SObjectizer-5 Extra
unique_subscribers.hpp
Go to the documentation of this file.
1 /*!
2  * \file
3  * \brief Implementation of unique_subscribers mbox.
4  *
5  * \since
6  * v.1.5.0
7  */
8 
9 #pragma once
10 
11 #include <so_5_extra/error_ranges.hpp>
12 
13 #include <so_5/details/sync_helpers.hpp>
14 
15 #include <so_5/mbox.hpp>
16 
17 #include <so_5/impl/agent_ptr_compare.hpp>
18 #include <so_5/impl/message_limit_internals.hpp>
19 #include <so_5/impl/msg_tracing_helpers.hpp>
20 
21 #include <so_5/details/invoke_noexcept_code.hpp>
22 
23 #include <memory>
24 #include <tuple>
25 #include <utility>
26 
27 namespace so_5 {
28 
29 namespace extra {
30 
31 namespace mboxes {
32 
33 namespace unique_subscribers {
34 
35 namespace errors {
36 
37 /*!
38  * \brief An attempt to make another subscription to the same message type.
39  *
40  * This error is reported when there is an existing subscription to
41  * the same message type. And this subscription is made for another agent.
42  *
43  * \since
44  * v.1.5.0
45  */
48 
49 /*!
50  * \brief An attempt to set a delivery filter.
51  *
52  * Delivery filters are not supported by unique_subscribers mbox at the moment.
53  *
54  * \since
55  * v.1.5.0
56  */
59 
60 } /* namespace errors */
61 
62 namespace details {
63 
64 //
65 // subscriber_info_t
66 //
67 /*!
68  * \brief Description of a subscriber.
69  *
70  * \note
71  * It's assumed that the limit will be set only once and will not be
72  * changed after that.
73  *
74  * \since v.1.5.0
75  */
77  {
78  //! Subscriber.
79  /*!
80  * \attention
81  * It's assumed that this pointer can't be null.
82  */
84 
85  //! Message limit for that subscriber.
86  /*!
87  * \note
88  * This pointer can be null. It means that there is no limit.
89  */
91 
92  //! Initializing constructor.
93  /*!
94  * \attention
95  * The constructor doesn't check the validity of \a agent parameter,
96  * it's assumed that it can't be null.
97  */
99  agent_t * agent,
100  const so_5::message_limit::control_block_t * limit )
101  : m_agent{ agent }
102  , m_limit{ limit }
103  {}
104  };
105 
106 //
107 // data_t
108 //
109 
110 /*!
111  * \brief A coolection of data required for local mbox implementation.
112  *
113  * \since v.1.5.0
114  */
115 struct data_t
116  {
117  data_t( mbox_id_t id, environment_t & env )
118  : m_id{ id }
119  , m_env{ env }
120  {}
121 
122  //! ID of this mbox.
124 
125  //! Environment for which the mbox is created.
127 
128  /*!
129  * \brief Map from message type to subscribers.
130  */
132 
133  //! Map of subscribers to messages.
135  };
136 
137 //
138 // actual_mbox_t
139 //
140 
141 //! Actual implementation of unique_subscribers mbox.
142 /*!
143  * \tparam Mutex type of lock to be used for thread safety.
144  * \tparam Tracing_Base base class with implementation of message
145  * delivery tracing methods.
146  *
147  * \since v.1.5.0
148  */
149 template<
150  typename Mutex,
151  typename Tracing_Base >
153  : public abstract_message_box_t
154  , private ::so_5::details::lock_holder_detector< Mutex >::type
155  , private data_t
156  , private Tracing_Base
157  {
158  public:
159  template< typename... Tracing_Args >
161  //! ID of this mbox.
162  mbox_id_t id,
163  //! Environment for which the mbox is created.
164  outliving_reference_t< environment_t > env,
165  //! Optional parameters for Tracing_Base's constructor.
166  Tracing_Args &&... args )
167  : data_t{ id, env.get() }
169  {}
170 
171  mbox_id_t
172  id() const override
173  {
174  return this->m_id;
175  }
176 
177  void
179  const std::type_index & type_wrapper,
180  const so_5::message_limit::control_block_t * limit,
181  agent_t & subscriber ) override
182  {
184  type_wrapper,
186  }
187 
188  void
190  const std::type_index & type_wrapper,
191  agent_t & subscriber ) override
192  {
194  type_wrapper,
195  &subscriber );
196  }
197 
198  std::string
199  query_name() const override
200  {
202  s << "<mbox:type=UNIQUESUBSCRIBERS:id=" << m_id << ">";
203 
204  return s.str();
205  }
206 
208  type() const override
209  {
211  }
212 
213  void
215  const std::type_index & msg_type,
216  const message_ref_t & message,
217  unsigned int overlimit_reaction_deep ) override
218  {
220  *this, // as Tracing_base
221  *this, // as abstract_message_box_t
222  "deliver_message",
224 
226  tracer,
227  msg_type,
228  message,
230  }
231 
232  void
234  const std::type_index & /*msg_type*/,
235  const delivery_filter_t & /*filter*/,
236  agent_t & /*subscriber*/ ) override
237  {
239  "delivery filters can't be used with "
240  "unique_subscribers mboxes" );
241  }
242 
243  void
245  const std::type_index & /*msg_type*/,
246  agent_t & /*subscriber*/ ) noexcept override
247  {
248  // No nothing.
249  }
250 
251  environment_t &
252  environment() const noexcept override
253  {
254  return m_env;
255  }
256 
257  private :
258  void
260  const std::type_index & type_wrapper,
261  subscriber_info_t new_subscriber_info )
262  {
263  this->lock_and_perform( [&] {
264  auto it = this->m_subscribers.find( type_wrapper );
265  if( it == this->m_subscribers.end() )
266  {
267  // There isn't such message type yet.
269  }
270  else
271  {
272  // We assume that if a subscription exists it is made by
273  // different agent (because the current subscriber won't
274  // make the same subscription again).
277  std::string{ "subscription is already exists "
278  "for message type '" }
279  + type_wrapper.name()
280  + "'" );
281  }
282  } );
283  }
284 
285  void
287  const std::type_index & type_wrapper,
288  agent_t * subscriber )
289  {
290  this->lock_and_perform( [&] {
291  auto it = this->m_subscribers.find( type_wrapper );
292  if( it != this->m_subscribers.end() )
293  {
294  auto & subscriber_info = it->second;
295 
296  // Skip all other actions if the subscription is
297  // made for a different agent.
299  {
300  // Subscriber must be removed.
301  this->m_subscribers.erase( it );
302  }
303  }
304  } );
305  }
306 
307  void
309  typename Tracing_Base::deliver_op_tracer const & tracer,
310  const std::type_index & msg_type,
311  const message_ref_t & message,
312  unsigned int overlimit_reaction_deep )
313  {
314  this->lock_and_perform( [&] {
315  auto it = this->m_subscribers.find( msg_type );
316  if( it != this->m_subscribers.end() )
317  {
319  it->second,
320  tracer,
321  msg_type,
322  message,
324  }
325  else
327  } );
328  }
329 
330  void
332  const subscriber_info_t & agent_info,
333  typename Tracing_Base::deliver_op_tracer const & tracer,
334  const std::type_index & msg_type,
335  const message_ref_t & message,
336  unsigned int overlimit_reaction_deep ) const
337  {
338  using namespace so_5::message_limit::impl;
339 
341  this->m_id,
342  *(agent_info.m_agent),
344  msg_type,
345  message,
348  [&] {
350 
352  *(agent_info.m_agent),
354  this->m_id,
355  msg_type,
356  message );
357  } );
358  }
359  };
360 
361 } /* namespace details */
362 
363 //
364 // make_mbox
365 //
366 /*!
367  * \brief Factory function for creation of a new instance of unique_subscribers
368  * mbox.
369  *
370  * Usage examples:
371  *
372  * Create a mbox with std::mutex as Lock_Type (this mbox can safely be
373  * used in multi-threaded environments):
374  * \code
375  * so_5::environment_t & env = ...;
376  * auto mbox = so_5::extra::mboxes::unique_subscribers::make_mbox(env);
377  * \endcode
378  *
379  * Create a mbox with so_5::null_mutex_t as Lock_Type (this mbox can only
380  * be used in single-threaded environments):
381  * \code
382  * so_5::environment_t & env = ...;
383  * auto mbox = so_5::extra::mboxes::unique_subscribers::make_mbox<so_5::null_mutex_t>(env);
384  * \endcode
385  *
386  * \tparam Lock_Type type of lock to be used for thread safety. It can be
387  * std::mutex or so_5::null_mutex_t (or any other type which can be used
388  * with std::lock_quard).
389  *
390  * \since v.1.5.0
391  */
392 template<
393  typename Lock_Type = std::mutex >
394 [[nodiscard]]
395 mbox_t
397  {
398  return env.make_custom_mbox(
399  [&]( const mbox_creation_data_t & data ) {
400  mbox_t result;
401 
403  {
404  using T = details::actual_mbox_t<
405  Lock_Type,
407 
408  result = mbox_t{ new T{
409  data.m_id,
410  data.m_env,
411  data.m_tracer.get()
412  } };
413  }
414  else
415  {
416  using T = details::actual_mbox_t<
417  Lock_Type,
419  result = mbox_t{ new T{
420  data.m_id,
421  data.m_env
422  } };
423  }
424 
425  return result;
426  } );
427  }
428 
429 } /* namespace unique_subscribers */
430 
431 } /* namespace mboxes */
432 
433 } /* namespace extra */
434 
435 } /* namespace so_5 */
void try_insert_subscriber(const std::type_index &type_wrapper, subscriber_info_t new_subscriber_info)
Actual implementation of unique_subscribers mbox.
subscriber_info_t(agent_t *agent, const so_5::message_limit::control_block_t *limit)
Initializing constructor.
void remove_subscriber_if_needed(const std::type_index &type_wrapper, agent_t *subscriber)
const int rc_subscription_exists
An attempt to make another subscription to the same message type.
void unsubscribe_event_handlers(const std::type_index &type_wrapper, agent_t &subscriber) override
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)
void do_deliver_message(const std::type_index &msg_type, const message_ref_t &message, unsigned int overlimit_reaction_deep) override
Ranges for error codes of each submodules.
Definition: details.hpp:13
messages_table_t m_subscribers
Map of subscribers to messages.
const int rc_delivery_filters_not_supported
An attempt to set a delivery filter.
void drop_delivery_filter(const std::type_index &, agent_t &) noexcept override
mbox_t make_mbox(so_5::environment_t &env)
Factory function for creation of a new instance of unique_subscribers mbox.
void set_delivery_filter(const std::type_index &, const delivery_filter_t &, agent_t &) override
actual_mbox_t(mbox_id_t id, outliving_reference_t< environment_t > env, Tracing_Args &&... args)
A coolection of data required for local mbox implementation.
void do_deliver_message_to_subscriber(const subscriber_info_t &agent_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
environment_t & m_env
Environment for which the mbox is created.
const so_5::message_limit::control_block_t * m_limit
Message limit for that subscriber.
void subscribe_event_handler(const std::type_index &type_wrapper, const so_5::message_limit::control_block_t *limit, agent_t &subscriber) override