-
Notifications
You must be signed in to change notification settings - Fork 92
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
Is async macro leaking? #265
Comments
@hurlebouc While I realize that this is just a reproducer, that syncExecutor implementation is completely broken. I think you can just replace all of it with |
The syncExecutor runs futures in the current thread. It's by design. |
@hurlebouc parasitic is a correct implementation of a current thread ExecutionContext. The |
Ok. |
@hurlebouc No idea, but it's a bit of a two-for-one to reduce the size of the reproducer AND help people avoid problems with their sync ExecutionContexts. :) |
@viktorklang Thank you for the advice. But event replacing |
This is the translation: final class stateMachine$async extends scala.async.FutureStateMachine {
def <init>(): stateMachine$async = {
stateMachine$async.super.<init>(ec);
()
};
override def apply(tr$async: scala.util.Try): Unit = while$(){
try {
stateMachine$async.this.state() match {
case 0 => {
val awaitable$async: scala.concurrent.Future = scala.concurrent.Future.apply({
final <artifact> def $anonfun$apply(): scala.async.Leak.Witness = new scala.async.Leak.Witness();
(() => $anonfun$apply())
}, ec);
tr$async = stateMachine$async.this.getCompleted(awaitable$async);
stateMachine$async.this.state_=(1);
if (null.!=(tr$async))
while$()
else
{
stateMachine$async.this.onComplete(awaitable$async);
return ()
}
}
case 1 => {
<synthetic> val await$1: Object = {
val tryGetResult$async: Object = stateMachine$async.this.tryGet(tr$async);
if (stateMachine$async.this.==(tryGetResult$async))
return ()
else
tryGetResult$async.$asInstanceOf[Object]()
};
await$1;
Leak.this.stop();
stateMachine$async.this.completeSuccess(scala.runtime.BoxedUnit.UNIT);
return ()
}
case _ => throw new IllegalStateException(java.lang.String.valueOf(stateMachine$async.this.state()))
}
} catch {
case (throwable$async @ (_: Throwable)) => {
stateMachine$async.this.completeFailure(throwable$async);
return ()
}
};
while$()
};
override <bridge> <artifact> def apply(v1: Object): Object = {
stateMachine$async.this.apply(v1.$asInstanceOf[scala.util.Try]());
scala.runtime.BoxedUnit.UNIT
}
}; Your analysis is correct: the ANF transform introduces The workaround would be to manually discard the result of the future |
You would have the same issue using object Main {
def stop: Unit = {
Thread.sleep(60000)
println("stop")
}
class Witness
def main(args: Array[String]): Unit = {
Future(new Witness).map(_ => stop)
()
}
} You'll see the same issue above. The problem isn't really the async macro. The problem is the fact that You're basically asking import cats.effect._
import cats.effect.cps._
import scala.concurrent.duration._
object Main extends IOApp.Simple {
val stop: IO[Unit] =
IO.sleep(1.minute) >> IO.println("stop")
class Witness
val run =
async[IO] {
IO(new Witness).await
stop.await
}
} If you run the above and take a heap dump during import cats.effect._
import cats.effect.cps._
import scala.concurrent.duration._
object Main extends IOApp.Simple {
val stop: IO[Unit] =
IO.sleep(1.minute) >> IO.println("stop")
class Witness
val run =
async[IO] {
IO.fromFuture(IO(Future(new Witness))).await
stop.await
}
} Same idea. Since |
The PR turns the capturing At stage or round N of the state machine, we receive the result of the F at N-1, so presumably there is no live reference to the F. F itself should be off the hook. |
old WIP at |
that is this ticket, but my previous PR is liable to revivify at any time. Usually that is on Halloween. |
Hi,
when you run code
and then, during the sleep, you take a heap dump with VisualVM, you see that there is still an object of type
Witness
in the heap. Is this normal?The text was updated successfully, but these errors were encountered: