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: docs/faq.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
@@ -89,3 +89,83 @@ If you're seeing this warning and have not changed any of the default configurat
89
89
- If you would like to entirely disable this check, you can do so by overriding the `cpuStarvationCheckInitialDelay` value within `IORuntimeConfig` to `Duration.Inf`
90
90
91
91
Please understand that this warning is essentially never a false negative. Outside of some extremely rare circumstances, it accurately measures the responsiveness of your application runtime. However, as a corollary of this accuracy, it is measuring not only your application runtime, but also the system (and other adjacent processes on that system) on which your application is running. Thus, if you see this warning, it essentially always means that either the checker's definition of "responsiveness" (100 millisecond SLA) is different from what you expected, or it means that there is some legitimate problem *somewhere* in your application, your JVM, your kernel, your host environment, or all of the above.
92
+
93
+
## How do I cancel or timeout `delay` or `blocking`?
94
+
95
+
Under normal circumstances, effects constructed with `delay` or `blocking` are *uncancelable*, meaning that they will suppress any attempt to `cancel` their fiber until the effect completes. A classic example:
96
+
97
+
```scala
98
+
IO.blocking(Thread.sleep(1000))
99
+
```
100
+
101
+
The above will block a thread for 1 second. This is done in a *relatively* safe fashion since the `blocking` constructor will ensure that the compute pool is not starved for threads, but the behavior can still be unintuitive. For example:
This effect will take the full 1 second to complete! This is because the timeout fires at the 100 millisecond mark, but since `blocking` is uncancelable, `IO` will wait for it to complete. This is very much by design since it avoids situations in which resources might leak or be left in an invalid state due to unsupported cancelation, but it can be confusing in scenarios like this.
108
+
109
+
There are two possible ways to address this situation, and the correct one to use depends on a number of different factors. In this particular scenario, `Thread.sleep`*happens* to correctly respect Java `Thread` interruption, and so we can fix this by swapping `blocking` for `interruptible`:
The above will return in 100 milliseconds, raising a `TimeoutException` as expected.
116
+
117
+
However, not *all* effects respect thread interruption. Notably, most things involving `java.io` file operations (e.g. `FileReader`) ignore interruption. When working with this type of effect, we must turn to other means. A simple and naive example:
118
+
119
+
```scala
120
+
defreadBytes(fis: FileInputStream) =
121
+
IO blocking {
122
+
varbytes=newArrayBuffer[Int]
123
+
124
+
vari= fis.read()
125
+
while (i >=0) {
126
+
bytes += i
127
+
i = fis.read()
128
+
}
129
+
130
+
bytes.toArray
131
+
}
132
+
133
+
IO.bracket(
134
+
IO(newFileInputStream(inputFile)))(
135
+
readBytes(_))(
136
+
fis =>IO(fis.close()))
137
+
```
138
+
139
+
In the above snippet, swapping `blocking` for `interruptible` won't actually help, since `fis.read()` ignores `Thread` interruption! However, we can still *partially* resolve this by introducing a bit of external state:
140
+
141
+
```scala
142
+
IO(newAtomicBoolean(false)) flatMap { flag =>
143
+
defreadBytes(fis: FileInputStream) =
144
+
IO blocking {
145
+
varbytes=newArrayBuffer[Int]
146
+
147
+
vari= fis.read()
148
+
while (i >=0&&!flag.get()) {
149
+
bytes += i
150
+
i = fis.read()
151
+
}
152
+
153
+
bytes.toArray
154
+
}
155
+
156
+
valreadAll=IO.bracket(
157
+
IO(newFileInputStream(inputFile)))(
158
+
readBytes(_))(
159
+
fis =>IO(fis.close()))
160
+
161
+
readAll.cancelable(IO(flag.set(true)))
162
+
}
163
+
```
164
+
165
+
This is *almost* the same as the previous snippet except for the introduction of an `AtomicBoolean` which is checked on each iteration of the `while` loop. We then set this flag by using `cancelable` (right at the end), which makes the whole thing (safely!) cancelable, despite the fact that it was defined with `blocking` (note this also works with `delay` and absolutely everything else).
166
+
167
+
It is still worth keeping in mind that this is only a partial solution. Whenever `fis.read()` is blocked, cancelation will wait for that blocking to complete regardless of how long it takes. In other words, we still can't interrupt the `read()` function any more than we could with `interruptible`. All that `cancelable` is achieving here is allowing us to encode a more bespoke cancelation protocol for a particular effect, in this case in terms of `AtomicBoolean`.
168
+
169
+
> Note: It is actually possible to implement a *proper* solution for cancelation here simply by applying the `cancelable` to the inner `blocking` effect and defining the handler to be `IO.blocking(fis.close())`, in addition to adding some error handling logic to catch the corresponding exception. This is a special case however, specific to `FileInputStream`, and doesn't make for as nice of an example. :-)
170
+
171
+
The reason this is safe is it effectively leans entirely on *cooperative* cancelation. It's relatively common to have effects which cannot be canceled by normal means (and thus are, correctly, `uncancelable`) but which *can* be terminated early by using some ancillary protocol (in this case, an `AtomicBoolean`). Note that nothing is magic here and this is still fully safe with respect to backpressure and other finalizers. For example, `fis.close()` will still be run at the proper time, and cancelation of this fiber will only complete when all finalizers are done, exactly the same as non-`uncancelable` effects.
0 commit comments