1 /*
2  * SObjectizer-5
3  */
4 
5 /*!
6  * \since
7  * v.5.5.5
8  *
9  * \file
10  * \brief Definition of storage for delivery filters.
11  */
12 
13 #pragma once
14 
15 #include <so_5/mbox.hpp>
16 
17 #include <so_5/compiler_features.hpp>
18 
19 #include <so_5/details/rollback_on_exception.hpp>
20 
21 #include <map>
22 
23 namespace so_5 {
24 
25 namespace impl {
26 
27 //
28 // delivery_filter_storage_t
29 //
30 /*!
31  * \since
32  * v.5.5.5
33  *
34  * \brief Storage for message delivery filters.
35  *
36  * \attention This storage do not drop filters in the destructor.
37  * It is because the reference to agent is necessary for filter dropping.
38  * Storage do not have this reference.
39  * Because of that it is necessary to drop all filters explicitely
40  * by drop_all() method.
41  */
42 class delivery_filter_storage_t
43 	{
44 		//! Type of key for filters map.
45 		struct key_t
46 			{
47 				//! Message mbox.
48 				mbox_t m_mbox;
49 				//! Message type.
50 				std::type_index m_msg_type;
51 
52 				bool
operator <so_5::impl::delivery_filter_storage_t::key_t53 				operator<( const key_t & o ) const
54 					{
55 						const abstract_message_box_t & a = *m_mbox;
56 						const abstract_message_box_t & b = *(o.m_mbox);
57 						return a < b || ( a == b && m_msg_type < o.m_msg_type );
58 					}
59 			};
60 
61 		//! Type of filters map.
62 		using map_t = std::map< key_t, delivery_filter_unique_ptr_t >;
63 
64 		//! Information about defined filters.
65 		map_t m_filters;
66 
67 	public :
68 		//! Drop all defined filters.
69 		/*!
70 		 * Filters are removed from corresponding mboxes and destroyed.
71 		 */
72 		void
drop_all(agent_t & owner)73 		drop_all( agent_t & owner ) noexcept
74 			{
75 				for( auto & p : m_filters )
76 					p.first.m_mbox->drop_delivery_filter(
77 							p.first.m_msg_type,
78 							owner );
79 
80 				m_filters.clear();
81 			}
82 
83 		//! Set a delivery filter.
84 		/*!
85 		 * If there already is previous filter it will be destroyed.
86 		 */
87 		void
set_delivery_filter(const mbox_t & mbox,const std::type_index & msg_type,delivery_filter_unique_ptr_t filter,agent_t & owner)88 		set_delivery_filter(
89 			const mbox_t & mbox,
90 			const std::type_index & msg_type,
91 			delivery_filter_unique_ptr_t filter,
92 			agent_t & owner )
93 			{
94 				const key_t key{ mbox, msg_type };
95 				auto it = m_filters.find( key );
96 				if( it == m_filters.end() )
97 					{
98 						// There is no previous filter.
99 						// New filter must be added.
100 						auto ins_result = m_filters.emplace(
101 								map_t::value_type{ key, std::move( filter ) } );
102 						so_5::details::do_with_rollback_on_exception(
103 							[&] {
104 								mbox->set_delivery_filter(
105 										msg_type,
106 										*(ins_result.first->second),
107 										owner );
108 							},
109 							[&] {
110 								m_filters.erase( ins_result.first );
111 							} );
112 					}
113 				else
114 					{
115 						// Replace previous filter with new one.
116 						delivery_filter_unique_ptr_t old{ std::move( it->second ) };
117 						it->second = std::move( filter );
118 
119 						// Mbox must change delivery filter too.
120 						so_5::details::do_with_rollback_on_exception(
121 							[&] {
122 								mbox->set_delivery_filter(
123 										msg_type,
124 										*(it->second),
125 										owner );
126 							},
127 							[&] {
128 								it->second = std::move( old );
129 							} );
130 					}
131 			}
132 
133 		//! Remove delivery filter.
134 		void
drop_delivery_filter(const mbox_t & mbox,const std::type_index & msg_type,agent_t & owner)135 		drop_delivery_filter(
136 			const mbox_t & mbox,
137 			const std::type_index & msg_type,
138 			agent_t & owner ) noexcept
139 			{
140 				auto it = m_filters.find( key_t{ mbox, msg_type } );
141 				if( it != m_filters.end() )
142 					{
143 						mbox->drop_delivery_filter( msg_type, owner );
144 						m_filters.erase( it );
145 					}
146 			}
147 	};
148 
149 } /* namespace impl */
150 
151 } /* namespace so_5 */
152 
153