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

Agent shutdown

ilpropheta opened this issue · comments

Hi,
I have a simple question: is it possible to stop an agent from inside of that agent? In other words, can an agent signal to the environment that it wants to be stopped/terminated/finished in a normal way?

so_deregister_agent_coop_normally() and so_deregister_agent_coop() are close to what I need, however such functions deregister the entire cooperation. My current approach is based on parent-child relations:

  • create a "root" cooperation
  • for each agent, create a child cooperation and register the agent at that
  • when an agent needs to be deregistered, call so_deregister_agent_coop_normally() (this will result in shutting down that agent only)

Is that a good approach?

Just to give you an example of why I need this: I have an agent that communicates with a child process. When the child process sends back a particular data or it just terminates, that agent must be stopped.

Many thanks.

PS I hope to see you in 1-hour at the meetup :)

Is that a good approach?

Yes, this is the right approach.

Sometimes can be necessary to "deactivate" just one agent from a coop without deregistration of the whole cooperation. It can be done via additional state in that agent without any subscriptions:

class demo : public so_5::agent_t {
  state_t st_deactivated{this};
  ...
  void on_some_event(mhood_t<some_message> cmd) {
    if(some_condtion)
      this >>= st_deactivated;
    ...
  }
  ...
};

PS I hope to see you in 1-hour at the meetup :)

I plan to be here but in silent mode because I don't speak English :)

Thanks!

Sometimes can be necessary to "deactivate" just one agent from a coop without deregistration of the whole cooperation. It can be done via additional state in that agent without any subscriptions

But in this case I expect that the agent will be still "alive", won't it? For example, if a dispatcher has associated a thread to it, it will be "paused" forever? This has probably no impact at all, but just to be sure if I understood.

I plan to be here but in silent mode because I don't speak English :)

You are very welcome even if you stay in the chat :)

But in this case I expect that the agent will be still "alive", won't it?

