This section focuses on explaining coroutines in the specific context of the IntelliJ Platform. If you are not experienced with Kotlin Coroutines, it is highly recommended to get familiar with Learning Resources first.
Kotlin's coroutines follow the principle of structured concurrency. It means that each coroutine is run in a specific CoroutineScope, which delimits the lifetime of the coroutine. This ensures that they are not lost and do not leak. An outer scope does not complete until all its child coroutines are completed. Cancellation of the outer scope also cancels its child coroutines. Structured concurrency ensures that any errors in the code are properly reported and never lost.
IntelliJ Platform ScopesIntelliJ Platform provides special coroutine scopes that help ensure proper structured concurrency of coroutines run from the platform or plugin code. After cancellation, the platform awaits the completion of each scope. Using correct parent scopes guarantees that child coroutines will be properly canceled when no longer needed, preventing resource leaks.
The following diagram presents the scopes and their parent-child relationships:
All scopes presented on the diagram are supervisor scopes â they ignore the failures of their children.
Each coroutine scope can have only one actual parent, pointed with solid arrow lines. Dashed arrow lines point to fictional parents, which follow the actual coroutine parent-child semantics:
a parent scope cancels children on its own cancellation
a parent scope awaits children before considering itself complete
a failed child cancels its parent (which effectively is not happening because presented scopes are supervisors)
The ApplicationÃPlugin and ProjectÃPlugin are intersection scopes with two semantic parents (actual and fictional).
Main ScopesRoot - the root scope spans all the coroutines. This is the standard root scope launched with runBlocking
coroutine builder.
Application - a scope associated with the Application
container (component manager) lifetime. It is canceled on application shutdown. This triggers cancellation of the ApplicationÃPlugin scope and, subsequently, its children, including the ProjectÃPlugin scope.
Project - a scope associated with a Project
container (component manager) lifetime. It is canceled when a project is being closed. This triggers the cancellation of the ProjectÃPlugin scope and, subsequently, its children.
Plugin - a scope associated with a plugin lifetime. It is canceled on unloading of the corresponding plugin. This triggers cancellation of the ApplicationÃPlugin scope and, subsequently, its children, including the ProjectÃPlugin scope.
ApplicationÃPlugin - a scope which is an intersection of the Application and Plugin scopes. It is canceled when the application is shutdown or the corresponding plugin is unloaded. This triggers the cancellation of its children and the ProjectÃPlugin scope and, subsequently, its children.
ProjectÃPlugin - a scope which is an intersection of the Project and Plugin scopes. It is canceled when a project is being closed or the corresponding plugin is unloaded.
Intersection scopes enable creating coroutines whose lifetime is limited by application/project and plugin lifetimes, e.g., application/project services provided by a plugin.
Service ScopesThe Application Service and Project Service scopes are bound to an application and project service lifetimes accordingly. They are children of the Intersection Scopes, which means that they are canceled when the application/project is closed or a plugin is unloaded.
The service scope is provided to services via constructor injection. The following constructor signatures are supported:
MyService(CoroutineScope)
for application and project services
MyProjectService(Project, CoroutineScope)
for project services
Each service instance receives its own scope instance. The injected scopes' contexts contain Dispatchers.Default
and CoroutineName(serviceClass)
.
See Launching Coroutine From Service Scope for full samples.
Using a Correct Scope Use Service ScopesIf a plugin requires running some code in a coroutine, the approach recommended in most cases is to create a separate service that will receive its own scope via constructor and launch the coroutine in this scope. This approach guarantees the usage of the correct scope, preventing leaks and canceling wrong scopes and killing all their (e.g., application's or project's) coroutines accidentally.
Note that since 2024.2, AnAction.actionPerformed()
logic can be executed in the current thread coroutine scope.
See the Launching Coroutines section for details.
The following sections describe the potential problems that would occur if the wrong coroutine scopes were used. This allows better understanding of the platform scopes and why the service approach mentioned above must be used.
Do Not Use Application/Project ScopeApplication and Project scopes are exposed with Application.getCoroutineScope()
and Project.getCoroutineScope()
. Never use these methods, as they are deprecated and will be removed in the future.
Using these scopes could easily lead to project or plugin class leaks.
Project leak:
application.coroutineScope.launch { project.getService(PsiDirectoryFactory::class.java) }
Closing the project cancels its scope. The application scope remains active, and the project is leaked.
â â â â â â â â â â â Application Scope Project Scope Project leak LifetimesPlugin leak:
project.coroutineScope.launch { project.getService(MyPluginService::class.java) }
Unloading of the plugin cancels its scope. The project scope remains active, and the plugin classes are leaked.
â â â â â â â â â â â Project Scope Plugin Scope MyPluginService leak LifetimesThere is no API for retrieving ApplicationÃPlugin and ProjectÃPlugin intersection scopes, but let's assume there is a method exposing the ProjectÃPlugin scope:
/** * Returns the correct intersection scope for the project and plugin * by a given plugin class. */ fun Project.getCoroutineScope(pluginClass: Class<*>): CoroutineScope
Using this scope could lead to a plugin leak:
project.getCoroutineScope(PluginBService::class.java).launch { project.getService(PluginAService::class.java) }
Unloading of Plugin A cancels its scope. The Project à Plugin B scope remains active, and the Plugin A classes are leaked.
â â â â â â â â â â â Application Scope Plugin A Scope Project Scope Project à Plugin A Scope Plugin B Scope Project à Plugin B Scope PluginAService leak Correct PluginAService lifetime should end here LifetimesIf a topic is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels.
Be specific about the topics and reasons for adding them and leave your email in case we need more details. Thanks for your feedback!
11 August 2025
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4