Skip to content

Commit

Permalink
A new way of holding disp_binder inside agent.
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit 81d2553
Author: Yauheni Akhotnikau <[email protected]>
Date:   Wed May 31 15:52:41 2023 +0300

    Some documentation related FIXME resolved.

commit 7f9b4fb
Author: Yauheni Akhotnikau <[email protected]>
Date:   Wed May 31 15:23:41 2023 +0300

    More exception safe implementation of coop_impl_t::destroy_content.

commit 5cc2ffc
Author: Yauheni Akhotnikau <[email protected]>
Date:   Wed May 31 15:08:46 2023 +0300

    disp_binder pointer is now stored inside the agent.
  • Loading branch information
eao197 committed May 31, 2023
1 parent 72e8cd2 commit d83313b
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 62 deletions.
20 changes: 20 additions & 0 deletions dev/so_5/agent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include <so_5/message_handler_format_detector.hpp>
#include <so_5/coop_handle.hpp>

#include <so_5/disp_binder.hpp>

#include <atomic>
#include <map>
#include <memory>
Expand Down Expand Up @@ -2669,6 +2671,24 @@ class SO_5_TYPE agent_t
*/
const priority_t m_priority;

/*!
* \brief Binder for this agent.
*
* Since v.5.7.5 disp_binder for the agent is stored inside the agent.
* It guarantees that disp_binder will be deleted after destruction
* of the agent (if there is no circular references between the agent
* and the disp_binder).
*
* This value will be set by coop_t when agent is being add to the
* coop.
*
* \note
* Access to that field provided by so_5::impl::internal_agent_iface_t.
*
* \since v.5.7.5
*/
disp_binder_shptr_t m_disp_binder;

