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

Java interop doc update #556

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Java interop doc update #556

wants to merge 5 commits into from

Conversation

jatcwang
Copy link

@jatcwang jatcwang commented Mar 23, 2024

Rough draft for now. Any feedback/nitpick welcome :)

Async[F].delay {
val jContext: JContext = ctx.underlying
val _ = jContext.makeCurrent()
}.flatMap(_ => k(cb))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is problematic because there's no guarantee that delay and the subsequent flatMap are executed together. Any ideas?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we can use evalOn with a custom ExecutonContext:

def tracedContext(ctx: JContext): ExecutionContext =
  new ExecutionContext {
    def execute(runnable: Runnable): Unit = {
      val scope = ctx.makeCurrent()
      try {
        runnable.run()
      } finally {
        scope.close()
      }
    }
    def reportFailure(cause: Throwable): Unit =
      cause.printStackTrace()
  }
  
def asyncWithContext[F[_]: Async, A](k: (Either[Throwable, A] => Unit) => F[Option[F[Unit]]])(implicit L: Local[F, Context]): F[A] =
  Local[F, Context].ask.flatMap { ctx =>
    Async[F].evalOn(
      Async[F].async[A](cb => k(cb)), 
      tracedContext(ctx.underlying)
    )
  }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But implications on performance are unknown :(

Copy link
Contributor

@iRevive iRevive Mar 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

evalOn is applicable for all scenarios, actually. But again, it may (and highly likely will) affect the performance.

def evalWithJContext[F[_]: Async, A](fa: F[A])(implicit L: Local[F, Context]): F[A] = 
  Local[F, Context].ask.flatMap { ctx =>
    Async[F].evalOn(fa, tracedContext(ctx.underlying))
  }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea :) Why do you think it'll affect performance (assuming fa pretty much immediately calls the async API instead of doing a bunch of CPU work)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK context shifts may be costly.

The current implementation of the tracedContext is basically a same-thread execution.

Here is the logic for the evalOn in the CE: https://github.com/typelevel/cats-effect/blob/3cf97ae4fd643f333febb4554af2fb603ed2c9d2/core/shared/src/main/scala/cats/effect/IOFiber.scala#L976-L988.

If I get it right, fa will be executed right within the fiber's loop.

@armanbilge perhaps you have some insides?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added your suggestion for now. I'm thinking that a typical async API involves using another threadpool so context shift isn't avoidable in that case. (But yeah will be good if an additional context shift can be avoided)

createOtel4s[IO].flatMap(otel4s => program(otel4s))
def run: IO[Unit] =
OtelJava.autoConfigured[IO]() { otel4s =>
implicit val local: Local[IO, Context] = otel4s.localContext
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
implicit val local: Local[IO, Context] = otel4s.localContext
import otel4s.localContext

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@jatcwang jatcwang marked this pull request as ready for review May 20, 2024 17:26
@jatcwang
Copy link
Author

Seems like one of the builds had an OOM. If someone can help me retrigger it it should pass 🤞

Comment on lines +204 to +216
def tracedContext(ctx: JContext): ExecutionContext =
new ExecutionContext {
def execute(runnable: Runnable): Unit = {
val scope = ctx.makeCurrent()
try {
runnable.run()
} finally {
scope.close()
}
}
def reportFailure(cause: Throwable): Unit =
cause.printStackTrace()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, just getting caught up ...

Hmm, I'm concerned by this. If I'm reading correctly you have effectively implemented a ParasiticExecutionContext. If you run arbitrary effects on this you may stackoverflow and/or dead-lock.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants