Skip to content

Add coro::condition_variable #297

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 33 commits into
base: main
Choose a base branch
from

Conversation

dobord
Copy link

@dobord dobord commented Feb 15, 2025

Added coro::condition_variable to work together with coro::mutex for scenarios where std::mutex and std::condition_variable need to be replaced

@jbaldwin
Copy link
Owner

Hello @dobord ,

This is an awesome addition! Thank you for opening a PR.

Can you please add a test/test_condition_variable.cpp with some tests to this PR please? I think it would also be a good idea to add it as an entry in the readme so it is documented with an example like the other coro objects. The README.md file is generated from the .githooks/readme-template.md file, you can enable the custom githooks for this project via the CMake option LIBCORO_RUN_GITCONFIG=ON one time which will then run the README.md generation on git precommit hook after that is completed. The precommit hook also runs clang-format across modified files as well.

I also wonder if you could implement this without the coro::io_scheduler dependency and instead use a modified coro::event that has a notify_all() and notify_one() modification? The modified coro::event::notify_all() would behave just like coro::event::set(true) and awake all awaiters to check the condition variable's predicate(), where notify_one() would awake the first waiter in the linked list of coro::event::m_state. I think this would be beneficial since it would decouple coro::condition_variable from requiring the networking feature flag.

@dobord
Copy link
Author

dobord commented Feb 16, 2025

Hello @jbaldwin!
I'm going to add test/test condition_variable.cpp and example to this PR.

Regarding the dependency on coro::io_scheduler: the implementation of coro::condition_variable :: wait_for and wait_until requires coro::io_scheduler, which is able to suspend the execution of a coroutine in a threadpool for a specified time and schedule its launch in a threadpool later.
Without these functions, of course, it would be possible to implement the feature using coro::event. This would not require the networking feature flag. But such a cut-down version of coro::condition_variable would look of little use in those scenarios where a full-fledged replacement for std::condition_variable is needed.

@jbaldwin
Copy link
Owner

Yeah excellent points, I was only considering the base wait() without the time functionality, so my bad, the event loop like functionality on the coro::io_scheduler is definitely required for this.

@jbaldwin
Copy link
Owner

Hello @dobord , I'm trying to add a coro::when_any #298. I wonder if this could be used to return the first result of the notify() or the timeout? I noticed you are trying to add a lot of logic around having either the notify or timeout complete and tracking those looks quite difficult.

If the timeout fires the notify task will need to be cancelled somehow, and vice versa (although the timeout task would eventually trigger...).

@dobord
Copy link
Author

dobord commented Feb 18, 2025

Hello @jbaldwin, Super! Thanks a lot for the feature coro::when_any #298!
I'll try to simplify the code with it.

While writing tests for coro::condition_variable I found problems that occur in multi-threaded/multi-coroutine scenarios of using coro::condition_variable. Also, spending a file descriptor for each instance of coro::condition_variable seems wasteful. I will make a refactoring that will use the new feature with cancellation of scheduled coro::task by timeout and cancellation token

@dobord dobord force-pushed the feature/condition_variable branch 2 times, most recently from f286c47 to d309cc1 Compare February 20, 2025 16:21
@dobord dobord force-pushed the feature/condition_variable branch from db65306 to d50ed2c Compare February 21, 2025 09:53
@dobord
Copy link
Author

dobord commented Feb 22, 2025

Hello, @jbaldwin !
Can you help my to fix issues in CI pipeline? I don't know why pipeline ci-fedora, ci-opensuse-15-g++(10,20,ON,ON) are failed?
CI Tests here dobord#4

@jbaldwin
Copy link
Owner

Hmm, this is a very strange linker error, I'm trying to figure out whats going on but it seems like gcc <= 13 is not including the when_any template functions in the output for some reason.

@dobord
Copy link
Author

dobord commented Feb 25, 2025

Hello, @jbaldwin!
I put some tests in test_condition_variable under conditional compilation. This is a workaround to solve the build issue.

@jbaldwin
Copy link
Owner

Cool, I think that makes sense since the templates are now 'used' so they are not being optimized out.

@dobord dobord marked this pull request as draft February 28, 2025 06:25
@dobord dobord force-pushed the feature/condition_variable branch 4 times, most recently from b287fc4 to 6f84a94 Compare February 28, 2025 15:31
@dobord dobord marked this pull request as ready for review March 16, 2025 08:33
@dobord dobord force-pushed the feature/condition_variable branch from c24fa57 to 4387c6c Compare March 17, 2025 00:16
@jbaldwin
Copy link
Owner