//! Destroy all agent's subscriptions.
/*!
* \note
Expand Down
90 changes: 58 additions & 32 deletions dev/so_5/coop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <so_5/impl/agent_ptr_compare.hpp>

#include <so_5/details/abort_on_fatal_error.hpp>
#include <so_5/details/rollback_on_exception.hpp>

#include <so_5/exception.hpp>
#include <so_5/environment.hpp>
Expand Down Expand Up @@ -54,35 +55,35 @@ void
coop_impl_t::destroy_content(
coop_t & coop ) noexcept
{
using std::swap;

// Initiate deleting of agents by hand to guarantee that
// agents will be destroyed before return from coop_t
// destructor.
//
// NOTE: because agents are stored here by smart references
// for some agents this operation will lead only to reference
// counter descrement. Not to deletion of agent.
decltype(coop.m_agent_array) agents;
swap( coop.m_agent_array, agents );

agents.clear();
coop.m_agent_array.clear();

// Now all user resources should be destroyed.
decltype(coop.m_resource_deleters) resources;
swap( coop.m_resource_deleters, resources );

for( auto & d : resources )
for( auto & d : coop.m_resource_deleters )
d();
coop.m_resource_deleters.clear();
}

void
coop_impl_t::do_add_agent(
coop_t & coop,
agent_ref_t agent_ref )
{
coop.m_agent_array.emplace_back(
std::move(agent_ref), coop.m_coop_disp_binder );
internal_agent_iface_t agent_iface{ *agent_ref };
so_5::details::do_with_rollback_on_exception(
[&]() {
agent_iface.set_disp_binder( coop.m_coop_disp_binder );
coop.m_agent_array.emplace_back( std::move(agent_ref) );
},
[&agent_iface]() noexcept {
agent_iface.drop_disp_binder();
} );
}

void
Expand All @@ -91,8 +92,15 @@ coop_impl_t::do_add_agent(
agent_ref_t agent_ref,
disp_binder_shptr_t disp_binder )
{
coop.m_agent_array.emplace_back(
std::move(agent_ref), std::move(disp_binder) );
internal_agent_iface_t agent_iface{ *agent_ref };
so_5::details::do_with_rollback_on_exception(
[&]() {
agent_iface.set_disp_binder( std::move(disp_binder) );
coop.m_agent_array.emplace_back( std::move(agent_ref) );
},
[&agent_iface]() noexcept {
agent_iface.drop_disp_binder();
} );
}

namespace
Expand Down Expand Up @@ -261,16 +269,17 @@ class coop_impl_t::registration_performer_t
std::begin(m_coop.m_agent_array),
std::end(m_coop.m_agent_array),
[]( const auto & a, const auto & b ) noexcept {
return special_agent_ptr_compare(
*a.m_agent_ref, *b.m_agent_ref );
return special_agent_ptr_compare( *a, *b );
} );
}

void
bind_agents_to_coop()
{
for( auto & i : m_coop.m_agent_array )
internal_agent_iface_t{ *i.m_agent_ref }.bind_to_coop( m_coop );
for( const auto & agent_ref : m_coop.m_agent_array )
{
internal_agent_iface_t{ *agent_ref }.bind_to_coop( m_coop );
}
}

void
Expand All @@ -285,8 +294,10 @@ class coop_impl_t::registration_performer_t
it != m_coop.m_agent_array.end();
++it )
{
it->m_binder->preallocate_resources(
*(it->m_agent_ref) );
agent_t & agent = **it;
internal_agent_iface_t agent_iface{ agent };
agent_iface.query_disp_binder()
.preallocate_resources( agent );
}
}
catch( const std::exception & x )
Expand All @@ -296,8 +307,10 @@ class coop_impl_t::registration_performer_t
it2 != it;
++it2 )
{
it2->m_binder->undo_preallocation(
*(it2->m_agent_ref) );
agent_t & agent = **it2;
internal_agent_iface_t agent_iface{ agent };
agent_iface.query_disp_binder()
.undo_preallocation( agent );
}

SO_5_THROW_EXCEPTION(
Expand All @@ -314,8 +327,8 @@ class coop_impl_t::registration_performer_t
{
try
{
for( auto & info : m_coop.m_agent_array )
internal_agent_iface_t{ *info.m_agent_ref }
for( const auto & agent_ref : m_coop.m_agent_array )
internal_agent_iface_t{ *agent_ref }
.initiate_agent_definition();
}
catch( const exception_t & )
Expand Down Expand Up @@ -347,15 +360,22 @@ class coop_impl_t::registration_performer_t
void
bind_agents_to_disp() noexcept
{
for( auto & info : m_coop.m_agent_array )
info.m_binder->bind( *info.m_agent_ref );
for( const auto & agent_ref : m_coop.m_agent_array )
{
internal_agent_iface_t agent_iface{ *agent_ref };
agent_iface.query_disp_binder().bind( *agent_ref );
}
}

void
deallocate_disp_resources() noexcept
{
for( auto & info : m_coop.m_agent_array )
info.m_binder->undo_preallocation( *(info.m_agent_ref) );
for( const auto & agent_ref : m_coop.m_agent_array )
{
internal_agent_iface_t agent_iface{ *agent_ref };
agent_iface.query_disp_binder()
.undo_preallocation( *agent_ref );
}
}

public :
Expand Down Expand Up @@ -422,8 +442,10 @@ class coop_impl_t::deregistration_performer_t
void
shutdown_all_agents() noexcept
{
for( auto & info : m_coop.m_agent_array )
internal_agent_iface_t{ *info.m_agent_ref }.shutdown_agent();
for( const auto & agent_ref : m_coop.m_agent_array )
{
internal_agent_iface_t{ *agent_ref }.shutdown_agent();
}
}

void
Expand Down Expand Up @@ -475,8 +497,12 @@ coop_impl_t::do_final_deregistration_actions(
coop_t & coop )
{
// Agents should be unbound from their dispatchers.
for( auto & info : coop.m_agent_array )
info.m_binder->unbind( *info.m_agent_ref );
for( const auto & agent_ref : coop.m_agent_array )
{
agent_t & agent = *agent_ref;
internal_agent_iface_t agent_iface{ agent };
agent_iface.query_disp_binder().unbind( agent );
}

// Now the coop can be removed from it's parent.
// We don't except an exception here because m_parent should
Expand Down
31 changes: 1 addition & 30 deletions dev/so_5/coop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -933,37 +933,8 @@ class coop_t : public std::enable_shared_from_this<coop_t>
}

protected:
//! Information about agent and its dispatcher binding.
/*!
* \note
* Order of fields in this structure is critically important:
* the destructor of m_agent_ref must be invoked before the
* destructor of m_binder.
*/
struct agent_with_disp_binder_t
{
agent_with_disp_binder_t(
agent_ref_t agent_ref,
disp_binder_shptr_t binder )
: m_binder{ std::move(binder) }
, m_agent_ref{ std::move(agent_ref) }
{}

//! Binder to a dispatcher.
/*!
* \attention
* This member has to be defined before m_agent_ref.
* In that case the destructor of m_binder will be called
* after the destructor of m_agent_ref.
*/
disp_binder_shptr_t m_binder;

//! Agent.
agent_ref_t m_agent_ref;
};

