SObjectizer-5 Extra
pub.hpp
Go to the documentation of this file.
1 /*!
2  * \file
3  * \brief Implementation of revocable messages.
4  *
5  * \since
6  * v.1.2.0
7  */
8 
9 #pragma once
10 
11 #include <so_5/h/version.hpp>
12 
13 #if (SO_5_VERSION < SO_5_VERSION_MAKE(5, 23, 0))
14  #error "SObjectizer v.5.5.23 or above is required"
15 #endif
16 
17 #include <so_5_extra/error_ranges.hpp>
18 
19 #include <so_5/rt/h/enveloped_msg.hpp>
20 #include <so_5/rt/h/send_functions.hpp>
21 
22 #include <atomic>
23 
24 namespace so_5 {
25 
26 namespace extra {
27 
28 namespace revocable_msg {
29 
30 namespace errors {
31 
32 //! Mutability of envelope for revocable message can't be changed.
33 /*!
34  * An envelope for a revocable message inherits mutability flag
35  * from its payload. It means that mutability should be set for payload
36  * first and it can't be changed after enveloping the payload into
37  * the special envelope.
38  *
39  * \since
40  * v.1.2.0
41  */
44 
45 //! An attempt to envelope service request.
46 /*!
47  * Special envelope type, so_5::extra::revocable_msg::details::envelope_t
48  * should be used only with messages, signals and envelopes.
49  * Service requests can't be enveloped by this type of envelope.
50  *
51  * \since
52  * v.1.2.0
53  */
56 
57 } /* namespace errors */
58 
59 namespace details {
60 
61 //
62 // envelope_t
63 //
64 /*!
65  * \brief A special envelope to be used for revocable messages.
66  *
67  * This envelope uses an atomic flag. When this flag is set to \a true
68  * the message becomes _revoked_. Value of this flag is checked in
69  * access_hook(). If the message if revoked that handler do nothing.
70  *
71  * \note
72  * This class is intended to be used with invocation_type_t::event
73  * and invocation_type_t::enveloped_msg.
74  * Service requests are not supported.
75  *
76  * \since
77  * v.1.2.0
78  */
79 class envelope_t final : public so_5::enveloped_msg::envelope_t
80  {
81  //! Has message been revoked?
83 
84  //! Message to be delivered.
86 
87  void
89  handler_invoker_t & invoker ) const noexcept
90  {
91  if( !has_been_revoked() )
92  {
93  // Message is not revoked yet.
94  // Message handler can be called.
95  invoker.invoke( payload_info_t{ m_payload } );
96  }
97  // Otherwise message should be ignored.
98  }
99 
100  // Special check for kind of the payload.
101  static auto
102  ensure_right_payload_kind( so_5::message_ref_t payload )
103  {
104  switch( message_kind( payload ) )
105  {
106  case message_kind_t::signal: break;
107  case message_kind_t::classical_message: break;
108  case message_kind_t::user_type_message: break;
109  case message_kind_t::service_request:
110  SO_5_THROW_EXCEPTION(
111  errors::rc_invalid_payload_kind,
112  "service requests can't be enveloped in "
113  "revocable_msg::details::envelope_t" );
114  case message_kind_t::enveloped_msg: break;
115  }
116  return payload;
117  }
118 
119  // Mutability of payload will be returned as mutability
120  // of the whole envelope.
122  so5_message_mutability() const override
123  {
124  return message_mutability( m_payload );
125  }
126 
127  // Disables changing of mutability by throwing an exception.
128  void
130  message_mutability_t ) override
131  {
132  SO_5_THROW_EXCEPTION(
133  so_5::extra::revocable_msg::errors
134  ::rc_mutabilty_of_envelope_cannot_be_changed,
135  "revocable_msg's envelope prohibit changing of "
136  "message mutability" );
137  }
138 
139  public :
140  envelope_t( so_5::message_ref_t payload )
142  {}
143 
144  void
145  revoke() noexcept
146  {
147  m_revoked.store( true, std::memory_order_release );
148  }
149 
150  bool
151  has_been_revoked() const noexcept
152  {
153  return m_revoked.load( std::memory_order_acquire );
154  }
155 
156  void
158  access_context_t /*context*/,
159  handler_invoker_t & invoker ) noexcept override
160  {
161  if( !has_been_revoked() )
162  {
163  // Message is not revoked yet.
164  // Message handler can be called.
165  invoker.invoke( payload_info_t{ m_payload } );
166  }
167  // Otherwise message should be ignored.
168  }
169  };
170 
171 } /* namespace details */
172 
173 namespace impl {
174 
175 // Just forward declaration. Definition will be below definition of delivery_id_t.
176 struct delivery_id_maker_t;
177 
178 } /* namespace impl */
179 
180 //
181 // delivery_id_t
182 //
183 /*!
184  * \brief The ID of revocable message/signal.
185  *
186  * An instance of delivery_id_t returned from send()-functions need
187  * to be store somewhere. Otherwise the message/signal will be revoked
188  * just after completion of send() function. It is
189  * because the destructor of delivery_id_t will be called and that destructor
190  * revokes the message/signal.
191  *
192  * An instance of delivery_id_t can be used for revocation of a message.
193  * Revocation can be performed by two ways:
194  *
195  * 1. Destructor of delivery_id_t automatically revokes the message.
196  * 2. Method delivery_id_t::revoke() is called by an user.
197  *
198  * For example:
199  * \code
200  * namespace delivery_ns = so_5::extra::revocable_msg;
201  * void demo(so_5::mchain_t work_queue) {
202  * // Send a demand to work queue and store the ID returned.
203  * auto id = delivery_ns::send<do_something>(work_queue, ...);
204  * ... // Do some work.
205  * if(some_condition)
206  * // Our previous message should be revoked if it is not delivered yet.
207  * id.revoke();
208  * ...
209  * // Message will be automatically revoked here because ID is destroyed
210  * // on leaving the scope.
211  * }
212  * \endcode
213  *
214  * \note
215  * The delivery_id_t is Movable, not Copyable.
216  *
217  * \attention
218  * This is not a thread-safe class. It means that it is dangerous to
219  * call methods of that class (like revoke() or has_been_revoked()) from
220  * different threads at the same time.
221  *
222  * \since
223  * v.1.2.0
224  */
225 class delivery_id_t final
226  {
228 
229  private :
230  //! The envelope that was sent.
231  /*!
232  * \note Can be nullptr if default constructor was used.
233  */
235 
237  so_5::intrusive_ptr_t< details::envelope_t > envelope )
238  : m_envelope{ std::move(envelope) }
239  {}
240 
241  public :
242  delivery_id_t() = default;
243  /*!
244  * \note The destructor automatically revokes the message if it is
245  * not delivered yet.
246  */
247  ~delivery_id_t() noexcept
248  {
249  revoke();
250  }
251 
252  // This class is not copyable.
253  delivery_id_t( const delivery_id_t & ) = delete;
254  delivery_id_t & operator=( const delivery_id_t & ) = delete;
255 
256  // But this class is moveable.
257  delivery_id_t( delivery_id_t && ) noexcept = default;
258  delivery_id_t & operator=( delivery_id_t && ) noexcept = default;
259 
260  friend void
261  swap( delivery_id_t & a, delivery_id_t & b ) noexcept
262  {
263  a.m_envelope.swap( b.m_envelope );
264  }
265 
266  //! Revoke the message.
267  /*!
268  * \note
269  * It is safe to call revoke() for already revoked message.
270  */
271  void
272  revoke() noexcept
273  {
274  if( m_envelope )
275  {
276  m_envelope->revoke();
277  m_envelope.reset();
278  }
279  }
280 
281  //! Has the message been revoked?
282  /*!
283  * \note
284  * This method can return \a true for an empty instance of
285  * delivery_id_t. For example:
286  * \code
287  * namespace delivery_ns = so_5::extra::revokable_msg;
288  * delivery_id_t null_id;
289  * assert(null_id.has_been_revoked());
290  *
291  * auto id1 = delivery_ns::send<my_message>(mbox, ...);
292  * assert(!id1.has_been_revoked());
293  *
294  * delivery_id_t id2{ std::move(id1) }; // Move id1 to id2.
295  * assert(id1.has_been_revoked());
296  * assert(!id2.has_been_revoked());
297  * \endcode
298  */
299  bool
300  has_been_revoked() const noexcept
301  {
302  return m_envelope ? m_envelope->has_been_revoked() : true;
303  }
304  };
305 
306 namespace impl {
307 
308 /*
309  * This is helper for creation of initialized delivery_id objects.
310  */
312  {
313  template< typename... Args >
314  SO_5_NODISCARD static auto
315  make( Args && ...args )
316  {
317  return delivery_id_t{ std::forward<Args>(args)... };
318  }
319  };
320 
321 /*
322  * Helper function for actual sending of revocable message.
323  */
327  const so_5::mbox_t & to,
328  const std::type_index & msg_type,
330  {
332 
335 
337 
339  }
340 
341 /*
342  * This is helpers for send() implementation.
343  */
344 
345 template< class Message, bool Is_Signal >
347  {
348  template< typename... Args >
351  const so_5::mbox_t & to,
352  Args &&... args )
353  {
356  std::forward< Args >( args )...)
357  };
358 
360 
362  to,
364  std::move(payload) );
365  }
366  };
367 
368 template< class Message >
369 struct instantiator_and_sender_base< Message, true >
370  {
371  //! Type of signal to be delivered.
373 
376  const so_5::mbox_t & to )
377  {
379  to,
381  message_ref_t{} );
382  }
383  };
384 
385 template< class Message >
388  Message,
390  {};
391 
392 } /* namespace impl */
393 
394 /*!
395  * \brief A utility function for creating and delivering a revocable message.
396  *
397  * This function can be used for sending messages and signals to
398  * mboxes and mchains, and to the direct mboxes of agents and
399  * ad-hoc agents.
400  *
401  * Message/signal sent can be revoked by using delivery_id_t::revoke()
402  * method:
403  * \code
404  * auto id = so_5::extra::revocable_msg::send<my_message>(...);
405  * ...
406  * id.revoke();
407  * \endcode
408  * Please note that revoked message is not removed from queues where it
409  * wait for processing. But revoked message/signal will be ignored just
410  * after extraction from a queue.
411  *
412  * Usage examples:
413  * \code
414  * namespace delivery_ns = so_5::extra::revocable_msg;
415  *
416  * // Send a revocable message to mbox mb1.
417  * so_5::mbox_t mb1 = ...;
418  * auto id1 = delivery_ns::send<my_message>(mb1, ...);
419  *
420  * // Send a revocable message to mchain ch1 and revoke it after some time.
421  * so_5::mchain_t ch1 = ...;
422  * auto id2 = delivery_ns::send<my_message>(ch1, ...);
423  * ...
424  * id2.revoke();
425  *
426  * // Send a revocable message to the direct mbox of agent a1.
427  * so_5::agent_t & a1 = ...;
428  * auto id3 = delivery_ns::send<my_message>(a1, ...);
429  * \endcode
430  *
431  * \note
432  * The return value of that function must be stored somewhere. Otherwise
433  * the revocable message will be revoked automatically just right after
434  * send() returns.
435  *
436  * \since
437  * v.1.2.0
438  *
439  */
440 template< typename Message, typename Target, typename... Args >
441 SO_5_NODISCARD delivery_id_t
443  //! Target for the message.
444  //! Can be a reference to mbox, mchain, agent or ad-hod agent.
445  Target && to,
446  //! Message constructor parameters.
447  Args&&... args )
448  {
449  return impl::instantiator_and_sender< Message >::send(
450  so_5::send_functions_details::arg_to_mbox(
451  std::forward< Target >( to ) ),
452  std::forward< Args >( args )... );
453  }
454 
455 /*!
456  * \brief A helper function for redirection of an existing message
457  * as a revocable one.
458  *
459  * Usage example:
460  * \code
461  * class my_agent : public so_5::agent_t {
462  * ...
463  * so_5::extra::revocable_msg::delivery_id_t id_;
464  * ...
465  * void on_some_event(mhood_t<my_message> cmd) {
466  * ... // Some processing.
467  * // Redirection to another destination.
468  * id_ = so_5::extra::revocable_msg::send(another_mbox_, cmd);
469  * }
470  * };
471  * \endcode
472  *
473  * \note
474  * The return value of that function must be stored somewhere. Otherwise
475  * the revocable message will be revoked automatically just right after
476  * send() returns.
477  *
478  * \since
479  * v.1.2.0
480  */
481 template< typename Message, typename Target >
483 typename std::enable_if<
484  !::so_5::is_signal< Message >::value,
487  //! Target for the message.
488  //! Can be a reference to mbox, mchain, agent or ad-hod agent.
489  Target && to,
490  //! Message to be delivered.
491  mhood_t<Message> cmd )
492  {
495  std::forward< Target >( to ) ),
497  cmd.make_reference() );
498  }
499 
500 /*!
501  * \brief A helper function for redirection of an existing signal
502  * as a revocable one.
503  *
504  * This function can be useful in template classes where it is unknown
505  * is template parameter message of signal.
506  *
507  * Usage example:
508  * \code
509  * template<typename Msg>
510  * class my_agent : public so_5::agent_t {
511  * ...
512  * so_5::extra::revocable_msg::delivery_id_t id_;
513  * ...
514  * void on_some_event(mhood_t<Msg> cmd) {
515  * ... // Some processing.
516  * // Redirection to another destination.
517  * id_ = so_5::extra::revocable_msg::send(another_mbox_, cmd);
518  * }
519  * };
520  * \endcode
521  *
522  * \note
523  * The return value of that function must be stored somewhere. Otherwise
524  * the revocable message will be revoked automatically just right after
525  * send() returns.
526  *
527  * \since
528  * v.1.2.0
529  */
530 template< typename Message, typename Target >
532 typename std::enable_if<
533  ::so_5::is_signal< Message >::value,
536  //! Target for the message.
537  //! Can be a reference to mbox, mchain, agent or ad-hod agent.
538  Target && to,
539  //! Signal to be delivered.
540  mhood_t<Message> /*cmd*/ )
541  {
544  std::forward< Target >( to ) ),
546  message_ref_t{} );
547  }
548 
549 } /* namespace revocable_msg */
550 
551 } /* namespace extra */
552 
553 } /* namespace so_5 */
SO_5_NODISCARD so_5::extra::revocable_msg::delivery_id_t make_envelope_and_deliver(const so_5::mbox_t &to, const std::type_index &msg_type, message_ref_t payload)
Definition: pub.hpp:326
delivery_id_t(const delivery_id_t &)=delete
static SO_5_NODISCARD auto make(Args &&...args)
Definition: pub.hpp:315
void do_if_not_revoked_yet(handler_invoker_t &invoker) const noexcept
Definition: pub.hpp:88
void so5_change_mutability(message_mutability_t) override
Definition: pub.hpp:129
delivery_id_t & operator=(delivery_id_t &&) noexcept=default
bool has_been_revoked() const noexcept
Has the message been revoked?
Definition: pub.hpp:300
so_5::intrusive_ptr_t< details::envelope_t > m_envelope
The envelope that was sent.
Definition: pub.hpp:234
Ranges for error codes of each submodules.
Definition: details.hpp:14
const int rc_invalid_payload_kind
An attempt to envelope service request.
Definition: pub.hpp:54
delivery_id_t(so_5::intrusive_ptr_t< details::envelope_t > envelope)
Definition: pub.hpp:236
friend void swap(delivery_id_t &a, delivery_id_t &b) noexcept
Definition: pub.hpp:261
SO_5_NODISCARD delivery_id_t send(Target &&to, Args &&... args)
A utility function for creating and delivering a revocable message.
Definition: pub.hpp:442
message_mutability_t so5_message_mutability() const override
Definition: pub.hpp:122
delivery_id_t(delivery_id_t &&) noexcept=default
SO_5_NODISCARD std::enable_if< ::so_5::is_signal< Message >::value, delivery_id_t >::type send(Target &&to, mhood_t< Message >)
A helper function for redirection of an existing signal as a revocable one.
Definition: pub.hpp:535
const int rc_mutabilty_of_envelope_cannot_be_changed
Mutability of envelope for revocable message can&#39;t be changed.
Definition: pub.hpp:42
static auto ensure_right_payload_kind(so_5::message_ref_t payload)
Definition: pub.hpp:102
envelope_t(so_5::message_ref_t payload)
Definition: pub.hpp:140
void access_hook(access_context_t, handler_invoker_t &invoker) noexcept override
Definition: pub.hpp:157
static SO_5_NODISCARD so_5::extra::revocable_msg::delivery_id_t send(const so_5::mbox_t &to, Args &&... args)
Definition: pub.hpp:350
std::atomic_bool m_revoked
Has message been revoked?
Definition: pub.hpp:82
delivery_id_t & operator=(const delivery_id_t &)=delete
so_5::message_ref_t m_payload
Message to be delivered.
Definition: pub.hpp:85
void revoke() noexcept
Revoke the message.
Definition: pub.hpp:272