add mutex::co_unlock_return()#239
Merged
Merged
Conversation
Contributor
|
Nice, I'll recall whether I have coroutines that unlock the mutex right before return and give it a try, if any. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem Statement
Say we have the following code:
If there was a task waiting for the mutex when we unlock it, there are now 2 tasks to continue: the current task, and the waiting task. There are two ways to handle this:
unlock()will post the waiting task to its executor, and resume the current task inline.co_unlock()will resume the waiting task inline, and post the current task to its executor.However, both of these operations have some inefficiency, because "the current task" is basically done. The only thing that remains is to return a value to the parent and then destroy it. So even if we try to be efficient by calling
co_unlock()to resume the waiting task immediately, we still have to re-post the current task. When that task resumes, it will do almost nothing - just return its value, do parent synchronization, destroy itself, and then possibly resume its parent.Solution
This PR provides an awaitable operation
tmc::mutex::co_unlock_return()that allows us to combine theco_unlock()andco_returnoperations together. In addition to the "unlock and symmetric transfer to a possible awaiter" behavior ofco_unlock(), it also does the full "return value, perform parent synchronization, destroy this, and possibly resume parent" behavior, as if it were hitting the normalco_return/final_suspendpath.This means we can rewrite the example from the problem statement as:
If the current task has no parent, or the parent is running on a different executor, or this task is part of a group that hasn't completed yet, this allows us to skip an entire round-trip through the executor.
Safety
This operation is generally safe and behaves correctly across all combinations of waiting task / parent. The only thing to remember is that this call unconditionally completes the current coroutine, so nothing will run after it in the current scope. Locally scoped objects will be cleaned up immediately (their destructors run as if
co_returnwas called).It has two overloads. Using the wrong overload will give you a compile-time error (same as if you tried to
co_returnthe wrong type).co_unlock_return()returns void (can only be used within atmc::task<void>) and is equivalent to:co_unlock_return(result)returns the Result type (can only be used within atmc::task<Result>) and is equivalent to: