-
Notifications
You must be signed in to change notification settings - Fork 252
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
support lazy callback #376
Conversation
Please describe the motivation, the design and the use cases more clearly. Also we need a document this. |
has added motivation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I feel the doc may not clear.
Also if I understand correctly, this may not be something we want. This has the semantics as then
semantics. And we should co_await
directly to get the value and write the logics directly instead chaining the callbacks.
This is a regression from then-style futures to coroutine tasks.
It's not relate with then, only allow co_await in callback, as i showed example, we need co_await another coroutine in callback. |
I think this may be over designed. On the detail side, it violates our principles to start new chain of Lazies without binding executors. On the higher level side, if we want to implement semantics "do something after another gets ready", we should use |
So, how to implement the showed example with current collectAny? why don't support lazy callback, lazy is also a function, of course canbe a callback, lazy as callback is common, not over designed. |
std::remove_cvref_t<decltype(*callback)>, size_t, | ||
std::remove_cvref_t<decltype(result)>>; | ||
if constexpr (HasMemberCoAwaitOperator<U>) { | ||
(*callback)(i, std::move(result)).start([](auto&&) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the detail side, it violates our principles to start new chain of Lazies without binding executors.
We could binding executores here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may not be correct since we can change the executor of the current lazy during its execution. See dispatch.
assume we have 4 lazy task: A, B, C ,D. B depends on A and D depends on C. Now we want to start those works. For this PR: A().start([](auto&&)->Lazy<void> { co_await B();});
C().start([](auto&&)->Lazy<void> { co_await D();}); The old solution is: []()->Lazy<void>{
co_await A();
co_await B();
}().start();
[]()->Lazy<void>{
co_await C();
co_await D();
}().start(); |
Or just let collectAny callback support Lazy, don't modify Lazy start? |
As the example showed by @poor-circle (except we still need to set the executor), it is not complicated to do it now.
No. There are million cases that the rules applied to functions can't be applied to coroutines.
I never see any design that a callback can accept a lazy-like task. It is not common for sure. Also one of the main motivations of lazy-like coroutines is to avoid callback(hell)s. It is a dangerous design direction to try to make the callback mechanism more complicated. If we need to deal with the result returned first from collectAny, this is the job of |
https://godbolt.org/z/rcWhsfGMx Lazy coroutine can help to reduce callback hell, but can't stop user write callback hell code with Lazy, it's up to users, current Lazy still can write callback hell code: async_simple::coro::Lazy<void> f1(){
co_return;
}
async_simple::coro::Lazy<void> f2(){
co_return;
}
async_simple::coro::Lazy<void> f3(){
co_return;
}
async_simple::coro::Lazy<void> f4(){
co_return;
}
int main() {
auto foo = []()->async_simple::coro::Lazy<void> {
f1().start([](auto&&){
f2().start([](auto&&){
f3().start([](auto&&){
f4().start([](auto&&){});
});
});
});
co_return;
};
async_simple::coro::syncAwait(foo());
} so i think callback function can't be a Lazy function is not reasonable, we should not do restrict here, let the user choose |
Coroutines in different languages are different things. e.g., the go coroutine is actually a stackful coroutine. And calls a coroutine in other languages may mean to send the coroutines to the executor. In async_simple, it corresponds to Then I don't think we can mimic
No mechanism can stop user writing bad codes. We shouldn't encourage them. |
I don't think the usage is related with languages, it's from application requirement. If we only can do this in statckful coroutine, we need another statckful coroutine library until C++26? About executor it's not a big problem, one solution: pass current executor to lazy callback; another way: let the lazy callback execute immediately with start, the user will use their executer in the callback, and the callback Lazy function give a chance to use co_await in callback function for user. |
It is not about stackful or stackless, it is about we don't have a global by default executor. And the design for I do think it is bad to abusing callbacks with coroutines. |
Ok, got it. |
都是 chinglish 啊,看着脑瓜子疼 |
Why
allow lazy start and collectAny callback support lazy.
motivation
Use a kotlin example to show the motiviation:
The application need to call another coroutine in the select callback. Current collectAny callback don't support Lazy, can't call another coroutine, so need to support the feature.
What is changing
Example