Create rule S6307: Suspending functions should be main-safe (#173)
Co-authored-by: johann-beleites-sonarsource <johann-beleites-sonarsource@users.noreply.github.com> Co-authored-by: Johann Beleites <johann.beleites@sonarsource.com> Co-authored-by: Johann Beleites <63855942+johann-beleites-sonarsource@users.noreply.github.com> Co-authored-by: margarita-nedzelska-sonarsource <70522623+margarita-nedzelska-sonarsource@users.noreply.github.com>
This commit is contained in:
parent
217e186529
commit
aa67048f1c
20
rules/S6307/kotlin/metadata.json
Normal file
20
rules/S6307/kotlin/metadata.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"title": "Suspending functions should be main-safe",
|
||||
"type": "CODE_SMELL",
|
||||
"status": "ready",
|
||||
"remediation": {
|
||||
"func": "Constant\/Issue",
|
||||
"constantCost": "10min"
|
||||
},
|
||||
"tags": [
|
||||
"coroutines",
|
||||
"performance",
|
||||
"bad-practice",
|
||||
"pitfall"
|
||||
],
|
||||
"defaultSeverity": "Major",
|
||||
"ruleSpecification": "RSPEC-6307",
|
||||
"sqKey": "S6307",
|
||||
"scope": "All",
|
||||
"defaultQualityProfiles": ["Sonar way"]
|
||||
}
|
62
rules/S6307/kotlin/rule.adoc
Normal file
62
rules/S6307/kotlin/rule.adoc
Normal file
@ -0,0 +1,62 @@
|
||||
Generally speaking, main threads should be available to allow user-facing parts of an application to remain responsive. Long-running blocking operations can significantly reduce threads' availability and are best executed on a designated thread pool.
|
||||
|
||||
As a consequence, suspending functions should not block main threads and instead move any long-running blocking tasks off the main thread. This can be done conveniently by using `withContext` with an appropriate dispatcher. Alternatively, coroutine builders such as `launch` and `async` accept an optional `CoroutineContext`. An appropriate dispatcher could be `Dispatchers.IO` for long-running blocking IO operations, which can create and shutdown threads on demand.
|
||||
|
||||
For some blocking tasks and APIs there may already be suspending alternatives available. When available, these alternatives should be used instead of their blocking counterparts.
|
||||
|
||||
This rule raises an issue when the call of a long-running blocking function is detected within a suspending function without the use of an appropriate dispatcher. If non-blocking alternatives to the called function are known, they may be suggested (e.g. use `delay(...)` instead of `Thread.sleep(...)`).
|
||||
|
||||
== Noncompliant Code Example
|
||||
Executing long-running blocking IO operations on the main thread pool:
|
||||
----
|
||||
class workerClass {
|
||||
suspend fun worker(): String {
|
||||
val client = HttpClient.newHttpClient()
|
||||
val request = HttpRequest.newBuilder(URI("https://example.com")).build()
|
||||
return coroutineScope {
|
||||
client.send(request, HttpResponse.BodyHandlers.ofString()).body() // Noncompliant
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Using inappropriate blocking APIs:
|
||||
----
|
||||
suspend fun example() {
|
||||
...
|
||||
Thread.sleep(1000) // Noncompliant
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
== Compliant Solution
|
||||
Executing long-running blocking IO operations in an appropriate thread pool using `Dispatcher.IO`:
|
||||
----
|
||||
class workerClass(
|
||||
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
) {
|
||||
suspend fun worker(): String {
|
||||
val client = HttpClient.newHttpClient()
|
||||
val request = HttpRequest.newBuilder(URI("https://example.com")).build()
|
||||
return withContext(ioDispatcher) {
|
||||
client.send(request, HttpResponse.BodyHandlers.ofString()).body() // Compliant
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Using appropriate non-blocking APIs:
|
||||
----
|
||||
suspend fun example() {
|
||||
...
|
||||
delay(1000) // Compliant
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
== See
|
||||
|
||||
* https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html[Coroutine context and dispatchers]
|
||||
* https://developer.android.com/kotlin/coroutines/coroutines-best-practices#main-safe[Suspend functions should be safe to call from the main thread] (Android coroutines best practices)
|
||||
* https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-i-o.html[IO CoroutineDispatcher]
|
||||
* https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html[Default CoroutineDispatcher]
|
2
rules/S6307/metadata.json
Normal file
2
rules/S6307/metadata.json
Normal file
@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user