Skip to content

Instantly share code, notes, and snippets.

@fanf
Created March 10, 2020 12:32
Show Gist options
  • Save fanf/fda33966183f208a40e2b183eef88e65 to your computer and use it in GitHub Desktop.
Save fanf/fda33966183f208a40e2b183eef88e65 to your computer and use it in GitHub Desktop.
Deadlock with ZIO runtime / init in object
object TestRuntime {
object internal {
val platform : _root_.zio.internal.Platform = _root_.zio.internal.Platform.makeDefault()
// a local runtime just to build first zlayer
private val r = Runtime((), platform)
// non memomized env
val env = ZEnv.live
val runtime = Runtime.unsafeFromLayer(env, platform)
// here, everything is fine
runtime.unsafeRun(UIO.effectTotal(println("case 2.1: non blocking")))
runtime.unsafeRun(ZIO.accessM[_root_.zio.blocking.Blocking](_.get.blocking(UIO.effectTotal(println("case 2.2: blocking")))) )
}
def main(args: Array[String]): Unit = {
// in main, this works
{
val platform : _root_.zio.internal.Platform = _root_.zio.internal.Platform.makeDefault()
// a local runtime just to build first zlayer
val r = Runtime((), platform)
val env = ZEnv.live
val runtime = Runtime.unsafeFromLayer(env, platform)
// here, everything is fine
runtime.unsafeRun(UIO.effectTotal(println("case 1.1: non blocking")))
runtime.unsafeRun(ZIO.accessM[_root_.zio.blocking.Blocking](_.get.blocking(UIO.effectTotal(println("case 1.2: blocking")))) )
}
println("done in function, start in object")
// in internal, it blocks
internal
println("done")
/* console output:
case 1.1: non blocking
case 1.2: blocking
done in function, start in object
case 2.1: non blocking
// block forever, thread waiting
"zio-default-blocking-1-555173385@1943" daemon prio=5 tid=0x20 nid=NA runnable
java.lang.Thread.State: RUNNABLE
at com.normation.TestRuntime$internal$$$Lambda$388.1905485420.apply$mcV$sp(Unknown Source:-1) // seems to be "1.2 blocking" effect
at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
at zio.internal.FiberContext.evaluateNow(FiberContext.scala:383) // curZio = nextInstr(effect())
at zio.internal.FiberContext.$anonfun$evaluateLater$1(FiberContext.scala:679)
at zio.internal.FiberContext$$Lambda$224.1368440436.run(Unknown Source:-1)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.lang.Thread.run(Thread.java:834)
*/
}
}
@fanf
Copy link
Author

fanf commented Mar 10, 2020

Definitly a problem with object, if I replace internal by:

  trait Internal {
    def platform: _root_.zio.internal.Platform
    def runtime : Runtime.Managed[ZEnv]
  }

And then:

  val internal = new Internal () {
    val platform : _root_.zio.internal.Platform = _root_.zio.internal.Platform.makeDefault()
    // a local runtime just to build first zlayer
    private val r = Runtime((), platform)


    // non memomized env
    val env = ZEnv.live
    val runtime = Runtime.unsafeFromLayer(env, platform)
    // here, everything is fine
    runtime.unsafeRun(UIO.effectTotal(println("case 2.1: non blocking")))
    runtime.unsafeRun(ZIO.accessM[_root_.zio.blocking.Blocking](_.get.blocking(UIO.effectTotal(println("case 2.2: blocking")))) )
  }

It does not block

@fanf
Copy link
Author

fanf commented Mar 10, 2020

Also works in that case with

    internal.runtime.unsafeRun(UIO.effectTotal(println("case 2.1: non blocking")))
    internal.runtime.unsafeRun(ZIO.accessM[_root_.zio.blocking.Blocking](_.get.blocking(UIO.effectTotal(println("case 2.2: blocking")))) )

@fanf
Copy link
Author

fanf commented Mar 10, 2020

Well, it seems to be more complex than that. It works in unit test, but not in real app, so something may be related to other init order.

@fanf
Copy link
Author

fanf commented Mar 10, 2020

It may be variant of: zio/zio#1841
But just adding lazy is not sufficient:

object zioRuntime {
  object internal {
    lazy val platform : _root_.zio.internal.Platform = _root_.zio.internal.Platform.makeDefault()
    lazy val environment = {
      // that platform is just for init zlayer
      val r = Runtime((), platform)
      r.unsafeRun(ZEnv.live.build.memoize.map(ZLayer(_)).use(ZIO.succeed(_)))
    }
    lazy val runtime = Runtime.unsafeFromLayer(environment, platform)
    runtime.unsafeRun(UIO.effectTotal(println("**** case 1: non blocking")))
    runtime.unsafeRun(ZIO.accessM[_root_.zio.blocking.Blocking](_.get.blocking(UIO.effectTotal(println("**** case 2: blocking")))).provideLayer(environment) )
    runtime.unsafeRun(_root_.zio.blocking.blocking(UIO.effectTotal(println("**** case 3: blocking variant"))).provideLayer(environment))
  }
}

object TestRuntime {
  def main(args: Array[String]): Unit = {
    zioRuntime.internal.runtime.unsafeRun(UIO.effectTotal(println("**** case 10: non blocking")))
    zioRuntime.internal.runtime.unsafeRun(ZIO.accessM[_root_.zio.blocking.Blocking](_.get.blocking(UIO.effectTotal(println("**** case 20: blocking")))).provideLayer(zioRuntime.internal.environment) )
    zioRuntime.internal.runtime.unsafeRun(_root_.zio.blocking.blocking(UIO.effectTotal(println("**** case 30: blocking variant"))).provideLayer(zioRuntime.internal.environment))
    println("done")
}
``` => blocks in the println #2 in object `internal`

@fanf
Copy link
Author

fanf commented Mar 10, 2020

But that works:

class internalRuntime {
    lazy val platform : _root_.zio.internal.Platform = _root_.zio.internal.Platform.makeDefault()
    lazy val environment = {
      // that platform is just for init zlayer
      val r = Runtime((), platform)
      r.unsafeRun(ZEnv.live.build.memoize.map(ZLayer(_)).use(ZIO.succeed(_)))
    }
    lazy val runtime = Runtime.unsafeFromLayer(environment, platform)
    runtime.unsafeRun(UIO.effectTotal(println("**** case 1: non blocking")))
    runtime.unsafeRun(ZIO.accessM[_root_.zio.blocking.Blocking](_.get.blocking(UIO.effectTotal(println("**** case 2: blocking")))).provideLayer(environment) )
    runtime.unsafeRun(_root_.zio.blocking.blocking(UIO.effectTotal(println("**** case 3: blocking variant"))).provideLayer(environment))
  }

object zioRuntime {
  lazy val internal = new internalRuntime()
}
//same TestRuntime

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment