SObjectizer 5.8
Loading...
Searching...
No Matches
coop.cpp
Go to the documentation of this file.
1/*
2 SObjectizer 5.
3*/
4
5#include <so_5/coop.hpp>
6
7#include <so_5/impl/internal_env_iface.hpp>
8#include <so_5/impl/internal_agent_iface.hpp>
9#include <so_5/impl/agent_ptr_compare.hpp>
10
11#include <so_5/details/rollback_on_exception.hpp>
12
13#include <so_5/exception.hpp>
14#include <so_5/environment.hpp>
15
16#include <exception>
17#include <algorithm>
18
19namespace so_5
20{
21
22//
23// coop_reg_notificators_container_t
24//
25void
27 environment_t & env,
28 const coop_handle_t & coop ) const noexcept
29 {
30 for( auto & n : m_notificators )
31 n( env, coop );
32 }
33
34//
35// coop_dereg_notificators_container_t
36//
37void
39 environment_t & env,
40 const coop_handle_t & coop,
41 const coop_dereg_reason_t & reason ) const noexcept
42 {
43 for( auto & n : m_notificators )
44 n( env, coop, reason );
45 }
46
47namespace impl
48{
49
50//
51// coop_impl_t
52//
53void
55 coop_t & coop ) noexcept
56 {
57 // Initiate deleting of agents by hand to guarantee that
58 // agents will be destroyed before return from coop_t
59 // destructor.
60 //
61 // NOTE: because agents are stored here by smart references
62 // for some agents this operation will lead only to reference
63 // counter descrement. Not to deletion of agent.
64 coop.m_agent_array.clear();
65
66 // Don't expect exceptions here because all resource deleters have
67 // to be noexcept.
68 for( auto & d : coop.m_resource_deleters )
69 {
70 static_assert( noexcept( d() ),
71 "resource deleter is expected to be noexcept" );
72
73 d();
74 }
75 coop.m_resource_deleters.clear();
76 }
77
78void
80 coop_t & coop,
81 agent_ref_t agent_ref )
82 {
83 internal_agent_iface_t agent_iface{ *agent_ref };
85 [&]() {
87 coop.m_agent_array.emplace_back( std::move(agent_ref) );
88 },
89 [&agent_iface]() noexcept {
90 agent_iface.drop_disp_binder();
91 } );
92 }
93
94void
96 coop_t & coop,
97 agent_ref_t agent_ref,
98 disp_binder_shptr_t disp_binder )
99 {
100 internal_agent_iface_t agent_iface{ *agent_ref };
102 [&]() {
103 agent_iface.set_disp_binder( std::move(disp_binder) );
104 coop.m_agent_array.emplace_back( std::move(agent_ref) );
105 },
106 [&agent_iface]() noexcept {
107 agent_iface.drop_disp_binder();
108 } );
109 }
110
111namespace
112{
113 /*!
114 * \since
115 * v.5.2.3
116 *
117 * \brief Helper function for notificator addition.
118 */
119 template< class C, class N >
120 inline void
122 intrusive_ptr_t< C > & to,
123 N notificator )
124 {
125 if( !to )
126 {
127 to = intrusive_ptr_t< C >( new C() );
128 }
129
130 to->add( std::move(notificator) );
131 }
132
133} /* namespace anonymous */
134
135void
137 coop_t & coop,
138 coop_reg_notificator_t notificator )
139 {
142 std::move(notificator) );
143 }
144
145void
147 coop_t & coop,
148 coop_dereg_notificator_t notificator )
149 {
152 std::move(notificator) );
153 }
154
155[[nodiscard]]
158 const coop_t & coop ) noexcept
159 {
161 {
163 coop.m_parent );
164 if( parent )
165 return parent->exception_reaction();
166 else
168 }
169
170 return coop.m_exception_reaction;
171 }
172
173void
175 coop_t & coop ) noexcept
176 {
177 // If it is the last working agent then Environment should be
178 // informed that the cooperation is ready to be deregistered.
179 if( 0 == --coop.m_reference_count )
180 {
181 // NOTE: usage counter incremented and decremented during
182 // registration process even if registration of cooperation failed.
183 // So decrement_usage_count() could be called when cooperation
184 // has coop_not_registered status.
185 //
186 // It is possible that reference counter become 0 several times.
187 // For example when a child coop is being registered while
188 // the parent coop is in deregistration process.
189 // Because of that it is necessary to check the current status
190 // of the coop.
191 //
192 // If the coop should be deregistered finally its status should
193 // be changed to deregistration_in_final_stage.
194 const auto should_finalize = [&] {
195 std::lock_guard< std::mutex > lock{ coop.m_lock };
196
197 using status_t = coop_t::registration_status_t;
198 if( status_t::coop_registered == coop.m_registration_status ||
200 {
203 return true;
204 }
205 else
206 return false;
207 };
208
209 if( should_finalize() )
210 {
212 .ready_to_deregister_notify( coop.shared_from_this() );
213 }
214 }
215 }
216
218 {
220
221 void
228
229 void
231 {
234
235 // Coop's lock should be acquired before notification
236 // of the parent coop.
237 std::lock_guard< std::mutex > lock{ m_coop.m_lock };
239
240 // These actions shouldn't throw.
242 // This operation shouldn't throw because dispatchers
243 // allocated resources for agents.
244 //
245 // But it is possible that an exception will be throw
246 // during an attempt to send evt_start message to agents.
247 // In that case it is simpler to call std::terminate().
249
250 // Cooperation should assume that it is registered now.
253
254 // Increment reference count to reflect that cooperation
255 // is registered. This is necessary in v.5.5.12 to prevent
256 // automatic deregistration of the cooperation right after
257 // finish of registration process for empty cooperation.
259 } );
260 },
261 [this] {
262 // NOTE: we use the fact that actual binding of agents to
263 // dispatchers can't throw. It means that exception was thrown
264 // at earlier stages (in define_all_agents() or
265 // make_relation_with_parent_coop()).
267 } );
268 }
269
270 void
272 {
273 std::sort(
274 std::begin(m_coop.m_agent_array),
275 std::end(m_coop.m_agent_array),
276 []( const auto & a, const auto & b ) noexcept {
277 return special_agent_ptr_compare( *a, *b );
278 } );
279 }
280
281 void
283 {
284 for( const auto & agent_ref : m_coop.m_agent_array )
285 {
286 internal_agent_iface_t{ *agent_ref }.bind_to_coop( m_coop );
287 }
288 }
289
290 void
292 {
293 // In case of an exception we should undo preallocation only for
294 // those agents for which preallocation was successful.
295 coop_t::agent_array_t::iterator it;
296 try
297 {
298 for( it = m_coop.m_agent_array.begin();
299 it != m_coop.m_agent_array.end();
300 ++it )
301 {
302 agent_t & agent = **it;
303 internal_agent_iface_t agent_iface{ agent };
304 agent_iface.query_disp_binder()
306 }
307 }
308 catch( const std::exception & x )
309 {
310 // All preallocated resources should be returned back.
311 for( auto it2 = m_coop.m_agent_array.begin();
312 it2 != it;
313 ++it2 )
314 {
315 agent_t & agent = **it2;
316 internal_agent_iface_t agent_iface{ agent };
317 agent_iface.query_disp_binder()
319 }
320
323 std::string{
324 "an exception during the first stage of "
325 "binding agent to the dispatcher, exception: " }
326 + x.what() );
327 }
328 }
329
330 void
332 {
333 try
334 {
335 for( const auto & agent_ref : m_coop.m_agent_array )
336 internal_agent_iface_t{ *agent_ref }
338 }
339 catch( const exception_t & )
340 {
341 throw;
342 }
343 catch( const std::exception & ex )
344 {
347 ex.what() );
348 }
349 catch( ... )
350 {
353 "exception of unknown type has been thrown in "
354 "so_define_agent()" );
355 }
356 }
357
358 void
364
365 void
367 {
368 for( const auto & agent_ref : m_coop.m_agent_array )
369 {
370 internal_agent_iface_t agent_iface{ *agent_ref };
371 agent_iface.query_disp_binder().bind( *agent_ref );
372 }
373 }
374
375 void
377 {
378 for( const auto & agent_ref : m_coop.m_agent_array )
379 {
380 internal_agent_iface_t agent_iface{ *agent_ref };
381 agent_iface.query_disp_binder()
382 .undo_preallocation( *agent_ref );
383 }
384 }
385
386 public :
387 explicit registration_performer_t( coop_t & coop ) noexcept
388 : m_coop{ coop }
389 {}
390
391 void
393 {
394 // On first phase we perform actions that don't require
395 // any rollback on exception.
397
398 // Then we should perform some actions that require some
399 // rollback in the case of an exception.
401 }
402 };
403
404void
409
410//
411// deregistration_performer_t
412//
413//! A helper for coop's deregistration procedure.
415 {
418
424
426 perform_phase1() noexcept
427 {
428 // The first phase should be performed on locked object.
429 std::lock_guard< std::mutex > lock{ m_coop.m_lock };
430
433 // Deregistration is already in progress.
434 // Nothing to do.
436
437 // Deregistration process should be started.
441
443
445 }
446
447 void
449 {
450 for( const auto & agent_ref : m_coop.m_agent_array )
451 {
452 internal_agent_iface_t{ *agent_ref }.shutdown_agent();
453 }
454 }
455
456 void
463
464 public :
466 coop_t & coop,
467 coop_dereg_reason_t reason ) noexcept
468 : m_coop{ coop }
469 , m_reason{ reason }
470 {}
471
472 void
473 perform() noexcept
474 {
475 auto result = perform_phase1();
476
478 {
479 // Deregistration is initiated the first time.
480
481 // All agents should be shut down.
483
484 // Reference count to this coop can be decremented.
485 // If there is no more uses of that coop then the coop
486 // will be deregistered completely.
488 }
489 }
490 };
491
492void
499
500void
502 coop_t & coop )
503 {
504 // Agents should be unbound from their dispatchers.
505 for( const auto & agent_ref : coop.m_agent_array )
506 {
507 agent_t & agent = *agent_ref;
508 internal_agent_iface_t agent_iface{ agent };
509 agent_iface.query_disp_binder().unbind( agent );
510 }
511
512 // Now the coop can be removed from it's parent.
513 // We don't except an exception here because m_parent should
514 // contain an actual value.
515 // But if not then we have a serious problem and it is better
516 // to terminate the application.
518 }
519
520void
522 coop_t & parent,
523 coop_shptr_t child )
524 {
525 // Count of users on this coop is incremented.
527
528 // If an exception is throw below then usage count for the parent
529 // coop should be decremented.
531 // Modification of parent-child relationship must be performed
532 // on locked object.
533 std::lock_guard< std::mutex > lock{ parent.m_lock };
534
535 // A new coop can't be added as a child if coop is being
536 // deregistered.
541 "add_child() can be processed only when coop "
542 "is registered" );
543
544 // New child will be inserted to the head of children list.
545 if( parent.m_first_child )
546 parent.m_first_child->m_prev_sibling = child;
547
548 child->m_next_sibling = std::move(parent.m_first_child);
549
550 parent.m_first_child = std::move(child);
551 },
552 [&parent] {
553 // Something went wrong. Count of references should be
554 // returned back.
556 } );
557 }
558
559void
561 coop_t & parent,
562 coop_t & child ) noexcept
563 {
564 {
565 // Modification of parent-child relationship must be performed
566 // on locked object.
567 std::lock_guard< std::mutex > lock{ parent.m_lock };
568
569 if( parent.m_first_child.get() == &child )
570 {
571 // Child was a head of children chain. There is no prev-sibling
572 // for the child to be removed.
573 parent.m_first_child = child.m_next_sibling;
574 if( parent.m_first_child )
575 parent.m_first_child->m_prev_sibling.reset();
576 }
577 else
578 {
580 if( child.m_next_sibling )
582 }
583 }
584
585 // Count of references to the parent coop can be decremented now.
587 }
588
589} /* namespace impl */
590
591} /* namespace so_5 */
A base class for agents.
Definition agent.hpp:673
Container for cooperation deregistration notificators.
Definition coop.hpp:197
std::vector< coop_dereg_notificator_t > m_notificators
Definition coop.hpp:215
void call_all(environment_t &env, const coop_handle_t &coop, const coop_dereg_reason_t &reason) const noexcept
Call all notificators.
Definition coop.cpp:38
It's a kind of strong typedef for coop's deregistration reason.
Definition coop.hpp:80
Type of smart handle for a cooperation.
Container for cooperation registration notificators.
Definition coop.hpp:129
void call_all(environment_t &env, const coop_handle_t &coop) const noexcept
Call all notificators.
Definition coop.cpp:26
std::vector< coop_reg_notificator_t > m_notificators
Definition coop.hpp:146
Agent cooperation.
Definition coop.hpp:389
coop_shptr_t m_first_child
The head of list of children coops.
Definition coop.hpp:1129
environment_t & environment() const noexcept
Access to SO Environment for which cooperation is bound.
Definition coop.hpp:453
registration_status_t m_registration_status
The registration status of cooperation.
Definition coop.hpp:1092
void decrement_usage_count() noexcept
Decrement usage count for the coop.
Definition coop.hpp:1224
exception_reaction_t exception_reaction() const noexcept
Get the current exception rection flag for that cooperation.
Definition coop.hpp:758
resource_deleter_vector_t m_resource_deleters
Container of user resource deleters.
Definition coop.hpp:1101
disp_binder_shptr_t m_coop_disp_binder
Default agent to the dispatcher binder.
Definition coop.hpp:1026
void deregister(int reason) noexcept
Deregister the cooperation with the specified reason.
Definition coop.hpp:907
void for_each_child(Lambda &&lambda) const
A helper method for doing some actions with children coops.
Definition coop.hpp:1285
exception_reaction_t m_exception_reaction
A reaction to non-handled exception.
Definition coop.hpp:1121
std::mutex m_lock
A lock for synchonization of some operations on coop.
Definition coop.hpp:1077
coop_handle_t m_parent
Parent coop.
Definition coop.hpp:1023
registration_status_t
Registration status.
Definition coop.hpp:956
@ deregistration_in_final_stage
Deregistration of the coop is in the final stage.
@ coop_registered
Cooperation is registered.
@ coop_deregistering
Cooperation is in deregistration process.
atomic_counter_t m_reference_count
Count for entities.
Definition coop.hpp:1043
void remove_child(coop_t &child) noexcept
Remove a child from the parent coop.
Definition coop.hpp:1267
coop_dereg_reason_t m_dereg_reason
Deregistration reason.
Definition coop.hpp:1110
void increment_usage_count() noexcept
Increment usage count for the coop.
Definition coop.hpp:1196
coop_reg_notificators_container_ref_t m_reg_notificators
Notificators for registration event.
Definition coop.hpp:1050
coop_shptr_t m_prev_sibling
The previous coop in sibling's chain.
Definition coop.hpp:1141
void add_child(coop_shptr_t child)
Add a new child to the parent coop.
Definition coop.hpp:1242
agent_array_t m_agent_array
Cooperation agents.
Definition coop.hpp:1029
outliving_reference_t< environment_t > m_env
SObjectizer Environment for which cooperation is created.
Definition coop.hpp:1032
coop_dereg_notificators_container_ref_t m_dereg_notificators
Notificators for deregistration event.
Definition coop.hpp:1057
coop_shptr_t m_next_sibling
The next coop in sibling's chain.
Definition coop.hpp:1153
virtual void bind(agent_t &agent) noexcept=0
Bind agent to dispatcher.
virtual void unbind(agent_t &agent) noexcept=0
Unbind agent from dispatcher.
virtual void preallocate_resources(agent_t &agent)=0
Allocate resources in dispatcher for new agent.
virtual void undo_preallocation(agent_t &agent) noexcept=0
Undo resources allocation.
SObjectizer Environment.
exception_reaction_t exception_reaction() const noexcept
An exception reaction for the whole SO Environment.
The base class for all SObjectizer exceptions.
Definition exception.hpp:34
A helper for coop's deregistration procedure.
Definition coop.cpp:415
deregistration_performer_t(coop_t &coop, coop_dereg_reason_t reason) noexcept
Definition coop.cpp:465
registration_performer_t(coop_t &coop) noexcept
Definition coop.cpp:387
An internal class with real implementation of coop's logic.
Definition coop.hpp:249
static void destroy_content(coop_t &coop) noexcept
Perform all necessary cleanup actions for coop.
Definition coop.cpp:54
static void do_add_agent(coop_t &coop, agent_ref_t agent_ref, disp_binder_shptr_t disp_binder)
Add agent to the cooperation with the dispatcher binding.
Definition coop.cpp:95
static void do_final_deregistration_actions(coop_t &coop)
Perform final deregistration actions for an coop.
Definition coop.cpp:501
static void do_add_child(coop_t &parent, coop_shptr_t child)
Perform addition of a new child coop.
Definition coop.cpp:521
static void add_dereg_notificator(coop_t &coop, coop_dereg_notificator_t notificator)
Add notificator about cooperation deregistration event.
Definition coop.cpp:146
static exception_reaction_t exception_reaction(const coop_t &coop) noexcept
Get exception reaction for coop.
Definition coop.cpp:157
static void do_remove_child(coop_t &parent, coop_t &child) noexcept
Perform removement of a child coop.
Definition coop.cpp:560
static void add_reg_notificator(coop_t &coop, coop_reg_notificator_t notificator)
Add notificator about cooperation registration event.
Definition coop.cpp:136
static void do_decrement_reference_count(coop_t &coop) noexcept
Do decrement reference count for a coop.
Definition coop.cpp:174
static void do_deregistration_specific_actions(coop_t &coop, coop_dereg_reason_t reason) noexcept
Perform actions related to the deregistration of coop.
Definition coop.cpp:493
static void do_registration_specific_actions(coop_t &coop)
Perform actions related to the registration of coop.
Definition coop.cpp:405
static void do_add_agent(coop_t &coop, agent_ref_t agent_ref)
Add agent to cooperation.
Definition coop.cpp:79
internal_agent_iface_t(agent_t &agent) noexcept
disp_binder_t & query_disp_binder() const
Getter for disp_binder.
void drop_disp_binder() noexcept
Helper method that drops pointer to disp_binder.
void set_disp_binder(disp_binder_shptr_t binder)
Setter for disp_binder.
A helper class for accessing the functionality of environment-class which is specific for SObjectizer...
internal_env_iface_t(environment_t &env)
Initializing constructor.
void ready_to_deregister_notify(coop_shptr_t coop) noexcept
Notification about readiness to the deregistration.
Template class for smart reference wrapper on the atomic_refcounted_t.
T & operator*() const noexcept
T & get() const noexcept
#define SO_5_THROW_EXCEPTION(error_code, desc)
Definition exception.hpp:74
Enumeration of cooperation deregistration reasons.
Definition coop.hpp:39
const int parent_deregistration
Deregistration because parent cooperation deregistration.
Definition coop.hpp:52
Some reusable and low-level classes/functions which can be used in public header files.
auto do_with_rollback_on_exception(Main_Action main_action, Rollback_Action rollback_action) -> decltype(main_action())
Helper function for do some action with rollback in the case of an exception.
auto invoke_noexcept_code(L lambda) noexcept -> decltype(lambda())
void do_add_notificator_to(intrusive_ptr_t< C > &to, N notificator)
Helper function for notificator addition.
Definition coop.cpp:121
Details of SObjectizer run-time implementations.
Definition agent.cpp:780
coop_shptr_t to_shptr_noexcept(const coop_handle_t &) noexcept
A helper function for unsafe extraction of shared_ptr to coop from coop_handle instance.
coop_shptr_t to_shptr(const coop_handle_t &)
A helper function for safe extraction of shared_ptr to coop from coop_handle instance.
Private part of message limit implementation.
Definition agent.cpp:33
const int rc_coop_define_agent_failed
Cooperation couldn't be registered.
Definition ret_code.hpp:74
const int rc_coop_is_not_in_registered_state
An attempt to do something with coop that is not in registered state.
Definition ret_code.hpp:395
exception_reaction_t
A reaction of SObjectizer to an exception from agent event.
Definition agent.hpp:65
@ inherit_exception_reaction
Exception reaction should be inherited from SO Environment.
Definition agent.hpp:81
const int rc_agent_to_disp_binding_failed
Binding of agent to dispatcher failed.
Definition ret_code.hpp:81