Skip to content
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

Add IORuntime.isUnderFiberContext #4204

Merged
Merged
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
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,7 @@ lazy val tests: CrossProject = crossProject(JSPlatform, JVMPlatform, NativePlatf
)
.jvmSettings(
fork := true,
Test / javaOptions += "-Dcats.effect.ioLocalPropagation=true"
Test / javaOptions += "-Dcats.effect.trackFiberContext=true"
)
.nativeSettings(
Compile / mainClass := Some("catseffect.examples.NativeRunner")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private object IOFiberConstants {
final val AutoCedeR = 7
final val DoneR = 8

final val ioLocalPropagation = false
final val TrackFiberContext = false

@nowarn212
@inline def isVirtualThread(t: Thread): Boolean = false
Expand Down
2 changes: 1 addition & 1 deletion core/jvm/src/main/java/cats/effect/IOFiberConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ final class IOFiberConstants {
static final byte AutoCedeR = 7;
static final byte DoneR = 8;

static final boolean ioLocalPropagation = Boolean.getBoolean("cats.effect.ioLocalPropagation");
static final boolean TrackFiberContext = Boolean.getBoolean("cats.effect.trackFiberContext");

static boolean isVirtualThread(final Thread thread) {
try {
Expand Down
8 changes: 4 additions & 4 deletions core/jvm/src/main/scala/cats/effect/IOLocalPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@

package cats.effect

import IOFiberConstants.ioLocalPropagation
import IOFiberConstants.TrackFiberContext

private[effect] trait IOLocalPlatform[A] { self: IOLocal[A] =>

/**
* Returns a [[java.lang.ThreadLocal]] view of this [[IOLocal]] that allows to unsafely get,
* set, and remove (aka reset) the value in the currently running fiber. The system property
* `cats.effect.ioLocalPropagation` must be `true`, otherwise throws an
* `cats.effect.trackFiberContext` must be `true`, otherwise throws an
* [[java.lang.UnsupportedOperationException]].
*/
def unsafeThreadLocal(): ThreadLocal[A] = if (ioLocalPropagation)
def unsafeThreadLocal(): ThreadLocal[A] = if (TrackFiberContext)
new ThreadLocal[A] {
override def get(): A = {
val fiber = IOFiber.currentIOFiber()
Expand All @@ -51,7 +51,7 @@ private[effect] trait IOLocalPlatform[A] { self: IOLocal[A] =>
else
throw new UnsupportedOperationException(
"IOLocal-ThreadLocal propagation is disabled.\n" +
"Enable by setting cats.effect.ioLocalPropagation=true."
"Enable by setting cats.effect.trackFiberContext=true."
)

}
2 changes: 1 addition & 1 deletion core/jvm/src/main/scala/cats/effect/IOPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ abstract private[effect] class IOPlatform[+A] extends Serializable { self: IO[A]
case _: InterruptedException =>
None
} finally {
if (IOFiberConstants.ioLocalPropagation)
if (IOFiberConstants.TrackFiberContext)
IOLocal.setThreadLocalState(fiber.getLocalState())
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/shared/src/main/scala/cats/effect/IO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ sealed abstract class IO[+A] private () extends IOPlatform[A] {
implicit runtime: unsafe.IORuntime): IOFiber[A @uncheckedVariance] = {

val fiber = new IOFiber[A](
if (IOFiberConstants.ioLocalPropagation) IOLocal.getThreadLocalState()
if (IOFiberConstants.TrackFiberContext) IOLocal.getThreadLocalState()
else IOLocalState.empty,
{ oc =>
if (registerCallback) {
Expand Down
4 changes: 2 additions & 2 deletions core/shared/src/main/scala/cats/effect/IOFiber.scala
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private final class IOFiber[A](
// insert a read barrier after every async boundary
readBarrier()

if (ioLocalPropagation) {
if (TrackFiberContext) {
IOFiber.setCurrentIOFiber(this)
}

Expand All @@ -131,7 +131,7 @@ private final class IOFiber[A](
case 8 => () // DoneR
}

if (ioLocalPropagation) {
if (TrackFiberContext) {
IOFiber.setCurrentIOFiber(null)
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/shared/src/main/scala/cats/effect/IOLocal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ object IOLocal {
/**
* `true` if IOLocal-Threadlocal propagation is enabled
*/
def isPropagating: Boolean = IOFiberConstants.ioLocalPropagation
def isPropagating: Boolean = IOFiberConstants.TrackFiberContext

private[effect] def getThreadLocalState() = {
val fiber = IOFiber.currentIOFiber()
Expand Down
8 changes: 8 additions & 0 deletions core/shared/src/main/scala/cats/effect/unsafe/IORuntime.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ object IORuntime extends IORuntimeCompanionPlatform {
def builder(): IORuntimeBuilder =
IORuntimeBuilder()

/**
* Returns `true` if invoked within an executing `IOFiber`. If the property
* `cats.effect.trackFiberContext=true` then this method is always accurate. Otherwise, it is
* best-effort and may return `false` even when you are executing within an `IOFiber`.
*/
@static def isUnderFiberContext(): Boolean =
IOFiber.currentIOFiber() ne null

private[effect] def testRuntime(ec: ExecutionContext, scheduler: Scheduler): IORuntime = {
val config = IORuntimeConfig()
val metrics = IORuntimeMetrics(ec)
Expand Down
2 changes: 1 addition & 1 deletion docs/core/io-local.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,4 @@ TraceIdScope.fromIOLocal.flatMap { implicit traceIdScope: TraceIdScope[IO] =>

To support integration with Java libraries, `IOLocal` interoperates with the JDK `ThreadLocal` API via `IOLocal#unsafeThreadLocal`. This makes it possible to unsafely read and write the value of an `IOLocal` on the currently running fiber within a suspended side-effect (e.g. `IO.delay` or `IO.blocking`).

To use this feature you must set the property `cats.effect.ioLocalPropagation=true`. Note that enabling propagation causes a performance hit of up to 25% in some of our microbenchmarks. However, it is not clear that this performance impact matters in practice.
To use this feature you must set the property `cats.effect.trackFiberContext=true`. Note that enabling propagation causes a performance hit of up to 25% in some of our microbenchmarks. However, it is not clear that this performance impact matters in practice.
2 changes: 1 addition & 1 deletion docs/core/io-runtime-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ This can be done for example with the [EnvironmentPlugin for Webpack](https://we
| `cats.effect.cpu.starvation.check.interval` <br/> `CATS_EFFECT_CPU_STARVATION_CHECK_INTERVAL` | `FiniteDuration` (`1.second`) | The starvation checker repeatedly sleeps for this interval and then checks `monotonic` time when it awakens. It will then print a warning to stderr if it finds that the current time is greater than expected (see `threshold` below). |
| `cats.effect.cpu.starvation.check.initialDelay` <br/> `CATS_EFFECT_CPU_STARVATION_CHECK_INITIAL_DELAY` | `Duration` (`10.seconds`) | The initial delay before the CPU starvation checker starts running. Avoids spurious warnings due to the JVM not being warmed up yet. Set to `Duration.Inf` to disable CPU starvation checking. |
| `cats.effect.cpu.starvation.check.threshold` <br/> `CATS_EFFECT_CPU_STARVATION_CHECK_THRESHOLD` | `Double` (`0.1`) | The starvation checker will print a warning if it finds that it has been asleep for at least `interval * (1 + threshold)` (where `interval` from above is the expected time to be asleep for). Sleeping for too long is indicative of fibers hogging a worker thread either by performing blocking operations on it or by `cede`ing insufficiently frequently. |
| `cats.effect.ioLocalPropagation` <br/> N/A | `Boolean` (`false`) | Enables `IOLocal`s to be propagated as `ThreadLocal`s. |
| `cats.effect.trackFiberContext` <br/> N/A | `Boolean` (`false`) | Tracks the currently running fiber on each thread. Necessary for `IOLocal`s to be propagated as `ThreadLocal`s. |
Loading