Skip to content

Swapped out static tag approach with ClassValue cache #4266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: series/3.6.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 87 additions & 81 deletions core/shared/src/main/scala/cats/effect/IO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,6 @@ import Platform.static
*/
sealed abstract class IO[+A] private () extends IOPlatform[A] {

private[effect] def tag: Byte

/**
* Like [[*>]], but keeps the result of the source.
*
Expand Down Expand Up @@ -1202,6 +1200,66 @@ private[effect] trait IOLowPriorityImplicits {

object IO extends IOCompanionPlatform with IOLowPriorityImplicits with TupleParallelSyntax {

private[effect] val DispatchTable: ClassValue[Byte] = new ClassValue[Byte] {
// these technically get held indefinitely even though they don't *need* to be
// in theory, this shouldn't matter though, and it makes `computeValue` faster reducing warmup
private[this] val PureClass = classOf[IO.Pure[?]]
private[this] val ErrorClass = classOf[IO.Error]
private[this] val DelayClass = classOf[IO.Delay[?]]
private[this] val ReadTimeClass = IO.RealTime.getClass
private[this] val MonotonicClass = IO.Monotonic.getClass
private[this] val ReadEcClass = IO.ReadEC.getClass
private[this] val MapClass = classOf[IO.Map[?, ?]]
private[this] val FlatMapClass = classOf[IO.FlatMap[?, ?]]
private[this] val AttemptClass = classOf[IO.Attempt[?]]
private[this] val HandleErrorWithClass = classOf[IO.HandleErrorWith[?]]
private[this] val CanceledClass = IO.Canceled.getClass
private[this] val OnCancelClass = classOf[IO.OnCancel[?]]
private[this] val UncancelableClass = classOf[IO.Uncancelable[?]]
private[this] val UnmaskRunLoopClass = classOf[IO.Uncancelable.UnmaskRunLoop[?]]
private[this] val IOContClass = classOf[IO.IOCont[?, ?]]
private[this] val GetClass = classOf[IO.IOCont.Get[?]]
private[this] val CedeClass = IO.Cede.getClass
private[this] val StartClass = classOf[IO.Start[?]]
private[this] val RacePairClass = classOf[IO.RacePair[?, ?]]
private[this] val SleepClass = classOf[IO.Sleep]
private[this] val EvalOnClass = classOf[IO.EvalOn[?]]
private[this] val BlockingClass = classOf[IO.Blocking[?]]
private[this] val LocalClass = classOf[IO.Local[?]]
private[this] val IOTraceClass = IO.IOTrace.getClass
private[this] val ReadRTClass = IO.ReadRT.getClass
private[this] val EndFiberClass = IO.EndFiber.getClass

override def computeValue(clazz: Class[?]): Byte = (clazz: @unchecked) match {
case PureClass => 0
case ErrorClass => 1
case DelayClass => 2
case ReadTimeClass => 3
case MonotonicClass => 4
case ReadEcClass => 5
case MapClass => 6
case FlatMapClass => 7
case AttemptClass => 8
case HandleErrorWithClass => 9
case CanceledClass => 10
case OnCancelClass => 11
case UncancelableClass => 12
case UnmaskRunLoopClass => 13
case IOContClass => 14
case GetClass => 15
case CedeClass => 16
case StartClass => 17
case RacePairClass => 18
case SleepClass => 19
case EvalOnClass => 20
case BlockingClass => 21
case LocalClass => 22
case IOTraceClass => 23
case ReadRTClass => 24
case EndFiberClass => -1
}
}

implicit final def catsSyntaxParallelSequence1[T[_], A](
toia: T[IO[A]]): ParallelSequenceOps1[T, IO, A] = new ParallelSequenceOps1(toia)

Expand Down Expand Up @@ -2202,136 +2260,84 @@ object IO extends IOCompanionPlatform with IOLowPriorityImplicits with TuplePara
// implementations

private[effect] final case class Pure[+A](value: A) extends IO[A] {
def tag = 0
override def toString: String = s"IO($value)"
}

private[effect] final case class Error(t: Throwable) extends IO[Nothing] {
def tag = 1
}
private[effect] final case class Error(t: Throwable) extends IO[Nothing]

private[effect] final case class Delay[+A](thunk: () => A, event: TracingEvent)
extends IO[A] {
def tag = 2
}
private[effect] final case class Delay[+A](thunk: () => A, event: TracingEvent) extends IO[A]

private[effect] case object RealTime extends IO[FiniteDuration] {
def tag = 3
}
private[effect] case object RealTime extends IO[FiniteDuration]

private[effect] case object Monotonic extends IO[FiniteDuration] {
def tag = 4
}
private[effect] case object Monotonic extends IO[FiniteDuration]

private[effect] case object ReadEC extends IO[ExecutionContext] {
def tag = 5
}
private[effect] case object ReadEC extends IO[ExecutionContext]

private[effect] final case class Map[E, +A](ioe: IO[E], f: E => A, event: TracingEvent)
extends IO[A] {
def tag = 6
}
extends IO[A]

private[effect] final case class FlatMap[E, +A](
ioe: IO[E],
f: E => IO[A],
event: TracingEvent)
extends IO[A] {
def tag = 7
}
extends IO[A]

private[effect] final case class Attempt[+A](ioa: IO[A]) extends IO[Either[Throwable, A]] {
def tag = 8
}
private[effect] final case class Attempt[+A](ioa: IO[A]) extends IO[Either[Throwable, A]]

private[effect] final case class HandleErrorWith[+A](
ioa: IO[A],
f: Throwable => IO[A],
event: TracingEvent)
extends IO[A] {
def tag = 9
}
extends IO[A]

private[effect] case object Canceled extends IO[Unit] {
def tag = 10
}
private[effect] case object Canceled extends IO[Unit]

private[effect] final case class OnCancel[+A](ioa: IO[A], fin: IO[Unit]) extends IO[A] {
def tag = 11
}
private[effect] final case class OnCancel[+A](ioa: IO[A], fin: IO[Unit]) extends IO[A]

private[effect] final case class Uncancelable[+A](
body: Poll[IO] => IO[A],
event: TracingEvent)
extends IO[A] {
def tag = 12
}
extends IO[A]

private[effect] object Uncancelable {
// INTERNAL, it's only created by the runloop itself during the execution of `Uncancelable`
final case class UnmaskRunLoop[+A](ioa: IO[A], id: Int, self: IOFiber[?]) extends IO[A] {
def tag = 13
}
final case class UnmaskRunLoop[+A](ioa: IO[A], id: Int, self: IOFiber[?]) extends IO[A]
}

// Low level construction that powers `async`
private[effect] final case class IOCont[K, R](body: Cont[IO, K, R], event: TracingEvent)
extends IO[R] {
def tag = 14
}
extends IO[R]

private[effect] object IOCont {
// INTERNAL, it's only created by the runloop itself during the execution of `IOCont`
final case class Get[A](state: ContState) extends IO[A] {
def tag = 15
}
final case class Get[A](state: ContState) extends IO[A]
}

private[effect] case object Cede extends IO[Unit] {
def tag = 16
}
private[effect] case object Cede extends IO[Unit]

private[effect] final case class Start[A](ioa: IO[A]) extends IO[FiberIO[A]] {
def tag = 17
}
private[effect] final case class Start[A](ioa: IO[A]) extends IO[FiberIO[A]]

private[effect] final case class RacePair[A, B](ioa: IO[A], iob: IO[B])
extends IO[Either[(OutcomeIO[A], FiberIO[B]), (FiberIO[A], OutcomeIO[B])]] {
def tag = 18
}
extends IO[Either[(OutcomeIO[A], FiberIO[B]), (FiberIO[A], OutcomeIO[B])]]

private[effect] final case class Sleep(delay: FiniteDuration) extends IO[Unit] {
def tag = 19
}
private[effect] final case class Sleep(delay: FiniteDuration) extends IO[Unit]

private[effect] final case class EvalOn[+A](ioa: IO[A], ec: ExecutionContext) extends IO[A] {
def tag = 20
}
private[effect] final case class EvalOn[+A](ioa: IO[A], ec: ExecutionContext) extends IO[A]

private[effect] final case class Blocking[+A](
hint: Sync.Type,
thunk: () => A,
event: TracingEvent)
extends IO[A] {
def tag = 21
}
extends IO[A]

private[effect] final case class Local[+A](f: IOLocalState => (IOLocalState, A))
extends IO[A] {
def tag = 22
}
private[effect] final case class Local[+A](f: IOLocalState => (IOLocalState, A)) extends IO[A]

private[effect] case object IOTrace extends IO[Trace] {
def tag = 23
}
private[effect] case object IOTrace extends IO[Trace]

private[effect] case object ReadRT extends IO[IORuntime] {
def tag = 24
}
private[effect] case object ReadRT extends IO[IORuntime]

// INTERNAL, only created by the runloop itself as the terminal state of several operations
private[effect] case object EndFiber extends IO[Nothing] {
def tag = -1
}

private[effect] case object EndFiber extends IO[Nothing]
}

private object SyncStep {
Expand Down
8 changes: 4 additions & 4 deletions core/shared/src/main/scala/cats/effect/IOFiber.scala
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ private final class IOFiber[A](
* The cases have to use continuous constants to generate a `tableswitch`.
* Do not name or reorder them.
*/
(cur0.tag: @switch) match {
(IO.DispatchTable.get(cur0.getClass): @switch) match {
case 0 =>
val cur = cur0.asInstanceOf[Pure[Any]]
runLoop(succeeded(cur.value, 0), nextCancelation, nextAutoCede)
Expand Down Expand Up @@ -332,7 +332,7 @@ private final class IOFiber[A](
if (error == null) succeeded(result, 0) else failed(error, 0)
}

(ioe.tag: @switch) match {
(IO.DispatchTable.get(ioe.getClass): @switch) match {
case 0 =>
val pure = ioe.asInstanceOf[Pure[Any]]
runLoop(next(pure.value), nextCancelation - 1, nextAutoCede)
Expand Down Expand Up @@ -403,7 +403,7 @@ private final class IOFiber[A](
onFatalFailure(t)
}

(ioe.tag: @switch) match {
(IO.DispatchTable.get(ioe.getClass): @switch) match {
case 0 =>
val pure = ioe.asInstanceOf[Pure[Any]]
runLoop(next(pure.value), nextCancelation - 1, nextAutoCede)
Expand Down Expand Up @@ -458,7 +458,7 @@ private final class IOFiber[A](

val ioa = cur.ioa

(ioa.tag: @switch) match {
(IO.DispatchTable.get(ioa.getClass): @switch) match {
case 0 =>
val pure = ioa.asInstanceOf[Pure[Any]]
runLoop(succeeded(Right(pure.value), 0), nextCancelation - 1, nextAutoCede)
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading