/** * Returns an effect that races this effect with all the specified effects, * yielding the value of the first effect to succeed with a value. Losers of * the race will be interrupted immediately * * @tsplus static effect/core/io/Effect.Aspects raceAll * @tsplus pipeable effect/core/io/Effect raceAll */ export function raceAll(effects: Collection>) { return (self: Effect): Effect => Do(($) => { const ios = $(Effect.sync(Chunk.from(effects))) const done = $(Deferred.make]>()) const fails = $(Ref.make(ios.size)) return $(Effect.uninterruptibleMask(({ restore }) => Do(($) => { const head = $(self.interruptible.fork) const tail = $(Effect.forEach(ios, (io) => io.interruptible.fork)) const fs = tail.prepend(head) as Chunk> $(fs.reduce( Effect.unit, (io, fiber) => io > fiber.await.flatMap(arbiter(fs, fiber, done, fails)).fork )) const inheritAll = (res: readonly [A | A1, Fiber]) => res[1].inheritAll.as(res[0]) return $( restore(done.await.flatMap(inheritAll)).onInterrupt(() => fs.reduce(Effect.unit, (io, fiber) => io < fiber.interrupt) ) ) }) )) }) } function arbiter( fibers: Chunk>, winner: Fiber, promise: Deferred]>, fails: Ref ) { return (exit: Exit): Effect => { return exit.foldEffect( (e) => fails .modify((c) => [c === 0 ? promise.failCause(e).unit : Effect.unit, c - 1] as const) .flatten, (a) => promise .succeed([a, winner] as const) .flatMap((set) => set ? fibers.reduce( Effect.unit, (io, fiber) => fiber === winner ? io : io.zipLeft(fiber.interrupt) ) : Effect.unit ) ) } }