oops I didn't mean to update, meant to approve and run workflow... you'll have a conflict or need to force push if there are any more changes, my bad, giving it another review now

Copy link
Owner

@jbaldwin jbaldwin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a quick look over the new coro::condition_variable but I need some more time (ran out this morning but I will get to it!).

I really like the idea behind the default executor as well, its a nice addition.

Do you have a need to support emscripten for the example? It seems to complicate it a lot with the custom stop token, just curious.

* Facade for universal access to default scheduler or default thread pool.
* This facade supports the concept of coro::concepts::executor.
*/
class facade
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think libuv calls this default_loop() for a similar concept implementation, so this could be possibly named default_executor()?

I think its interesting trying to abstract away which executor it is, but I wonder if it would be better to simply have:

  1. static coro::default::io_executor() -> coro::io_scheduler&
  2. static coro::default::executor() -> coro::thread_pool&

And the user can choose which one to use, I would think they should know if they are compiling with or without the networking support? It should also reduce a lot of the duplicate code from the facade.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps these should be returning std::shared_ptr<T> since most functions/objects in the library takes those as arguments.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remade default_executor, now it has 4 global functions in the namespace coro::default_executor.

@dobord dobord force-pushed the feature/condition_variable branch from 9fea175 to cb49cc7 Compare March 24, 2025 13:51
@dobord
Copy link
Author

dobord commented Mar 24, 2025

Hello, @jbaldwin!
I agree, the name default_executor suits this class even better.

It seems impossible to make static coro::default::io_executor and static coro::default::executor(). How to provide user with possibility to set io_scheduler::options and thread_pool::options for creation of these static objects in this case?

In general, I would really like to spread the use of default_executor throughout the library to maximize performance improvements when resuming coroutines.

There is another idea, of course it goes far beyond the scope of this PR#297, I would like to make another coro::scheduler based on coro::thread_pool, which would support the entire interface of coro::io_scheduler except for working with epoll (network, file descriptors, etc.). It would also be cool to make a concept for this scheduler. And after that implement in coro::default_executor the choice between coro::scheduler or coro::io_scheduler as the main scheduler for the application.

@dobord dobord force-pushed the feature/condition_variable branch from 8df6ed8 to 4b1c221 Compare March 24, 2025 15:02
@jbaldwin
Copy link
Owner

How would you feel about splitting out the facade/default_executor to another PR? It feels like its own feature. I might give a stab at the options that you talk about, but I'm also wondering if the defaults for them are good enough?

Regarding the coro::scheduler that works with a backing coro::thread_pool that already exists, since it has two strategies from execution_strategy_t process_tasks_on_thread_pool and process_tasks_inline for how you want it to behave. Perhaps the docs are not clear on this? Maybe I should rename or make it more obvious? Or maybe you mean something else?

@dobord
Copy link
Author

dobord commented Mar 24, 2025

I think it's a great idea to split facade/default_executor into another PR.
Should I remove the coro::default_executor code in this PR? If so, what should I put as the default argument here? explicit strategy_based_on_io_scheduler( std::shared_ptr<io_scheduler> io_scheduler = coro::default_executor::instance()->get_io_scheduler());

@dobord
Copy link
Author

dobord commented Mar 24, 2025

Regarding the coro::scheduler I meant something else. If the project is built without LIBCORO_FEATURE_NETWORKING, in this case it is currently impossible to use coro::io_scheduler to run a coroutine on a threadpool with a time limit or a token for cancellation. But there is a desire to make a new scheduler that would not use epoll for its work, but would use threads.

@dobord dobord marked this pull request as draft March 25, 2025 06:32
dobord and others added 22 commits March 27, 2025 09:23
coro::condition_variable is now an instance of the
coro::condition_variable_base template, which accepts the strategy
parameter
strategy_based_on_io_scheduler::timeout_task
@dobord dobord force-pushed the feature/condition_variable branch from 3c406ba to 82cb974 Compare March 27, 2025 06:23
@dobord dobord marked this pull request as draft March 28, 2025 13:21
@dobord dobord force-pushed the feature/condition_variable branch from ffb8d89 to ed78dc8 Compare March 28, 2025 13:34
@dobord dobord force-pushed the feature/condition_variable branch from ed78dc8 to 17006b1 Compare March 28, 2025 13:57
@dobord dobord marked this pull request as ready for review March 28, 2025 14:14
@dobord dobord changed the title Add condition_variable Add coro::condition_variable Mar 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants