-
Notifications
You must be signed in to change notification settings - Fork 5
so5extra 1.4 Revocable Timers
SObjectizer Core has send_delayed
and send_periodic
functions. The so_5::send_periodic
function returns an instance of so_5::timer_id_t
. This instance can be used for cancellation of delayed/periodic message delivery. For example:
auto id = so_5::send_periodic<my_message>(mbox, 10s, 0s, ...);
...
if(some_condition())
// Delivery of delayed message should be canceled.
id.release();
But there is a problem: the cancellation will actually be performed only if the message is under control of SObjectizer's timer thread/manager. But if the timer thread/manager already sent the message and the message is waiting in an event queue then delivery won't be cancelled. It is becase there is no way to extract a message from an event queue.
Since v.1.2.0 so_5_extra provides own timer_id_t
, send_delayed
and send_periodic
versions those allow guaranteed revocation of a delayed/periodic messages. For example:
auto id = so_5::extra::revocable_timer::send_delayed<my_message>(mbox, 10s, ...);
...
if(some_condition())
// Delivery of delayed message will be canceled.
// Even if the message is already in an event queue now.
id.release();
All stuff related to revocable timer messages/signals are defined in so_5_extra/revocable_timer/pub.hpp
header file. So to use this functionality it is necessary to include that file and so_5/all.hpp
file:
#include <so_5_extra/revocable_timer/pub.hpp>
#include <so_5/all.hpp>
To send a delayed message/signal it is necessary to use so_5::extra::revocable_timer::send_delayed
and to store the so_5::extra::revocable_timer::timer_id_t
instance returned:
// Note that this is so_5::extra::revocable_timer::timer_id_t, not so_5::timer_id_t
so_5::extra::revocable_timer::timer_id_t id =
so_5::extra::revocable_timer::send_delayed<my_message>(mbox, delay,
... /* Arguments for my_message's constructor */
);
To send a periodic message/signal it is necessary to use so_5::extra::revocable_timer::send_periodic
and to store the so_5::extra::revocable_timer::timer_id_t
instance returned:
// Note that this is so_5::extra::revocable_timer::timer_id_t, not so_5::timer_id_t
so_5::extra::revocable_timer::timer_id_t id =
so_5::extra::revocable_timer::send_periodic<my_message>(mbox, delay, period,
... /* Arguments for my_message's constructor */
);
It is important to note that if the return value of send_delayed()
or send_periodic()
is not stored then the message sent will be revoked automatically.
To revoke message/signal sent it is possible to use those ways:
- Destruction of
timer_id_t
object. Destructor oftimer_id_t
automaticaly revokes message/signal sent. - Calling
timer_id_t::revoke()
ortimer_id_t::release()
method. These methods revoke message/signal sent. It is safe to callrevoke()
/release()
several times. - Assigning a new value to
timer_id_t
object. Previously sent message/signal is revoked in this case.
For example:
class worker final : public so_5::agent_t {
...
so_5::extra::revocable_timer::timer_id_t check_timer_;
...
void on_do_something(mhood_t<some_cmd> cmd) {
... // Initiate some actions to be performed by other agents.
// Start a timer for checking actions results after some time.
check_timer_ = so_5::extra::revocable_timer::send_delayed<check_results>(
*this, 10s, ...);
}
void on_results(mhood_t<some_result> cmd) {
... // Some handling.
// Timer is no more needed.
check_timer_.revoke();
}
...
};
There are several forms of send_delayed
functions:
// Construct and send a delayed message to an arbitrary mbox.
auto id1 = so_5::extra::revocable_timer::send_delayed<my_message>(mbox, 10s, ...);
// Construct and send a delayed message to the direct mbox of an agent.
auto id2 = so_5::extra::revocable_timer::send_delayed<my_message>(agent, 10s, ...);
// Construct and send a delayed message to a mchain.
auto id3 = so_5::extra::revocable_timer::send_delayed<my_message>(ch, 10s, ...);
// Resend an existing message to an arbitrary mbox:
void some_agent::on_some_message(mhood_t<some_message> cmd) {
id_ = so_5::extra::revocable_timer::send_delayed(mbox, 10s, std::move(cmd));
}
// Resend an existing message to the direct mbox of an agent:
void some_agent::on_some_message(mhood_t<some_message> cmd) {
id_ = so_5::extra::revocable_timer::send_delayed(*this, 10s, std::move(cmd));
}
// Resend an existing message to a mchain:
void some_agent::on_some_message(mhood_t<some_message> cmd) {
id_ = so_5::extra::revocable_timer::send_delayed(ch, 10s, std::move(cmd));
}
The same is for send_periodic
:
// Construct and send a periodic message to an arbitrary mbox.
auto id1 = so_5::extra::revocable_timer::send_periodic<my_message>(mbox, 10s, 5s, ...);
// Construct and send a periodic message to the direct mbox of an agent.
auto id2 = so_5::extra::revocable_timer::send_periodic<my_message>(agent, 10s, 5s, ...);
// Construct and send a periodic message to a mchain.
auto id3 = so_5::extra::revocable_timer::send_periodic<my_message>(ch, 10s, 5s, ...);
// Resend an existing message to an arbitrary mbox:
void some_agent::on_some_message(mhood_t<some_message> cmd) {
id_ = so_5::extra::revocable_timer::send_periodic(mbox, 10s, 5s, std::move(cmd));
}
// Resend an existing message to the direct mbox of an agent:
void some_agent::on_some_message(mhood_t<some_message> cmd) {
id_ = so_5::extra::revocable_timer::send_periodic(*this, 10s, 5s, std::move(cmd));
}
// Resend an existing message to a mchain:
void some_agent::on_some_message(mhood_t<some_message> cmd) {
id_ = so_5::extra::revocable_timer::send_periodic(ch, 10s, 5s, std::move(cmd));
}
A message/signal that sent via so_5::extra::revocable_timer::send_*()
functions is enveloped into a special envelope with atomic revocation flag inside. A smart pointer to that envelope is stored inside timer_id_t
object. That smart pointer is released when message is revoked (by any way).
Revocation means setting a special flag value inside the envelope. The envelope checks that flag in access_hook()
method and provides the access to enveloped message/signal only if that flag is not set.
Revocation of message/signal doesn't affect message limits. It means that actual delivery of message/signal from timer to receiver's queue increments message counter, but revoke()
doesn't decrement it. Message counter will be decremented only on extraction of envelope with message from an event queue.
If an instance of message is transformed during delivery (as part of limit_then_transform
overlimit reaction or because message was collected by collecting mbox) then transformed message can't be revoked. Transformed message regarded as a new one, that has no relation to original revocable message.