File tree Expand file tree Collapse file tree 2 files changed +50
-5
lines changed Expand file tree Collapse file tree 2 files changed +50
-5
lines changed Original file line number Diff line number Diff line change @@ -15,6 +15,7 @@ import java.util.Objects
15
15
16
16
import scala .util .{Failure , Success , Try }
17
17
import scala .concurrent .{ExecutionContext , Future , Promise }
18
+ import scala .util .control .NonFatal
18
19
19
20
/** The base class for state machines generated by the `scala.async.Async.async` macro.
20
21
* Not intended to be directly extended in user-written code.
@@ -34,12 +35,14 @@ abstract class FutureStateMachine(execContext: ExecutionContext) extends Functio
34
35
/** Assign `i` to the state variable */
35
36
protected def state_= (s : Int ): Unit = state$async = s
36
37
38
+ NonFatal // eagerly classloading NonFatal to reduce the chance of a cascading StackOverflowError in `completeFailure`
39
+
37
40
/** Complete the state machine with the given failure. */
38
- // scala-async accidentally started catching NonFatal exceptions in:
39
- // https://github.com/scala/scala-async/commit/e3ff0382ae4e015fc69da8335450718951714982#diff-136ab0b6ecaee5d240cd109e2b17ccb2R411
40
- // This follows the new behaviour but should we fix the regression?
41
- protected def completeFailure ( t : Throwable ) : Unit = {
42
- result$async.complete( Failure (t))
41
+ protected def completeFailure ( t : Throwable ) : Unit = t match {
42
+ case NonFatal (t) =>
43
+ result$async.complete( Failure (t))
44
+ case _ =>
45
+ throw t
43
46
}
44
47
45
48
/** Complete the state machine with the given value. */
Original file line number Diff line number Diff line change
1
+ package scala .async
2
+
3
+ import java .util .concurrent .atomic .AtomicReference
4
+
5
+ import org .junit .{Assert , Ignore , Test }
6
+
7
+ import scala .async .Async .{async , await }
8
+ import scala .concurrent .{ExecutionContext , Future , _ }
9
+ import scala .language .postfixOps
10
+ import scala .util .control .NonFatal
11
+
12
+ class ExceptionalTest {
13
+ @ Test
14
+ def nonFatalNotCaughtFutureCombinators (): Unit = {
15
+ check { implicit ec =>
16
+ Future .successful(42 ).map(x => (x, throw fatal))
17
+ }
18
+ }
19
+
20
+ @ Test
21
+ def nonFatalNotCaughtAsync (): Unit = {
22
+ check { implicit ec =>
23
+ async {
24
+ (await(Future .successful(42 )), throw fatal)
25
+ }
26
+ }
27
+ }
28
+
29
+ def check (f : ExecutionContext => Future [Any ]): Unit = {
30
+ val lastUncaught = new AtomicReference [Throwable ]()
31
+ implicit val executor : ExecutionContextExecutor = ExecutionContext .fromExecutor(null , lastUncaught.set(_))
32
+ val future = f(executor)
33
+ Thread .sleep(100 )
34
+ Assert .assertSame(fatal, lastUncaught.get())
35
+ }
36
+
37
+ private val fatal : Throwable = {
38
+ val t = new VirtualMachineError () {}
39
+ Assert .assertTrue(NonFatal .unapply(t).isEmpty)
40
+ t
41
+ }
42
+ }
You can’t perform that action at this time.
0 commit comments