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  a.m_envelope.swap( b.m_envelope );
241  }
242 
243  //! Revoke the message.
244  /*!
245  * \note
246  * It is safe to call revoke() for already revoked message.
247  */
248  void
249  revoke() noexcept
250  {
251  if( m_envelope )
252  {
253  m_envelope->revoke();
254  m_envelope.reset();
255  }
256  }
257 
258  //! Has the message been revoked?
259  /*!
260  * \note
261  * This method can return \a true for an empty instance of
262  * delivery_id_t. For example:
263  * \code
264  * namespace delivery_ns = so_5::extra::revokable_msg;
265  * delivery_id_t null_id;
266  * assert(null_id.has_been_revoked());
267  *
268  * auto id1 = delivery_ns::send<my_message>(mbox, ...);
269  * assert(!id1.has_been_revoked());
270  *
271  * delivery_id_t id2{ std::move(id1) }; // Move id1 to id2.
272  * assert(id1.has_been_revoked());
273  * assert(!id2.has_been_revoked());
274  * \endcode
275  */
276  bool
277  has_been_revoked() const noexcept
278  {
279  return m_envelope ? m_envelope->has_been_revoked() : true;
280  }
281  };
282 
283 namespace impl {
284 
285 /*
286  * This is helper for creation of initialized delivery_id objects.
287  */
289  {
290  template< typename... Args >
291  SO_5_NODISCARD static auto
292  make( Args && ...args )
293  {
294  return delivery_id_t{ std::forward<Args>(args)... };
295  }
296  };
297 
298 /*
299  * Helper function for actual sending of revocable message.
300  */
304  const so_5::mbox_t & to,
305  const std::type_index & msg_type,
307  {
309 
312 
314 
316  }
317 
318 /*
319  * This is helpers for send() implementation.
320  */
321 
322 template< class Message, bool Is_Signal >
324  {
325  template< typename... Args >
328  const so_5::mbox_t & to,
329  Args &&... args )
330  {
333  std::forward< Args >( args )...)
334  };
335 
337 
339  to,
341  std::move(payload) );
342  }
343  };
344 
345 template< class Message >
346 struct instantiator_and_sender_base< Message, true >
347  {
348  //! Type of signal to be delivered.
350 
353  const so_5::mbox_t & to )
354  {
356  to,
358  message_ref_t{} );
359  }
360  };
361 
362 template< class Message >
365  Message,
367  {};
368 
369 } /* namespace impl */
370 
371 /*!
372  * \brief A utility function for creating and delivering a revocable message.
373  *
374  * This function can be used for sending messages and signals to
375  * mboxes and mchains, and to the direct mboxes of agents and
376  * ad-hoc agents.
377  *
378  * Message/signal sent can be revoked by using delivery_id_t::revoke()
379  * method:
380  * \code
381  * auto id = so_5::extra::revocable_msg::send<my_message>(...);
382  * ...
383  * id.revoke();
384  * \endcode
385  * Please note that revoked message is not removed from queues where it
386  * wait for processing. But revoked message/signal will be ignored just
387  * after extraction from a queue.
388  *
389  * Usage examples:
390  * \code
391  * namespace delivery_ns = so_5::extra::revocable_msg;
392  *
393  * // Send a revocable message to mbox mb1.
394  * so_5::mbox_t mb1 = ...;
395  * auto id1 = delivery_ns::send<my_message>(mb1, ...);
396  *
397  * // Send a revocable message to mchain ch1 and revoke it after some time.
398  * so_5::mchain_t ch1 = ...;
399  * auto id2 = delivery_ns::send<my_message>(ch1, ...);
400  * ...
401  * id2.revoke();
402  *
403  * // Send a revocable message to the direct mbox of agent a1.
404  * so_5::agent_t & a1 = ...;
405  * auto id3 = delivery_ns::send<my_message>(a1, ...);
406  * \endcode
407  *
408  * \note
409  * The return value of that function must be stored somewhere. Otherwise
410  * the revocable message will be revoked automatically just right after
411  * send() returns.
412  *
413  * \since
414  * v.1.2.0
415  *
416  */
417 template< typename Message, typename Target, typename... Args >
418 SO_5_NODISCARD delivery_id_t
420  //! Target for the message.
421  //! Can be a reference to mbox, mchain, agent or ad-hod agent.
422  Target && to,
423  //! Message constructor parameters.
424  Args&&... args )
425  {
426  return impl::instantiator_and_sender< Message >::send(
427  so_5::send_functions_details::arg_to_mbox(
428  std::forward< Target >( to ) ),
429  std::forward< Args >( args )... );
430  }
431 
432 /*!
433  * \brief A helper function for redirection of an existing message
434  * as a revocable one.
435  *
436  * Usage example:
437  * \code
438  * class my_agent : public so_5::agent_t {
439  * ...
440  * so_5::extra::revocable_msg::delivery_id_t id_;
441  * ...
442  * void on_some_event(mhood_t<my_message> cmd) {
443  * ... // Some processing.
444  * // Redirection to another destination.
445  * id_ = so_5::extra::revocable_msg::send(another_mbox_, cmd);
446  * }
447  * };
448  * \endcode
449  *
450  * \note
451  * The return value of that function must be stored somewhere. Otherwise
452  * the revocable message will be revoked automatically just right after
453  * send() returns.
454  *
455  * \since
456  * v.1.2.0
457  */
458 template< typename Message, typename Target >
460 typename std::enable_if<
461  !::so_5::is_signal< Message >::value,
464  //! Target for the message.
465  //! Can be a reference to mbox, mchain, agent or ad-hod agent.
466  Target && to,
467  //! Message to be delivered.
468  mhood_t<Message> cmd )
469  {
472  std::forward< Target >( to ) ),
474  cmd.make_reference() );
475  }
476 
477 /*!
478  * \brief A helper function for redirection of an existing signal
479  * as a revocable one.
480  *
481  * This function can be useful in template classes where it is unknown
482  * is template parameter message of signal.
483  *
484  * Usage example:
485  * \code
486  * template<typename Msg>
487  * class my_agent : public so_5::agent_t {
488  * ...
489  * so_5::extra::revocable_msg::delivery_id_t id_;
490  * ...
491  * void on_some_event(mhood_t<Msg> cmd) {
492  * ... // Some processing.
493  * // Redirection to another destination.
494  * id_ = so_5::extra::revocable_msg::send(another_mbox_, cmd);
495  * }
496  * };
497  * \endcode
498  *
499  * \note
500  * The return value of that function must be stored somewhere. Otherwise
501  * the revocable message will be revoked automatically just right after
502  * send() returns.
503  *
504  * \since
505  * v.1.2.0
506  */
507 template< typename Message, typename Target >
509 typename std::enable_if<
510  ::so_5::is_signal< Message >::value,
513  //! Target for the message.
514  //! Can be a reference to mbox, mchain, agent or ad-hod agent.
515  Target && to,
516  //! Signal to be delivered.
517  mhood_t<Message> /*cmd*/ )
518  {
521  std::forward< Target >( to ) ),
523  message_ref_t{} );
524  }
525 
526 } /* namespace revocable_msg */
527 
528 } /* namespace extra */
529 
530 } /* 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:303
delivery_id_t(const delivery_id_t &)=delete
static SO_5_NODISCARD auto make(Args &&...args)
Definition: pub.hpp:292
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:277
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
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
SO_5_NODISCARD delivery_id_t send(Target &&to, Args &&... args)
A utility function for creating and delivering a revocable message.
Definition: pub.hpp:419
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:512
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
static SO_5_NODISCARD so_5::extra::revocable_msg::delivery_id_t send(const so_5::mbox_t &to, Args &&... args)
Definition: pub.hpp:327
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
void revoke() noexcept
Revoke the message.
Definition: pub.hpp:249