Skip to content

Commit aa419aa

Browse files
authored
Add possiblity to add suppressed application errors to ErrorMode (#101)
1 parent 2a6baea commit aa419aa

File tree

3 files changed

+27
-12
lines changed

3 files changed

+27
-12
lines changed

core/src/main/scala/ox/ErrorMode.scala

+10-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ trait ErrorMode[E, F[_]] {
3232
/** Adds a suppressed exception to the value being represented by `error`. This is only called if `isError(error)` returns `true`. By
3333
* default, the suppressed exception is discarded and the original value is returned.
3434
*/
35-
def addSuppressed[T](error: F[T], e: Throwable): F[T] = error
35+
def addSuppressedException[T](error: F[T], e: Throwable): F[T] = error
36+
37+
/** Adds a suppressed application error to the value being represented by `error`. This is only called if `isError(error)` returns `true`.
38+
* By default, the suppressed application error is discarded and the original value is returned.
39+
*/
40+
def addSuppressedError[T](error: F[T], e: E): F[T] = error
3641
}
3742

3843
/** An error mode which doesn't allow reporting application errors.
@@ -67,3 +72,7 @@ class UnionMode[E: ClassTag] extends ErrorMode[E, [T] =>> E | T] {
6772
override def pure[T](t: T): E | T = t
6873
override def pureError[T](e: E): E | T = e
6974
}
75+
76+
//
77+
78+
case class SecondaryApplicationError[E](e: E) extends Throwable("Secondary application error reported")

core/src/main/scala/ox/race.scala

+8-3
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,17 @@ def race[E, F[_], T](em: ErrorMode[E, F])(fs: Seq[() => F[T]]): F[T] =
3838
@tailrec
3939
def takeUntilSuccess(failures: Vector[Either[E, Throwable]], left: Int): F[T] =
4040
if left == 0 then
41-
val otherExceptions = failures.tail.collect { case Right(e) => e }
4241
failures.headOption.getOrElse(throw new NoSuchElementException) match
4342
case Left(appError) =>
44-
otherExceptions.foldLeft(em.pureError(appError))(em.addSuppressed)
43+
failures.tail.foldLeft(em.pureError(appError)) {
44+
case (acc, Left(e)) => em.addSuppressedError(acc, e)
45+
case (acc, Right(e)) => em.addSuppressedException(acc, e)
46+
}
4547
case Right(e) =>
46-
otherExceptions.foreach(ee => if e != ee then e.addSuppressed(ee))
48+
failures.tail.foreach {
49+
case Left(ee) => e.addSuppressed(SecondaryApplicationError(ee))
50+
case Right(ee) => if e != ee then e.addSuppressed(ee)
51+
}
4752
throw e
4853
else
4954
result.take() match

core/src/main/scala/ox/supervised.scala

+9-8
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ def supervisedError[E, F[_], T](em: ErrorMode[E, F])(f: OxError[E, F] ?=> F[T]):
5454
// all forks are guaranteed to have finished; some might have ended up throwing exceptions (InterruptedException or
5555
// others), but if the scope ended because of an application error, only that result will be returned, hence we have
5656
// to add the other exceptions as suppressed
57-
if em.isError(scopeResult) then s.addExceptionsAsSuppressed(scopeResult, em) else scopeResult
57+
if em.isError(scopeResult) then s.addSuppressedErrors(scopeResult, em) else scopeResult
5858
catch
5959
case e: Throwable =>
6060
// all forks are guaranteed to have finished: some might have ended up throwing exceptions (InterruptedException or
6161
// others), but only the first one is propagated below. That's why we add all the other exceptions as suppressed.
62-
s.addOtherExceptionsAsSuppressedTo(e)
62+
s.addSuppressedErrors(e)
6363
throw e
6464

6565
private[ox] sealed trait Supervisor[-E]:
@@ -79,6 +79,7 @@ private[ox] class DefaultSupervisor[E] extends Supervisor[E]:
7979
// the result might be completed with: a success marker, an app error, or an exception
8080
private val result: CompletableFuture[ErrorModeSupervisorResult | E] = new CompletableFuture()
8181
private val otherExceptions: java.util.Set[Throwable] = ConcurrentHashMap.newKeySet()
82+
private val otherErrors: java.util.Set[E] = ConcurrentHashMap.newKeySet()
8283

8384
override def forkStarts(): Unit = running.incrementAndGet()
8485

@@ -88,7 +89,7 @@ private[ox] class DefaultSupervisor[E] extends Supervisor[E]:
8889

8990
override def forkException(e: Throwable): Unit = if !result.completeExceptionally(e) then otherExceptions.add(e)
9091

91-
override def forkAppError(e: E): Unit = if !result.complete(e) then otherExceptions.add(SecondaryApplicationError(e))
92+
override def forkAppError(e: E): Unit = if !result.complete(e) then otherErrors.add(e)
9293

9394
/** Wait until the count of all supervised, user forks that are running reaches 0, or until any supervised fork fails with an exception.
9495
*
@@ -98,16 +99,16 @@ private[ox] class DefaultSupervisor[E] extends Supervisor[E]:
9899
*/
99100
def join(): ErrorModeSupervisorResult | E = unwrapExecutionException(result.get())
100101

101-
def addOtherExceptionsAsSuppressedTo(e: Throwable): Throwable =
102+
def addSuppressedErrors(e: Throwable): Throwable =
102103
otherExceptions.forEach(e2 => if e != e2 then e.addSuppressed(e2))
104+
otherErrors.forEach(e2 => e.addSuppressed(SecondaryApplicationError(e2)))
103105
e
104106

105-
def addExceptionsAsSuppressed[F[_], T](r: F[T], errorMode: ErrorMode[E, F]): F[T] =
107+
def addSuppressedErrors[F[_], T](r: F[T], errorMode: ErrorMode[E, F]): F[T] =
106108
var result = r
107-
otherExceptions.forEach(e => result = errorMode.addSuppressed(result, e))
109+
otherExceptions.forEach(e => result = errorMode.addSuppressedException(result, e))
110+
otherErrors.forEach(e => result = errorMode.addSuppressedError(result, e))
108111
result
109112

110113
private[ox] enum ErrorModeSupervisorResult:
111114
case Success
112-
113-
case class SecondaryApplicationError[E](e: E) extends Throwable("Secondary application error reported to the supervisor")

0 commit comments

Comments
 (0)