@@ -34,15 +34,16 @@ def forkError[E, F[_], T](using OxError[E, F])(f: => F[T]): Fork[T] =
3434 val oxError = summon[OxError [E , F ]]
3535 // the separate result future is needed to wait for the result, as there's no .join on individual tasks (only whole scopes can be joined)
3636 val result = new CompletableFuture [T ]()
37- oxError.scope.fork { () =>
37+
38+ oxError.herd.startThread:
3839 val supervisor = oxError.supervisor
3940 try
4041 val resultOrError = f
4142 val errorMode = oxError.errorMode
4243 if errorMode.isError(resultOrError) then
4344 // result is never completed, the supervisor should end the scope
4445 supervisor.forkAppError(errorMode.getError(resultOrError))
45- else result.complete(errorMode.getT(resultOrError))
46+ else result.complete(errorMode.getT(resultOrError)).discard
4647 catch
4748 case e : Throwable =>
4849 // we notify the supervisor first, so that if this is the first failing fork in the scope, the supervisor will
@@ -51,7 +52,7 @@ def forkError[E, F[_], T](using OxError[E, F])(f: => F[T]): Fork[T] =
5152 // completing the result; any joins will end up being interrupted
5253 if ! supervisor.forkException(e) then result.completeExceptionally(e).discard
5354 end try
54- }
55+
5556 new ForkUsingResult (result) {}
5657end forkError
5758
@@ -81,7 +82,8 @@ def forkUserError[E, F[_], T](using OxError[E, F])(f: => F[T]): Fork[T] =
8182 val oxError = summon[OxError [E , F ]]
8283 val result = new CompletableFuture [T ]()
8384 oxError.supervisor.forkStarts()
84- oxError.scope.fork { () =>
85+
86+ oxError.herd.startThread:
8587 val supervisor = oxError.supervisor.asInstanceOf [DefaultSupervisor [E ]]
8688 try
8789 val resultOrError = f
@@ -96,7 +98,7 @@ def forkUserError[E, F[_], T](using OxError[E, F])(f: => F[T]): Fork[T] =
9698 case e : Throwable =>
9799 if ! supervisor.forkException(e) then result.completeExceptionally(e).discard
98100 end try
99- }
101+
100102 new ForkUsingResult (result) {}
101103end forkUserError
102104
@@ -112,11 +114,13 @@ end forkUserError
112114 */
113115def forkUnsupervised [T ](f : => T )(using OxUnsupervised ): UnsupervisedFork [T ] =
114116 val result = new CompletableFuture [T ]()
115- summon[OxUnsupervised ].scope.fork { () =>
116- try result.complete(f)
117- catch case e : Throwable => result.completeExceptionally(e)
118- }
117+
118+ summon[OxUnsupervised ].herd.startThread:
119+ try result.complete(f).discard
120+ catch case e : Throwable => result.completeExceptionally(e).discard
121+
119122 new ForkUsingResult (result) with UnsupervisedFork [T ] {}
123+ end forkUnsupervised
120124
121125/** For each thunk in the given sequence, starts a fork using [[fork ]]. All forks are guaranteed to complete before the enclosing
122126 * [[supervised ]] or [[unsupervised ]] block completes.
@@ -149,21 +153,27 @@ def forkCancellable[T](f: => T)(using OxUnsupervised): CancellableFork[T] =
149153 // interrupt signal
150154 val done = new Semaphore (0 )
151155 val ox = summon[OxUnsupervised ]
152- ox.scope.fork { () =>
153- val nestedOx = OxError (NoOpSupervisor , NoErrorMode )
154- scopedWithCapability(nestedOx) {
155- nestedOx.scope.fork { () =>
156- // "else" means that the fork is already cancelled, so doing nothing in that case
157- if ! started.getAndSet(true ) then
158- try result.complete(f).discard
159- catch case e : Throwable => result.completeExceptionally(e).discard
160-
161- done.release() // the nested scope can now finish
156+
157+ ox.herd.startThread:
158+ try
159+ val nestedOx = OxError (NoOpSupervisor , NoErrorMode )
160+ scopedWithCapability(nestedOx) {
161+ nestedOx.herd.startThread {
162+ // "else" means that the fork is already cancelled, so doing nothing in that case
163+ if ! started.getAndSet(true ) then
164+ try result.complete(f).discard
165+ catch case e : Throwable => result.completeExceptionally(e).discard
166+
167+ done.release() // the nested scope can now finish
168+ }.discard
169+
170+ done.acquire()
162171 }
172+ catch
173+ // if this thread was interrupted, any context is already captured as part of the thread started in the nested scope
174+ // hence, ignoring this exception, so that it's not logged by the uncaught exception handler
175+ case e : InterruptedException =>
163176
164- done.acquire()
165- }
166- }
167177 new ForkUsingResult (result) with CancellableFork [T ]:
168178 override def cancel (): Either [Throwable , T ] =
169179 cancelNow()
0 commit comments