You're right. It'll still be here and can consume some resources (messages will be delivered to it but will just be ignored because there won't be a handler in st_deactivated state).

Just another thing: do you expect any (sensible) performance drawbacks in this scenario where each agent has its own cooperation?

Only if you'll register/deregister them at the rate of 30-50K per second.

It can also depend on the dispatcher(s) used. If you bind your agent to active_obj dispatcher then you will pay for the creation and destruction of a thread for every agent bound.

Ok, so after creating the agents, you don't expect any sensible performance problem, do you?

You can have some penalties when you're using dispatchers like active_obj and active_group, because they perform some cleanup at the deregistration of an agent.

But those penalties are measurable and you can write a benchmark that emulates your case to get reasonable numbers.

Ok, good for me. I close the issue!

Let me add one thing that I have just experienced, I hope it will be useful to design a proper agent deactivation as mentioned in #29.

The approach based on so_deregister_agent_coop has an additional potential drawback: just after calling so_deregister_agent_coop, the agent does not change its (internal) state and then it could keep on handling messages until the cooperation gets deregistered and the agent itself terminated. This leads to a hybrid solution where a "deactivated" state is added, but just to manage this "limbo".

Finally, when the cooperation is deregistered, the agent gets terminated correctly.

just after calling so_deregister_agent_coop, the agent does not change its (internal) state and then it could keep on handling messages until the cooperation gets deregistered and the agent itself terminated.

It's intended behavior. It can be useful in the cases where a coop contains several agents: one of them is a leader that decided when so_deregister_agent_coop has to be called, all other agents just do their work. After the call of so_deregister_agent_coop by the leader, all other agents have a possibility to complete their work (e.g. they will process messages already in the event-queue, and those messages can be important for some other agents in the application).

Generally speaking, the deregistration can be seen as a statement "I want to quit as soon as I complete all my current work", but not as a demand "I want to be killed right now".

Thanks for clarifying that. It makes sense.

Hi,
let me ask again on this topic since I am getting a behavior I wasn't expecting.

I have some agents created each with their own child cooperation (sharing the very same parent cooperation and binder). For example:

const auto binder = so_5::disp::active_group::make_dispatcher(sobj->environment()).binder("group1");
auto parent = sobj->environment().register_coop(sobj->environment().make_coop(binder));

auto child1 = sobj->environment().make_coop(parent, binder);
auto agent1 = child1->make_agent<MyAgent1>();
sobj->environment().register_coop(std::move(child1));

auto child2 = sobj->environment().make_coop(parent, binder);
auto agent2 = child2->make_agent<MyAgent2>();
sobj->environment().register_coop(std::move(child2));

If any of these agents calls so_deregister_agent_coop_normally, I get a crash when the environment gets destroyed (it's an access violation somewhere in the library).

Instead, if the the dispatcher is turned into active_obj, the behavior is that I expect (as we have discussed in this conversation).

Is this an expected behavior?

Many thanks for your help!

Hi!

Is this an expected behavior?

No, it looks like a bug. I'll check this case tomorrow.

Hi, Marco!

Could you check the version from https://github.com/Stiffstream/sobjectizer/tree/5.7-dev-issue-28 branch?

Hi Yauheni! I think you have fixed the issue 👍 now I get expected behavior on agents in the active_group.
Let me know if you need more tests from my side.

I think you have fixed the issue

Ok, I'll prepare a new release with the bugfix.

The updated version is fixed as a tag: https://github.com/Stiffstream/sobjectizer/releases/tag/v.5.7.2.5
Updates for vcpkg and conan will be available ASAP.

Hi, I have just seen this new great feature that is strongly related to this issue.

I would like to be sure that I got the feature right in order to clean up my code.

As now, some of my agents have a dedicated cooperation in order to be deactivated by calling so_deregister_agent_coop_normally. Before that, the agent is moved to a "deactivated" state, as you proposed here.

Is it correct to suppose that now I can call so_deactivate_agent instead and get an equivalent behavior (except for that dedicated cooperation for each agent that is not needed anymore)?

Is it correct to suppose that now I can call so_deactivate_agent instead and get an equivalent behavior

As far as I can understand your description the answer is YES. You don't need to have a separate "deactivated" state, method so_deactivate_agent uses a special hidden state that exists for all agents.

But so_deactivate_agent just switches the agent to that hidden state and drops all subscriptions. It doesn't deregister the agent's coop. So you still needs a dedicated coops for your agents that might be deactivated.

Good point. Actually, I have introduced a dedicated sub-cooperation for each agent only for deactivating each agent separately. I have some "root" cooperations to gather such sub-cooperations.

For instance, I have a root coop made with an active_obj binder that contains sub-cooperations made with the same binder (e.g. each agent has its own worker thread). Another root coop is made with active_group binder, containing sub-cooperations made with the same binder (e.g. all agents share the same worker thread).

When I need to deregister an agent, I first move it to the (fake) "deactivated" state and then I call so_deregister_agent_coop_normally to deregister the sub-cooperation associated with that agent only.

Suppose now I remove a layer and get rid of those sub-cooperations. Each agent is now directly belonging to the "root" cooperation. At this point, when so_deactivate_agent is called, the agent moves to a "deactivated" state and drops all subscriptions. Since the cooperation now handles more agents, I don't want to deregister it. That's fine.

My question is: if the just deactivated agent has its own worker thread (e.g. the cooperation has an active_obj dispatcher), does calling so_deactivate_agent cause the associated worker thread to be detached as well? As far as I know, with my approach I have this behavior.

Let me know if this is not clear and I will try setting up some code.

My question is: if the just deactivated agent has its own worker thread (e.g. the cooperation has an active_obj dispatcher), does calling so_deactivate_agent cause the associated worker thread to be detached as well?

If a separate thread is allocated for an agent (like in the case of active_obj dispatcher) then this thread will be associated with the agent until the agent is deregistered. This is because the binder is stored inside the coop and will be destroyed on deregistration. And the allocated thread will be released on the destruction of the binder.

So in your case, so_deactivate_agent just simplified your agents a little bit: you have no need to have a separate "deactivated" state. But if you want to release a thread allocated to the agent, you have to deregister the agent's coop.

Thanks, that's the clarification I needed. So, as you said, so_deactivate_agent enables me to get rid of the "deactivated" state, but I still need a dedicated coop to release any associated threads.

Hi, @ilpropheta

I've added some more clarifications to https://github.com/Stiffstream/sobjectizer/wiki/v.5.7.3#new-method-so_deactivate_agent-added-to-agent_t-class
I think it's important to note that so_evt_finish is called for deactivated agents the usual way during deregistration of the coop.

Thanks for this additional clarification.