+ The concept of read/write locks and running blocking and cancellable read actions is explained in + the Threading section: +
+ + +This section explains running read actions (RA) in coroutines specifically.
+ +
+ Running RA from coroutines is executed with *ReadAction*
functions from
+
+ coroutines.kt
+
+ (see their KDocs for the details).
+ Functions can be divided into two groups, which differ in reacting to an incoming write action (WA):
+
Write Allowing Read Action (WARA) | +Write Blocking Read Action (WBRA) | +
readAction |
+ readActionBlocking |
+
smartReadAction |
+ smartReadActionBlocking |
+
constrainedReadAction |
+ constrainedReadActionBlocking |
+
WARA is canceled when a parent coroutine is canceled or a WA arrives.
+ ++ WBRA is canceled only when a parent coroutine is canceled. + It blocks WA until finishing its lambda. +
+ +
+ It is important to note that in the coroutines context, default functions
+ (without the Blocking
suffix) behavior prioritizes WA.
+
+ In contrast, in the non-coroutine context,
+
+ Application.runReadAction
+
+ and similar methods (without any prefix/suffix) perform RA blocking WA, whereas RA allowing WA are invoked via
+ the NonBlockingReadAction
API.
+
Be careful when migrating the code running read actions to coroutines.
+
+ WARA API is simpler than NonBlockingReadAction
(NBRA).
+ WARA doesn't need the following API methods:
+
submit(Executor backgroundThreadExecutor)
because this is a responsibility of the coroutine
+ dispatcher
+ executeSynchronously()
because effectively they are executed in the current coroutine dispatcher
+ already
+ expireWhen(BooleanSupplier expireCondition)
,
+ expireWith(Disposable parentDisposable)
,
+ and wrapProgress(ProgressIndicator progressIndicator)
because they are canceled when the calling
+ coroutine is canceled
+
+ finishOnUiThread()
because this is handled by switching to the
+ EDT dispatcher.
+ Note that the UI data must be pure (e.g., strings/icons/element pointers), which inherently cannot
+ be invalidated during the transfer from a background thread to EDT.
+
+ In the case of using NBRA's finishOnUiThread
to start a write action, the coroutine equivalent
+ is readAndWriteAction
:
+
+ It provides the same guarantees as finishOnUIThread
+ (no WA between computeDataInReadAction
and applyData
),
+ but it is not bound to EDT.
+
+ coalesceBy(Object ... equality)
because this should be handled by
+
+ Flow.collectLatest()
+
+ and/or
+
+ Flow.distinctUntilChanged()
+ .
+ Usually, NBRAs are run as a reaction to user actions, and there might be multiple NBRAs running, even
+ if their results are unused.
+ Instead of cancelling the read action, in the coroutine world the coroutines are canceled:
+
Suspending read actions use coroutines as the underlying framework.
+ +
+ WARA (invoked with mentioned *ReadAction
functions)
+ may make several attempts to execute its lambda.
+ The block needs to know whether the current attempt was canceled.
+ *ReadAction
functions create a child
+
+ Job
+
+ for each attempt, and this job becomes canceled when a write action arrives.
+ *ReadAction
restarts the block if it was canceled by a write action, or throws
+ CancellationException
if the calling coroutine was canceled, causing the cancellation
+ of the child Job
.
+
+ To check whether the current action was canceled, clients must call ProgressManager.checkCanceled()
, which was adjusted to work in coroutines.
+ Clients must not throw ProcessCanceledException
manually.
+
+ Read actions must be short. + Technically, it is possible to allow suspension during the read action, but it is complex to implement, + and it still might be surprising: +
+ +Also, it is impossible to solve this with a continuation interceptor like:
++ It's impossible to give it suspending semantics: the interceptor will block its thread waiting for + the read lock. + The interceptors should not be used for that. +
+ ++ As of Kotlin 1.5.x, it is not possible to combine interceptors and dispatchers. + Only one of them can exist in the context: +
+ +Even if that wasn't the case, the following code will work unexpectedly:
+ +