Stiffstream / sobjectizer

An implementation of Actor, Publish-Subscribe, and CSP models in one rather small C++ framework. With performance, quality, and stability proved by years in the production.

Home Page:https://stiffstream.com/en/products/sobjectizer.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Any way to wait until an agent is ready to receive messages?

ilpropheta opened this issue · comments

Hi,
I have a very simple question. Suppose I am testing an application where I have some agents registered to a global environment.
I would like to be sure that such agents are ready to get messages from channels they have subscribed to. This way I can generate fake messages and make expectations.

At the moment, I just register them and wait - say - 10ms to be on the safe side. However, this is not portable and can fail sporadically on CI builds. In general, I don't like "sleep" of any sort.

Is it possible to wait for such a "state" without adding custom logic?
For example, agents have that ensure_binding_finished. Is this what I am looking for?

For the moment, I can't use the experimental "testing" capabilities in SObjectizer since I have some stable code that can't be easily accommodated to the "scenario" semantics.

Thanks!

Hi!

I added a new section to Wiki not so far ago. That section is related to coops and there is such thing as reg/dereg notitifcations: https://github.com/Stiffstream/sobjectizer/wiki/SO-5.7-InDepth-Coops#regdereg-notificators

Maybe reg-notificator can help you in your case.

There is also a possibility to set a global coop_listener: https://github.com/Stiffstream/sobjectizer/blob/master/dev/sample/so_5/coop_listener/main.cpp
This feature is not described well and is almost not used in SO-5, but can be useful too.

Hi @eao197 !
Thanks for the reply. This seems to be helpful, but I don't understand one point: why is the notificator called on the same thread as that calling register_coop? Naively, for my scenario, I thought I needed something like this:

auto childCoop = env.make_coop(parent, disp); // dispatcher here is active_group
some_event_type done;
childCoop->add_reg_notificator([&done, thisAgentCoopId = child->id()](so_5::environment_t&, const so_5::coop_handle_t& handle) noexcept {
	if (thisAgentCoopId == handle.id())
	{
		done.set();
	}
});
auto agent = child->make_agent<SomeAgentType>(...);
env.register_coop(std::move(child));
done.wait();
// at this point, the agent can receive messages (since child has been registered)

However, this code results in a deadlock.

Do you expect this?

why is the notificator called on the same thread as that calling register_coop?

It's because the calling of reg_notificator is a part of register_coop procedure. That procedure includes several steps. For example, preallocating resources on dispatchers, calling so_define_agent for agents, binding agents to dispatchers. All those actions are performed inside register_coop on the same thread.

After calling so_define_agent for all coop agents and after binding all of coop agents to dispatcher the coop is regarded as successfully registered. Because of that register_coop calls all reg_notificators just before the return. So reg_notificator is called on the thread where register_coop was invoked.

It means that the return from register_coop (and invocation of reg_notificator) tells you that coop is now living inside SObjectizer. And, if your agents made all their subscriptions inside so_define_agent then they will receive your messages. But they may not have started yet (so_evt_start may be called for them a bit latter).

For example:

class example : public so_5::agent_t {
...
  void so_define_agent() override {
    so_subscribe_self().event(mhood_t<message>) {...}
  }
};

so_5::mbox_t agent_mbox;
env.introduce_coop([&mbox](so_5::coop_t & coop) {
  agent_mbox = coop.make_agent<example>(...)->so_direct_mbox();
  so_5::send<message>(agent_mbox, "first"); // (1)
  ...
});
so_5::send<message>(agent_mbox, "second"); // (2)

The message "first" sent at (1) will be ignored because so_define_agent wasn't called for the agent yet.
But the message "second" will be delivered to the agent because so_define_agent was called during register_coop (inside introduce_coop).

Do you expect this?

No, I don't. But I can only take a look at it tomorrow.

Thanks for the detailed explanation. So is it guaranteed that, after returning from register_coop, every message sent to the mailboxes an agent has subscribed to will be delivered, sooner or later (depending on when the agent starts)? That's an important guarantee that was not clear to me. Maybe I don't need the notificator at all.

Another related question: is there any way to check if an agent has started (still without adding custom logic)?

So is it guaranteed that, after returning from register_coop, every message sent to the mailboxes an agent has subscribed to will be delivered, sooner or later (depending on when the agent starts)?

If all subscriptions are made in so_define_agent and messages are sent after the return from register_coop then yes, guaranteed.

But there could be mbox types that requires subscription in so_evt_start (retained_mbox for examples, the subscription in so_define_agent can lead to loss of some messages).

Another related question: is there any way to check if an agent has started (still without adding custom logic)?

No, there is no such a way yet.

I'll think maybe some kind of hook can be defined for so_evt_start (or delivery of internal message that causes so_evt_start).

Thanks @eao197 . For now I am fine. The problem I thought I had it's not really there since you have clarified everything (as always!). I don't really need hooks to evt_start either, this was just out of curiosity.

I close here but if you think that

Do you expect this?

is a bug, feel free to re-open/link/comment this issue.

Again, many thanks!