//! Typedef for the agent information container.
using agent_array_t = std::vector< agent_with_disp_binder_t >;
using agent_array_t = std::vector< agent_ref_t >;

/*!
* \since
Expand Down
9 changes: 9 additions & 0 deletions dev/so_5/disp_binder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ namespace so_5
/*!
* Dispatcher binders are used in the agent registration process to
* binding of agents to desired dispatchers.
*
* \attention
* If an implementation of disp_binder_t interface stores smart
* pointers to agents in methods preallocate_resources() and bind()
* then it must drop (or reset) these stored smart references in
* undo_preallocation() and unbind() methods. Otherwise there will
* be circular references between disp_binder and agents and this
* will lead to memory leaks and other related problems (for
* example, destructors for agents/disp_binders won't be called).
*/
class disp_binder_t
: private std::enable_shared_from_this< disp_binder_t >
Expand Down
64 changes: 64 additions & 0 deletions dev/so_5/impl/internal_agent_iface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#include <so_5/agent.hpp>

#include <so_5/ret_code.hpp>

namespace so_5 {

namespace impl {
Expand Down Expand Up @@ -54,6 +56,68 @@ class internal_agent_iface_t final
{
m_agent.shutdown_agent();
}

/*!
* \brief Setter for disp_binder.
*
* \attention
* It's not a thread safe method. But it's expected that it will be used
* in the context where just one entity owns the pointer to the agent.
*
* \since v.5.7.5
*/
void
set_disp_binder( disp_binder_shptr_t binder )
{
if( m_agent.m_disp_binder )
SO_5_THROW_EXCEPTION(
rc_disp_binder_already_set_for_agent,
"m_agent.m_disp_binder is not nullptr when "
"set_disp_binder is called" );

m_agent.m_disp_binder = std::move(binder);
}

/*!
* \brief Getter for disp_binder.
*
* \attention
* It's not a thread safe method. But it's expected that it will be used
* in the context where just one entity owns the pointer to the agent.
*
* \since v.5.7.5
*/
[[nodiscard]]
disp_binder_t &
query_disp_binder() const
{
if( !m_agent.m_disp_binder )
SO_5_THROW_EXCEPTION(
rc_no_disp_binder_for_agent,
"m_agent.m_disp_binder is nullptr when "
"query_disp_binder is called" );

return *m_agent.m_disp_binder;
}

/*!
* \brief Helper method that drops pointer to disp_binder.
*
* This method is intended to be used for rollback actions
* (for example, when disp_binder is set for the agent, but
* agent can't be stored in a coop).
*
* \attention
* It's not a thread safe method. But it's expected that it will be used
* in the context where just one entity owns the pointer to the agent.
*
* \since v.5.7.5
*/
void
drop_disp_binder() noexcept
{
m_agent.m_disp_binder.reset();
}
};

} /* namespace impl */
Expand Down
24 changes: 24 additions & 0 deletions dev/so_5/ret_code.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,30 @@ const int rc_agent_deactivated = 189;
*/
const int rc_mpsc_mbox_expected = 190;

/*!
* \brief The dispatcher binder is already set for the agent.
*
* Since v.5.7.5 disp_binder is stored inside the agent.
* Only one disp_binder can be set for an agent. An attempt to set
* disp_binder when the agent already has one is an error.
*
* \since v.5.7.5
*/
const int rc_disp_binder_already_set_for_agent = 194;

/*!
* \brief The dispatcher binder is not set for the agent yet.
*
* Since v.5.7.5 disp_binder is stored inside the agent.
* A disp_binder must be set for an agent during addition of the agent
* to a coop. It means that if agent is added to coop the agent should have
* non-null pointer to the disp_binder. If this pointer is still null then
* it's an error.
*
* \since v.5.7.5
*/
const int rc_no_disp_binder_for_agent = 195;

//! \name Common error codes.
//! \{

Expand Down

0 comments on commit d83313b

Please sign in to comment.