Skip to content

Object encapsulation sometimes required in order for the script to execute without blocking/Timeout #532

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

Closed
dacr opened this issue Jan 9, 2022 · 3 comments · Fixed by #2033
Assignees
Labels
bug Something isn't working script-wrapper

Comments

@dacr
Copy link

dacr commented Jan 9, 2022

As soon as a script have to deal with an execution context, or asynchronous processing such as scala Future, ZIO, cats, ...) it is needed to encapsulate the code within an object in order for the script to execute properly, if not it blocks/timeouts without giving any results.

Ammonite has the same issue, see : com-lihaoyi/Ammonite#802 and I was able in some cases to mitigate the issue by adding an ammonite startup option : amm --class-based ....

I think this is an important issue, as it makes our scripts more verbose by systematically having to encapsulate part of the code within objects, and requiring us then to call a function within that object to start the execution.

See next examples, but if required I can provide more examples as the issue is quite easy to reproduce as soon as you use libraries such as AKKA, ZIO, CATS, DOODLE, ...

I was using scala-cli 0.0.9 in this test and scala3, but it is reproductible with any other scala releases.

KO example : (https://gist.github.com/dacr/47ca242e6d672e0154c5816a0330eea7)

// ---------------------
// using scala 3.1.0
// ---------------------

import scala.concurrent.{Await, ExecutionContext, ExecutionContextExecutor, Future}
import scala.concurrent.duration.*

implicit val ec: ExecutionContextExecutor = ExecutionContext.global

val future =
  for
    message <- Future("Hello world")
    _       <- Future(println(message))
  yield ()

Await.ready(future, 5.second)

OK example : (https://gist.github.com/dacr/13ff195adf279798ed5dcc16da2a33ba)

// ---------------------
// using scala 3.1.0
// ---------------------

import scala.concurrent.{Await, ExecutionContext, ExecutionContextExecutor, Future}
import scala.concurrent.duration.*

object Encapsulated:
  implicit val ec: ExecutionContextExecutor = ExecutionContext.global

  def run()=
    val future =
    for
      message <- Future("Hello world")
      _ <- Future(println(message))
    yield ()
    Await.ready(future, 5.second)

Encapsulated.run()

Executions results :

$ scala-cli https://gist.github.com/dacr/13ff195adf279798ed5dcc16da2a33ba
Compiling project (Scala 3.1.0, JVM)
Compiled project (Scala 3.1.0, JVM)
Hello world
$ scala-cli https://gist.github.com/dacr/47ca242e6d672e0154c5816a0330eea7
...
Caused by: java.util.concurrent.TimeoutException: Future timed out after [5 seconds]
	at scala.concurrent.impl.Promise$DefaultPromise.tryAwait0(Promise.scala:212)
	at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:219)
	at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:104)
	at scala.concurrent.Await$.$anonfun$ready$1(package.scala:174)
	at scala.concurrent.BlockContext$DefaultBlockContext$.blockOn(BlockContext.scala:62)
	at scala.concurrent.Await$.ready(package.scala:124)
	at example$minusko$.<clinit>(47ca242e6d672e0154c5816a0330eea7-master/example-ko.sc:26)
@tpasternak tpasternak self-assigned this Jan 18, 2022
@lwronski lwronski added the bug Something isn't working label Apr 26, 2022
@dacr
Copy link
Author

dacr commented Sep 3, 2022

(scala-cli 0.1.12 - scala 3.1.3 - ZIO 2.0.0)
The described workaround is not a silver bullet :( I found a case when object encapsulation doesn't solve the stuck issue, consider those two ZIO scripts :

(See zio/zio#7301 which is going to be closed)

The first one is stucked for ever, and if we take a look to a thread dump :

10:17 $ scl issue-7301-example-ko.sc
timestamp=2022-09-03T08:17:31.720602031Z level=INFO thread=#zio-fiber-6 message="scanning..." location=<empty>.issue-7301-example-ko$.Photos$.run.macro file=issue-7301-example-ko.scala line=35
2022-09-03 10:18:04
Full thread dump OpenJDK 64-Bit Server VM (17.0.4+8-Ubuntu-122.04 mixed mode, sharing):

"main" #1 prio=5 os_prio=0 cpu=332,43ms elapsed=32,88s tid=0x00007f6254018a80 nid=0x22330 in Object.wait()  [0x00007f625a364000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait([email protected]/Native Method)
	- waiting on <0x0000000450010148> (a zio.internal.OneShot)
	at java.lang.Object.wait([email protected]/Object.java:338)
	at zio.internal.OneShot.get(OneShot.scala:83)
	- locked <0x0000000450010148> (a zio.internal.OneShot)
	at zio.Runtime$UnsafeAPIV1.run(Runtime.scala:133)
	at zio.ZIOAppPlatformSpecific.main$$anonfun$1(ZIOAppPlatformSpecific.scala:48)
	at zio.ZIOAppPlatformSpecific$$Lambda$24/0x0000000800cce8e8.apply(Unknown Source)
	at zio.Unsafe$.unsafeCompat(Unsafe.scala:36)
	at zio.ZIOAppPlatformSpecific.main(ZIOAppPlatformSpecific.scala:48)
	at zio.ZIOAppPlatformSpecific.main$(ZIOAppPlatformSpecific.scala:6)
	at issue$minus7301$minusexample$minusko$Photos$.main(issue-7301-example-ko.sc:25)
	at issue$minus7301$minusexample$minusko$.<clinit>(issue-7301-example-ko.sc:43)
	at issue$minus7301$minusexample$minusko_sc$.main(issue-7301-example-ko.sc:60)
	at issue$minus7301$minusexample$minusko_sc.main(issue-7301-example-ko.sc)
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0([email protected]/Native Method)
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke([email protected]/NativeMethodAccessorImpl.java:77)
	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke([email protected]/DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke([email protected]/Method.java:568)
	at scala.cli.runner.Runner$.main(Runner.scala:15)
	at scala.cli.runner.Runner.main(Runner.scala)

...

"ZScheduler-Worker-1" #15 daemon prio=5 os_prio=0 cpu=109,44ms elapsed=32,71s tid=0x00007f62544c7290 nid=0x22344 in Object.wait()  [0x00007f6214706000]
   java.lang.Thread.State: RUNNABLE
	at issue$minus7301$minusexample$minusko$Photos$$anon$1$$Lambda$481/0x0000000800e2c798.apply(Unknown Source)
	- waiting on the Class initialization monitor for issue$minus7301$minusexample$minusko$
	at zio.ZIO.map$$anonfun$1(ZIO.scala:927)
	at zio.ZIO$$Lambda$87/0x0000000800d3f3e0.apply(Unknown Source)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:822)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:822)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:889)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:874)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:822)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:936)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:889)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:936)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:936)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:889)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:822)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:936)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:822)
	at zio.internal.FiberRuntime.evaluateEffect(FiberRuntime.scala:324)
	at zio.internal.FiberRuntime.start(FiberRuntime.scala:1223)
	at zio.Runtime$UnsafeAPIV1.fork(Runtime.scala:154)
	at zio.Runtime$UnsafeAPIV1.fork(Runtime.scala:137)
	at zio.Runtime.run$$anonfun$1$$anonfun$1$$anonfun$1(Runtime.scala:53)
	at zio.Runtime$$Lambda$375/0x0000000800ddb458.apply(Unknown Source)
	at zio.ZIO$.asyncInterruptUnsafe$$anonfun$1$$anonfun$1$$anonfun$1(ZIO.scala:2631)
	at zio.ZIO$$$Lambda$373/0x0000000800ddacc0.apply(Unknown Source)
	at zio.Unsafe$.unsafeCompat(Unsafe.scala:36)
	at zio.ZIO$.asyncInterruptUnsafe$$anonfun$1$$anonfun$1(ZIO.scala:2631)
	at zio.ZIO$$$Lambda$370/0x0000000800dda260.apply(Unknown Source)
	at zio.ZIOCompanionVersionSpecific.asyncInterrupt$$anonfun$1$$anonfun$1$$anonfun$1(ZIOCompanionVersionSpecific.scala:51)
	at zio.ZIOCompanionVersionSpecific$$Lambda$320/0x0000000800dc7708.apply(Unknown Source)
	at zio.internal.FiberRuntime.initiateAsync(FiberRuntime.scala:615)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:905)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:936)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:889)
	at zio.internal.FiberRuntime.runLoop(FiberRuntime.scala:936)
	at zio.internal.FiberRuntime.evaluateEffect(FiberRuntime.scala:324)
	at zio.internal.FiberRuntime.evaluateMessageWhileSuspended(FiberRuntime.scala:447)
	at zio.internal.FiberRuntime.drainQueueOnCurrentThread(FiberRuntime.scala:217)
	at zio.internal.FiberRuntime.run(FiberRuntime.scala:134)
	at zio.internal.ZScheduler$$anon$3.run(ZScheduler.scala:415)

we are stuck on a Class initialization monitor :

"ZScheduler-Worker-1" #15 daemon prio=5 os_prio=0 cpu=109,44ms elapsed=32,71s tid=0x00007f62544c7290 nid=0x22344 in Object.wait()  [0x00007f6214706000]
   java.lang.Thread.State: RUNNABLE
	at issue$minus7301$minusexample$minusko$Photos$$anon$1$$Lambda$481/0x0000000800e2c798.apply(Unknown Source)
	- waiting on the Class initialization monitor for issue$minus7301$minusexample$minusko$

@dacr
Copy link
Author

dacr commented Apr 2, 2023

With latest release of scala-cli (0.12.1), the behavior is different, it didn't hang anymore but instead it raises an exception :

21:01 $ scl issue-532-example-ko.sc 
Exception in thread "main" java.lang.ExceptionInInitializerError
        at issue$minus532$minusexample$minusko_sc$.main(issue-532-example-ko.sc:49)
        at issue$minus532$minusexample$minusko_sc.main(issue-532-example-ko.sc)
Caused by: java.util.concurrent.TimeoutException: Future timed out after [5 seconds]
        at scala.concurrent.impl.Promise$DefaultPromise.tryAwait0(Promise.scala:248)
        at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:255)
        at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:104)
        at scala.concurrent.Await$.$anonfun$ready$1(package.scala:174)
        at scala.concurrent.BlockContext$DefaultBlockContext$.blockOn(BlockContext.scala:62)
        at scala.concurrent.Await$.ready(package.scala:124)
        at issue$minus532$minusexample$minusko$.<clinit>(issue-532-example-ko.sc:32)
        ... 2 more
java.lang.NoClassDefFoundError: Could not initialize class issue$minus532$minusexample$minusko$

same result using scala 3.2.2 or 3.1.1

And of course if I uses the REPL, no issue it runs fine :

21:03 $ scl
Welcome to Scala 3.2.2 (17.0.6, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
                                                                                                                                                                                                
scala> import scala.concurrent.{Await, ExecutionContext, ExecutionContextExecutor, Future}
     | import scala.concurrent.duration.*
     | 
                                                                                                                                                                                                
scala> implicit val ec: ExecutionContextExecutor = ExecutionContext.global
     | 
val ec: concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl$$anon$3@7d126083[Running, parallelism = 8, size = 0, active = 0, running = 0, steals = 0, tasks = 0, submissions = 0]
                                                                                                                                                                                                
scala> val future =
     |   for
     |     message <- Future("Hello world")
     |     _       <- Future(println(message))
     |   yield ()
     | 
Hello world
val future: scala.concurrent.Future[Unit] = Future(<not completed>)
                                                                                                                                                                                                
scala> Await.ready(future, 5.second)
     | 
val res0: scala.concurrent.Future[Unit] = Future(Success(()))
                                                                                               

@tpasternak tpasternak removed their assignment Apr 4, 2023
@MaciejG604
Copy link
Contributor

Thank You very much @dacr for documenting this issue and spending time on getting it resolved. What You have pointed out about the thread being stuck on a Class initialization monitor seems to be the key to this problem.

#2033 should resolve this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working script-wrapper
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

4 participants