You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CppCoreGuidelines.md
+80Lines changed: 80 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15023,6 +15023,8 @@ This section focuses on uses of coroutines.
15023
15023
Coroutine rule summary:
15024
15024
15025
15025
* [CP.51: Do not use capturing lambdas that are coroutines](#Rcoro-capture)
15026
+
* [CP.52: Do not hold locks or other synchronization primitives across suspension points](#Rcoro-locks)
15027
+
* [CP.53: Parameters to coroutines should not be passed by reference](#Rcoro-reference-parameters)
15026
15028
15027
15029
### <a name="Rcoro-capture"></a>CP.51: Do not use capturing lambdas that are coroutines
15028
15030
@@ -15082,6 +15084,84 @@ Use a function for coroutines.
15082
15084
Flag a lambda that is a coroutine and has a non-empty capture list.
15083
15085
15084
15086
15087
+
### <a name="Rcoro-locks"></a>CP.52: Do not hold locks or other synchronization primitives across suspension points
15088
+
15089
+
##### Reason
15090
+
15091
+
This pattern creates a significant risk of deadlocks. Some types of waits will allow the current thread to perform additional work until the asynchronous operation has completed. If the thread holding the lock performs work that requires the same lock then it will deadlock because it is trying to acquire a lock that it is already holding.
15092
+
15093
+
If the coroutine completes on a different thread from the thread that acquired the lock then that is undefined behavior. Even with an explicit return to the original thread an exception might be thrown before coroutine resumes and the result will be that the lock guard is not destructed.
15094
+
15095
+
##### Example, Bad
15096
+
15097
+
std::mutex g_lock;
15098
+
15099
+
std::future<void> Class::do_something()
15100
+
{
15101
+
std::lock_guard<std::mutex> guard(g_lock);
15102
+
co_await something(); // DANGER: coroutine has suspended execution while holding a lock
15103
+
co_await somethingElse();
15104
+
}
15105
+
15106
+
##### Example, Good
15107
+
15108
+
std::mutex g_lock;
15109
+
15110
+
std::future<void> Class::do_something()
15111
+
{
15112
+
{
15113
+
std::lock_guard<std::mutex> guard(g_lock);
15114
+
// modify data protected by lock
15115
+
}
15116
+
co_await something(); // OK: lock has been released before coroutine suspends
15117
+
co_await somethingElse();
15118
+
}
15119
+
15120
+
15121
+
##### Note
15122
+
15123
+
This pattern is also bad for performance. When a suspension point is reached, such as co_await, execution of the current function stops and other code begins to run. It may be a long period of time before the coroutine resumes. For that entire duration the lock will be held and cannot be acquired by other threads to perform work.
15124
+
15125
+
##### Enforcement
15126
+
15127
+
Flag all lock guards that are not destructed before a coroutine suspends.
15128
+
15129
+
### <a name="Rcoro-reference-parameters"></a>CP.53: Parameters to coroutines should not be passed by reference
15130
+
15131
+
##### Reason
15132
+
15133
+
Once a coroutine reaches the first suspension point, such as a co_await, the synchronous portion returns. After that point any parameters passed by reference are dangling. Any usage beyond that is undefined behavior which may include writing to freed memory.
co_return *input + 1; // input is a copy that is still valid here
15151
+
}
15152
+
15153
+
##### Note
15154
+
15155
+
This problem does not apply to reference parameters that are only accessed before the first suspension point. Subsequent changes to the function may add or move suspension points which would reintroduce this class of bug. Some types of coroutines have the suspension point before the first line of code in the coroutine executes, in which case reference parameters are always unsafe. It is safer to always pass by value because the copied parameter will live in the coroutine frame that is safe to access throughout the coroutine.
15156
+
15157
+
##### Note
15158
+
15159
+
The same danger applies to output parameters. [F.20: For "out" output values, prefer return values to output parameters](#Rf-out) discourages output parameters. Coroutines should avoid them entirely.
15160
+
15161
+
##### Enforcement
15162
+
15163
+
Flag all reference parameters to a coroutine.
15164
+
15085
15165
## <a name="SScp-par"></a>CP.par: Parallelism
15086
15166
15087
15167
By "parallelism" we refer to performing a task (more or less) simultaneously ("in parallel with") on many data items.
0 commit comments