8000 Do not destroy co-routines too early. by rgriebl · Pull Request #26 · qcoro/qcoro · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Do not destroy co-routines too early. #26

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

Closed
wants to merge 1 commit into from
Closed

Conversation

rgriebl
Copy link
@rgriebl rgriebl commented Dec 6, 2021

Co-routines returning immediately with co_return would have an invalid
result, because the co-routine handle was destroyed before the caller
had a chance to get the result.

We do have to make sure to still destroy co-routines in final-suspend,
if nobody is awaiting the result (e.g. when called from Qt's event
loop).

Fixes: #24

@danvratil
Copy link
Collaborator

Generally this look OK, thanks a lot for working on the patch!

I think we need to extend the test suite to cover all of the possible cases, including your original case in the bug report, as well as other cases when the Task is ignored (but has a value).

I have some tests locally, I'll push them when I get to my home computer and try to write some more :-)

@danvratil
Copy link
Collaborator

Hi. I've merged some additional test cases for Task (see #29) - unfortunately the tests don't pass with your patch. I'll get back to you when I figure out what's wrong :)

@danvratil
Copy link
Collaborator

With some small corrections to the tests, the sync coroutine test passes, but all the other tests fail with use-after-free error.

Looks like when we destroy the coroutine in ~Task, it causes a use-after-free error in TaskFinalSuspend afterwards, since it checks whether the coroutine is done or not, using a handle to the now-destroyed coroutine.

Co-routines returning immediately with co_return would have an invalid
result, because the co-routine handle was destroyed before the caller
had a chance to get the result.

We do have to make sure to still destroy co-routines in final-suspend,
if nobody is awaiting the result (e.g. when called from Qt's event
loop).

Fixes: qcoro#24
@rgriebl
Copy link
Author
rgriebl commented Dec 8, 2021

I have a fix for the use-after-free. The problem comes from the resuming of the mAwaitingCoroutine in final_suspend: when the resume() call finally returns, ~Task() may have been run already and it would have destroyed the finishedCoroutine. Sadly there's no easy way (e.g. using a smart pointer) to detect that the finishedCoroutine handle is dead and dangling. The only way I found around this problem is another bool flag to notify ~Task that the coroutine is already stuck in final_suspend.

With this version, all your tests on main pass, but I had to fix the life-time of the TestContext object like this:


diff --git a/tests/testobject.h b/tests/testobject.h
index 84559a1..422a930 100644
--- a/tests/testobject.h
+++ b/tests/testobject.h
@@ -66,8 +66,9 @@ protected:
         QEventLoop el;
         QTimer::singleShot(5s, &el, [&el]() mutable { el.exit(1); });
 
+{
         [[maybe_unused]] const auto unused = (static_cast<TestClass *>(this)->*testFunction)(el);
-
+}
         bool testFinished = el.property("testFinished").toBool();
         const bool shouldNotSuspend = el.property("shouldNotSuspend").toBool();
         if (testFinished) {

@github-actions
Copy link
Contributor
github-actions bot commented Dec 8, 2021

Unit Test Results

  12 files  +  5  12 suites  +5   53m 14s ⏱️ + 49m 59s
  13 tests ±  0    2 ✔️  - 10  0 💤 ±0    11 +  10 
146 runs  +62  24 ✔️  - 58  0 💤 ±0  122 +120 

For more details on these failures, see this check.

Results for commit 8b7b312. ± Comparison against base commit 194a6ef.

Copy link
Collaborator
@danvratil danvratil left a comment

Choose a reason for hiding this comment

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

Hi @rgriebl , sorry for the huge delay in review. I tried to come up with some solution myself but got stuck and eventually distracted by other work. Sadly, the holidays weren't as peaceful as I hoped :-)

Anyway, I please add the scope fix for the test coroutine that you posted in your comment, I think we could land the patch this way.

I'll try to get back to reworking the Task in some way that will solve this problem in some generic way.

Comment on lines +490 to +493
if (mCoroutine.done() && !mCoroutine.promise().isInFinalSuspend())
mCoroutine.destroy();
else
mCoroutine.promise().setDestroyOnFinalSuspend();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add braces, even for one-line if blocks.

@danvratil
Copy link
Collaborator

Hi,

there was another patch to solve this issue (#35) that goes basically the same way as yours, but the code is simpler (one atomics fewer) and has passing tests. I decided to merge that one. Apologies for not picking up your patch, hope you don't mind.

@danvratil danvratil closed this Jan 5, 2022
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.

Early co_returns do not resume the caller
2 participants
0