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 };
84 so_5::details::do_with_rollback_on_exception(
85 [&]() {
86 agent_iface.set_disp_binder( coop.m_coop_disp_binder );
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
95coop_impl_t::do_add_agent(
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 };
101 so_5::details::do_with_rollback_on_exception(
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
136coop_impl_t::add_reg_notificator(
137 coop_t & coop,
138 coop_reg_notificator_t notificator )
139 {
140 do_add_notificator_to(
141 coop.m_reg_notificators,
142 std::move(notificator) );
143 }
144
145void
146coop_impl_t::add_dereg_notificator(
147 coop_t & coop,
148 coop_dereg_notificator_t notificator )
149 {
150 do_add_notificator_to(
151 coop.m_dereg_notificators,
152 std::move(notificator) );
153 }
154
155[[nodiscard]]
158 const coop_t & coop ) noexcept
159 {
161 {
162 const auto parent = so_5::low_level_api::to_shptr_noexcept(
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 {
211 impl::internal_env_iface_t{ coop.m_env.get() }
212 .ready_to_deregister_notify( coop.shared_from_this() );
213 }
214 }
215 }
216
218 {
220
221 void
228
229 void
231 {
232 so_5::details::do_with_rollback_on_exception( [this] {
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.
241 details::invoke_noexcept_code( [&] {
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()
305 .preallocate_resources( agent );
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()
318 .undo_preallocation( agent );
319 }
320
322 rc_agent_to_disp_binding_failed,
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 }
337 .initiate_agent_definition();
338 }
339 catch( const exception_t & )
340 {
341 throw;
342 }
343 catch( const std::exception & ex )
344 {
346 rc_coop_define_agent_failed,
347 ex.what() );
348 }
349 catch( ... )
350 {
352 rc_coop_define_agent_failed,
353 "exception of unknown type has been thrown in "
354 "so_define_agent()" );
355 }
356 }
357
358 void
360 {
361 so_5::low_level_api::to_shptr(m_coop.m_parent)->add_child(
362 m_coop.shared_from_this() );
363 }
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
458 {
459 m_coop.for_each_child( []( coop_t & coop ) {
461 } );
462 }
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.
517 so_5::low_level_api::to_shptr(coop.m_parent)->remove_child( coop );
518 }
519
520void
521coop_impl_t::do_add_child(
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.
530 so_5::details::do_with_rollback_on_exception( [&] {
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.
537 if( coop_t::registration_status_t::coop_registered !=
538 parent.m_registration_status )
540 rc_coop_is_not_in_registered_state,
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 {
579 child.m_prev_sibling->m_next_sibling = child.m_next_sibling;
580 if( child.m_next_sibling )
581 child.m_next_sibling->m_prev_sibling = child.m_prev_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 */
Container for cooperation deregistration notificators.
Definition coop.hpp:197
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
Agent cooperation.
Definition coop.hpp:389
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
void deregister(int reason) noexcept
Deregister the cooperation with the specified reason.
Definition coop.hpp:907
exception_reaction_t m_exception_reaction
A reaction to non-handled exception.
Definition coop.hpp:1121
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.
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
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_final_deregistration_actions(coop_t &coop)
Perform final deregistration actions for an coop.
Definition coop.cpp:501
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 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
void drop_disp_binder() noexcept
Helper method that drops pointer to disp_binder.
A helper class for accessing the functionality of environment-class which is specific for SObjectizer...
Template class for smart reference wrapper on the atomic_refcounted_t.
#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.
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:905
Private part of message limit implementation.
Definition agent.cpp:33
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