Replies: 2 comments 1 reply
-
|
Hey @eao197, thanks for sharing! That's an interesting topic. I would be interested in the (experimental) code, if you have some that can be disclosed. I think the main point worth highlighting is the purpose of coroutines in agents (I know you were a bit reluctant at the beginning of the discussion): they (can) enable better concurrency without breaking the actor model. Coroutines give dispatchers more flexibility in thread usage. For example, if two agents share a However, for agents bound to dedicated threads, the benefit is less evident, but coroutines can still simplify asynchronous workflows and integrate with modern async APIs. What do you think? |
Beta Was this translation helpful? Give feedback.
-
Maybe this case has a natural and simple solution: when an agent uses own custom event queue, then it pushes a special execution_demand into the dispatcher queue. This execution_demand has to be treated as not-thread-safe one, because it will modify internal agent's state (extraction of the actual pending message from the custom queue). It means that agents with custom event queues simply can't have thread-safe event handlers. "Can't" means that an agent can make a subscription with the |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
These are my first thoughts about supporting C++20 coroutines inside an agent's event_handler.
I'm not going to try to fantasize about the purposes that coroutines in event_handler can be used for. It doesn't matter for now. Just assume that we want to have a possibility to write something like that:
The first key moment is a special return value type --
so_5::resumable_t(the name can be changed if something more appropriate will be found).The next key moment is a special handling of event_handlers that return
so_5::resumable_t. When an event_handler usesso_5::resumable_tas the return type then SO-5 will understand that event_handler is a coroutine and will do some special transformation. Let's suppose that we have something like that:This code will be "transformed" into something like this:
The next key moment is the presence of two special methods
_so_deactivate_normal_processingand_so_reactivate_normal_processing.In the normal processing mode there is an event_queue with pending demands (at the moment such a queue belongs to dispatchers, agents do not own these queues).
A demand in the queue holds information about one incoming message and how it should be processed (as normal message or as evt_start/evt_finish special signals). A dispatcher takes the first pending demand, calls an event_handler and then goes to the next pending demand if any.
It could be a case where the demand queue for
my_agentholds several pending demands:The dispatcher gets the first one (for
some_msg) and calls the event_handler for it. It will be the special_evt_auto_generated_for_some_msghandler that just stores the resumable object into_m_current_resumableand returns.The dispatcher sees that event_handled completed and takes the next demand from the queue.
But this normal processing is not appropriate when the event_handler for
some_msgis a coroutine. In this case the normal processing has to be paused and the agent should react to_msg_resume_current_coroutinesignals only.The new hypothetical methods
_so_deactivate_normal_processingand_so_reactivate_normal_processingare intended for switching from normal processing mode to the special one, and for returning back to the normal mode.When the agent switches to this special mode it will look like switching to a different and empty demand queue, the old demand queue will be stored somewhere. This new queue will be used for
_msg_resume_current_coroutinesignals only. All other messages will be redirected to the old queue (they will be stored to the old queue, but the processing will be delayed).When
_msg_resume_current_coroutinearrives it will be extracted and delivered to_evt_auto_generated_for_some_msghandler the usual way. The SO-5 will see such a signal just as usual without a need for a special handling.And the next key moment is to use
_msg_resume_current_coroutinesignal for resumption of a suspended coroutine. The promise and awaitables for SO-5 have to be written in such a way to send_msg_resume_current_coroutinewhen coroutine has to be resumed.This is a very basic idea with a lot of white spots.
The main of them is how to implement this changing of demand queues for an agent. It seems to me that the work on this old idea has to be resumed. If an agent has its custom event_queue then it could be changed to any other queue when it's appropriate and necessary.
Another white spot is support coroutine-based thread safe event handlers on adv_thread_pool dispatcher. The example shown above requires changing of the agent internal members for processing coroutine, but those changes are prohibited in thread safe event handlers. I hope the solution will be found. Even if this won't happen in the near future the first implementation for support of coroutines could be made for non-thread safe event handlers. With the hope that the solution will be found with time.
I think that one way to add support for C++20 coroutines into SO-5 is to use a special mark that enables coroutine-based event_handlers for an agent. Something like that:
An attempt to subscribe an event_handler that returns
so_5::resumable_twithout addition ofso_5::enable_coroutinesto the agent context will lead to an exception during subscription.I also think that SO-5.8 will still require C++17 as the minimum, the support for coroutines will be available only if SO-5 is being compiled in C++20/23 mode.
Beta Was this translation helpful? Give feedback.
All reactions