TaskLocal doesn't preserve locals for Task.executeOn #612
Comments
/cc @leandrob13 you available for investigating it? |
@alexandru sure thing! |
@alexandru @Avasil It seems that the writes to TaskLocal must be made from Task.now. This solved the problem: def write(value: A): Task[Unit] =
Task.now(ref.update(value)) The Gonna make the PR. |
We can't make them eager. If we do, then |
Understood. Going to leave the PR I already opened and find the correct solution. |
Sorry for the long delay in response, I had to do play with it myself. I don't like this solution in PR #613 because the problem is how the I also don't think that we need saving and restoring of that context on each trampolined async boundary, I think we need it only for the actually async ones, which the current implementation is failing to do. The real problem is that when an Playing around, I removed all import monix.eval._
import monix.execution.Scheduler
import monix.execution.schedulers.TracingScheduler
object Playground extends TaskApp {
implicit val ec: Scheduler = TracingScheduler(Scheduler.computation(4, "ec1"))
implicit val ec2: Scheduler = TracingScheduler(Scheduler.computation(4, "ec2"))
override protected val scheduler: Coeval[Scheduler] =
Coeval.evalOnce(TracingScheduler(Scheduler.global))
override def runc: Task[Unit] = {
for {
local <- TaskLocal(0)
_ <- local.write(100).executeOn(ec2)
v1 <- local.read.executeOn(ec)
v2 <- local.read.executeOn(ec2)
_ <- Task { println(s"$v1, $v2") }
} yield ()
}
} So going forward, one possible solution when detecting Another one would be something like what @leandrob13 has been doing in #613, but without the duplicated work. Going away for the Easter holiday, will be back on Tuesday to get to the bottom of this. |
@alexandru just saw your comment, I will be testing this asap. Got caught up with #624. |
@alexandru I remembered it was my first approach to the problem, let the |
Indeed, having TaskRunLoop handle it was my idea — but it’s not
necessarily a good one due to having no control over what happens
in an Async.
Note that having TracingScheduler handle it might be tricky as well. For
example what would happen if the user does an `asyncBoundary(ec)` with a
scheduler reference that isn’t traced? Nothing good I imagine.
|
I think that scenario is covered in the tests (provided by one of the issues @Avasil reported): testAsync("TaskLocal.apply with different schedulers") {
val test =
for {
local <- TaskLocal(0).asyncBoundary(ec2)
_ <- local.write(800).asyncBoundary(ec)
v1 <- local.read.asyncBoundary(ec2)
v2 <- local.read
_ <- Task.now(assertEquals(v1, v2))
} yield ()
test.runAsyncOpt
} Neither ec or ec2 are tracing schedulers. If there is a different use case of def apply(scheduler: Scheduler, options: Options, connection: StackedCancelable): Context = {
val em = scheduler.executionModel
val frameRef = FrameIndexRef(em)
val sch = if (options.localContextPropagation) scheduler.trace else scheduler
Context(sch, options, connection, frameRef)
} The def withScheduler(sch: Scheduler): Context =
copy(scheduler = if (options.localContextPropagation) sch.trace else sch) This should take care of your request of using a |
See code samples below.
asyncBoundary
works fine:executeOn
:Link to the discussion on Gitter
The text was updated successfully, but these errors were encountered: