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

supervised scopes do not run finalisers when used with never #174

Closed
bishabosha opened this issue Jul 9, 2024 · 9 comments
Closed

supervised scopes do not run finalisers when used with never #174

bishabosha opened this issue Jul 9, 2024 · 9 comments

Comments

@bishabosha
Copy link
Contributor

bishabosha commented Jul 9, 2024

The following pattern (used by Tapir's NettySyncServer startAndWait) seems to not respect finalisers of a scope if the never method is used.

import ox.*

@main def launch =
  supervised:
    releaseAfterScope:
      ???
    never

quitting an app with ctrl-c does not run the finaliser - or at least any side effect I tried such as printing does not appear to do anything.

So either this pattern should not be recommended if it is unsolvable (and fixed in Tapir), or there is a bug in the implementation.

@bishabosha bishabosha changed the title Ctrl-c does not run finalisers when used with never supervised scopes do not run finalisers when used with never Jul 9, 2024
@bishabosha
Copy link
Contributor Author

I didn't verify but I guess there is an interrupted exception and that bypasses everything

@adamw
Copy link
Member

adamw commented Jul 9, 2024

Can you try 0.3.0 & OxApp? https://ox.softwaremill.com/latest/oxapp.html cc @lbialy

@bishabosha
Copy link
Contributor Author

bishabosha commented Jul 9, 2024

so if I try the exact same code then it still swallows the exception

import ox.*

object launch extends OxApp.Simple:
  def run(using Ox): Unit =
    supervised:
      releaseAfterScope:
        ??? // (here!)
      never

edit: actually now if I put a println("cancelled") at (here!) then it works, but throwing an exception vanishes - is that expected?

@bishabosha
Copy link
Contributor Author

If I do my own little homebrew version - it seems the shutdown hook should be setup as soon as possible:

import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.locks.LockSupport
import java.util.concurrent.Executors

class MyOx(val finalizers: AtomicReference[List[() => Unit]] = AtomicReference(List.empty))

def releaseAfterScope(using myox: MyOx)(f: => Unit): Unit =
  myox.finalizers.updateAndGet((() => f) :: _)

inline def never: Nothing =
  while true do
    LockSupport.park()
    if Thread.interrupted() then throw new InterruptedException()
  ???

def mysupervised(f: MyOx ?=> Unit): Unit =
  val myox = MyOx()
  try f(using myox)
  catch
    case e: InterruptedException =>
      println("interrupted supervised")
      Thread.currentThread().interrupt()
  finally
    println("finalizing supervised")
    myox.finalizers.getAndSet(List.empty).foreach(_())

object Runner:

  def main(args: Array[String]): Unit =
    val thread = Thread.ofVirtual().start: () =>
      mysupervised:
        releaseAfterScope:
          println("cancelled")
        never
    sys.addShutdownHook:
      println("shutdown")
      thread.interrupt()
      thread.join()
      println("shutdown done")
    println("joining")
    thread.join()
    println("joined")

running this:

scala run .
Compiling project (Scala 3.5.0-RC3, JVM (21))
Compiled project (Scala 3.5.0-RC3, JVM (21))
joining
^Cshutdown
interrupted supervised
finalizing supervised
cancelled
joined
shutdown done

@bishabosha
Copy link
Contributor Author

Actually @adamw now with ox 0.3.0 and the OxApp the interruption does cause println in releaseAfterScope to work - but for some reason exceptions (e.g. ??? are swallowed)

@adamw
Copy link
Member

adamw commented Jul 9, 2024

So the problem is with exceptions being thrown from finalizers? Where would you expect to see this exception?

Not sure how to handle those ... when there's an interrupt, all forks that are running will end up with an exception. Most of these should be InterruptedException - I guess these should be ignored?

Maybe we should have another (overridable, or as part of OxAppConfiguration) method which is called for the exception with which the main scope ends, and then it could by default properly report any that are not InterruptedExceptions?

@bishabosha
Copy link
Contributor Author

bishabosha commented Jul 9, 2024

I guess this is now a different issue - how to handle errors - so maybe this one can be considered fixed? - seems the JDK realises it's enough of an ambiguity to offer different kinds of error handling in its structured task scope https://docs.oracle.com/en/java/javase/21/core/structured-concurrency.html#GUID-2EF450F4-58CA-4D30-AF86-8AAB92B2AD16

@adamw
Copy link
Member

adamw commented Jul 9, 2024

Sure, we can open another issue then :)

@bishabosha
Copy link
Contributor Author

fixed by #175

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

No branches or pull requests

2 participants