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