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