SObjectizer  5.8
Loading...
Searching...
No Matches
so-5.5.16: New helper function auto_close_mchains

New helper function so_5::auto_close_mchains has been added in v.5.5.16. It helps to close some mchains at scope exit.

When mchains are used for inter thread communication special care must be taken because there could be deadlocks. For example let see the simplest case:

using namespace std;
using namespace so_5;
void do_something(environment_t & env)
{
auto ch = create_mchain(env);
// Launch child thread which will read messages and do some processing.
thread child{ [ch]{
// Return from receive will be on close of ch.
receive(from(ch), [](const some_task & t) {...});
} };
// Do generation of tasks for child thread.
while(has_tasks())
{
... // generation of next task.
send<some_task>(ch, ...);
}
// Child thread must be joined before return from do_something.
child.join();
}
SObjectizer Environment.
Private part of message limit implementation.
Definition agent.cpp:33
STL namespace.

This is an error in this example: do_something will not return. It is becase child thread will never finished. Because mchain ch will not be closed explicitely.

It easy to fix that example:

// Do generation of tasks for child thread.
while(has_tasks())
{
... // generation of next task.
send<some_task>(ch, ...);
}
// Mchain must be closed to force finish of child thread.
close_retain_content(ch);
// Child thread must be joined before return from do_something.
child.join();

But there could be more complex cases related to exception safety. If an exception is thrown after construction of child but before call to close_retain_content there could be a deadlock (see above).

To make this code exception safe we need two helpers: one of them should close chain, another should call join for threads. Something like that:

class auto_closer {
mchain_t & ch_;
public :
auto_closer(mchain_t & ch) : ch_{ch} {}
~auto_closer() { close_retain_content(ch_); }
};
class auto_joiner {
thread & thr_;
public :
auto_joiner(thread & thr) : thr_{thr} {}
~auto_joiner() { if(thr_.joinable()) thr_.close(); }
};

With the help of these classes we can make do_something more exception safe:

void do_something(environment_t & env)
{
auto ch = create_mchain(env);
// Launch child thread which will read messages and do some processing.
thread child{ [ch]{
// Return from receive will be on close of ch.
receive(from(ch), [](const some_task & t) {...});
} };
// There is a trick: auto_joiner must be created before auto_closer.
// It is because we need the destructor of auto_closer be called first.
auto_joiner child_joiner{ child };
auto_closer ch_closer{ ch };
// Do generation of tasks for child thread.
while(has_tasks())
{
... // generation of next task.
send<some_task>(ch, ...);
}
// There is no need for calling close() and join() anymore.
}

But writting ad-hoc classes like auto_closer is a boring and error-prone tasks. Helper function so_5::auto_close_mchains do it for you:

void do_something(environment_t & env)
{
auto ch = create_mchain(env);
// Launch child thread which will read messages and do some processing.
thread child{ [ch]{
// Return from receive will be on close of ch.
receive(from(ch), [](const some_task & t) {...});
} };
auto_joiner child_joiner{ child };
auto ch_closer = auto_close_mchains(mchain_props::close_mode_t::retain_content, ch);
// Do generation of tasks for child thread.
while(has_tasks())
{
... // generation of next task.
send<some_task>(ch, ...);
}
// There is no need for calling close() and join() anymore.
}
mchain_auto_close_details::auto_closer_t< sizeof...(Tail) > auto_close_mchains(mchain_props::close_mode_t close_mode, Tail &&... tail)
Helper function for creation of automatic closer of mchains.

Function auto_close_mchains returns object very similar to auto_closer shown above. The destructor of that object will close all mchains passed to auto_close_mchains. It is possible to pass several mchains to one call of auto_close_mchains:

auto command_ch = create_mchain(env);
auto reply_ch = create_mchain(env);
auto status_ch = create_mchain(env);
auto log_ch = create_mchain(env);
auto ch_closer = auto_close_mchains(mchain_props::close_mode_t::drop_content,
command_ch, reply_ch, status_ch, log_ch);
...

Because usage of auto_close_mchains is verbose there are two shorthands: auto_close_retain_content and auto_close_drop_content. So instead of writting:

auto ch_closer = auto_close_mchains(mchain_props::close_mode_t::drop_content,
command_ch, reply_ch, status_ch, log_ch);

it is possible to write:

auto ch_closer = auto_close_drop_content(command_ch, reply_ch, status_ch, log_ch);