Dead Continuation cause resource leak?

Alex Otenko oleksandr.otenko at gmail.com
Fri May 29 11:28:21 UTC 2020


What I am getting at, is the nonblocking consumer may be given a
nonblocking action, and still it will behave like a blocking consumer. This
change of behaviour is obviously important - one needs to design the
consumer like it were blocking.

Alex

On Fri, 29 May 2020, 12:00 Ron Pressler, <ron.pressler at oracle.com> wrote:

> Blocking (as in I/O or synchronisation) is not the issue. In Java (or in C
> or
> Assmebly for that matter), if you write *any* two statements,
>
>     x;
>     y;
>
> then no matter what x does -- whether or not it throws an exception or
> blocks --
> there is no guarantee that if x executes then y will because there are no
> liveness guarantees on a non-realtime operating system. The kernel is free
> to
> preempt the current thread between x and y and suspend the thread
> indefinitely.
>
> What continuations change is properties related to thread identity.
> Assuming x
> doesn't throw any exceptions (mutatis mutandis if it does), what we know
> is the
> following safety property:
>
>     If x is executing on some thread t0 (i.e. Thread.currentThread() would
>     return a particular Thread object that we'll call t0), then no code
> that's
>     executing outside of any calls x makes will be running on t0 (i.e.
> will get
>     the t0 object from Thread.currentThread()) before y is entered.
>
> This is what continuations change, but virtual threads do not.
>
> I don’t understand the definition of the property for which you seek a
> name :)
>
> — Ron
>
>
>
> On 29 May 2020 at 07:39:24, Alex Otenko (oleksandr.otenko at gmail.com
> (mailto:oleksandr.otenko at gmail.com)) wrote:
>
> > Thanks, that's a very good argument.
> >
> > So in vanilla java we can speak of A() being nonblocking, in which case
> we can speak of B() being eventually reachable. How would you describe the
> property of A() that preserves semantics of return value and exception
> delivery? That is, hands off control to continuations correctly?
> >
> >
> > Alex
> > On Fri, 29 May 2020, 01:05 Ron Pressler, wrote:
> > >
> > > I’m not sure I follow. Suppose you have the following code:
> > >
> > > try {
> > > A();
> > > } finally {
> > > B();
> > > }
> > >
> > > Then Java makes no guarantees that, if you ever enter A, then B is
> eventually
> > > executed. For example, A can sleep forever, or the kernel scheduler
> could starve
> > > the thread.
> > >
> > > In fact, Continuation.run can be implemented in the mainline JDK by
> spawning a
> > > new thread and synchronizing with it. What is semantically different
> would be
> > > matters of thread identity. An example relevant to your situation is
> that two
> > > methods could make concurrent progress while both running on the same
> thread.
> > > The converse would be that the following code,
> > >
> > > var t1 = Thread.currentThread();
> > > A();
> > > var t2 = Thread.currentThread();
> > >
> > > would guarantee that t1 == t2 in the mainline JDK but not in Loom.
> This would
> > > probably be even more troublesome than your example, which is one
> reason why
> > > we're not exposing continuations at this time. If continuations are
> ever
> > > exposed, their behaviour will clearly become a part of the relevant
> Java
> > > specifications.
> > >
> > > These issues do not arise with virtual threads.
> > >
> > > - Ron
> > >
> > >
> > >
> > > On 28 May 2020 at 23:38:59, Alex Otenko (oleksandr.otenko at gmail.com
> (mailto:oleksandr.otenko at gmail.com)(mailto:oleksandr.otenko at gmail.com))
> wrote:
> > >
> > > > When you write an Iterator, you are conscious of writing an
> Iterator. When you are writing a consumer, you expect plain java semantics:
> combining pieces of computation that do terminate, produces a program that
> terminates.
> > > >
> > > > If Continuation is not going to be public, it's less of a concern.
> Although the question of verification of which parts of jls are not
> violated is interesting.
> > > >
> > > > Alex
> > > > On Thu, 28 May 2020, 17:32 Ron Pressler, wrote:
> > > > > What happens if you write an iterator that only performs cleanup
> when it
> > > > > terminates, but then encounters an exception in the middle of
> iteration? How
> > > > > does the consumer know if some cleanup has been missed?
> > > > >
> > > > > Anyway, just to make clear, continuations will *not* be exposed as
> a public
> > > > > class, at least not at first. The current public class will be
> made internal.
> > > > >
> > > > > — Ron
> > > > >
> > > > >
> > > > > On 28 May 2020 at 15:52:01, Alex Otenko (
> oleksandr.otenko at gmail.com(mailto:oleksandr.otenko at gmail.com)(mailto:
> oleksandr.otenko at gmail.com)) wrote:
> > > > >
> > > > > > I get that. What seems to be a problem, is that the language
> promise cannot be easily translated into Continuations.
> > > > > >
> > > > > > Case in point:
> https://github.com/forax/loom-fiber/blob/master/src/main/java/fr.umlv.loom/fr/umlv/loom/Generators.java#L74(https://urldefense.com/v3/__https://github.com/forax/loom-fiber/blob/master/src/main/java/fr.umlv.loom/fr/umlv/loom/Generators.java*L74__;Iw!!GqivPVa7Brio!NU6HHPPmB_c4ddOo6HK_BCS1olDVhg-f-WX1PBEIkCnQCxvU2Ixlw1rWNLIpQas1eA$)(https://urldefense.com/v3/__https://github.com/forax/loom-fiber/blob/master/src/main/java/fr.umlv.loom/fr/umlv/loom/Generators.java*L74(https:/*urldefense.com/v3/__https:/*github.com/forax/loom-fiber/blob/master/src/main/java/fr.umlv.loom/fr/umlv/loom/Generators.java*L74__;Iw!!GqivPVa7Brio!NU6HHPPmB_c4ddOo6HK_BCS1olDVhg-f-WX1PBEIkCnQCxvU2Ixlw1rWNLIpQas1eA$)__;Iy8vKg!!GqivPVa7Brio!KEQObH8AEiF0r5wuxDtHqJBZbGa8oPO7Z5YGkYSqHPxGIV3y79dmil1QmOTbu2PmjQ$)
>
> > > > > >
> > > > > > This is an example, a technology demonstrator, yes, but...
> > > > > >
> > > > > > consumer potentially has a try-finally block encompassing the
> invocation of consumer on lines 54...57. (Consider the closing bracket is
> the actual last statement, so it's easier to see the problem).
> > > > > >
> > > > > > The yield returns us to line 73.
> > > > > >
> > > > > > What happens if this.action.accept throws? I presume we never
> get to line 74, because consumer is oblivious of the need to yield, nor
> does it have a way to communicate that yield has failed.
> > > > > >
> > > > > > Ok, suppose, we reach line 74 due to a Throwable. What reasoning
> can be used to determine that consumer's try-finally does not need
> attending? (That is, returning back to line 57, perhaps with a Throwable)
> > > > > >
> > > > > > Alex
> > > > > > On Thu, 28 May 2020, 12:54 Ron Pressler, wrote:
> > > > > > > But the question was about a liveness property, not a safety
> property. *If* the
> > > > > > > continuation (or any thread, really) terminates *then* finally
> will run (actually,
> > > > > > > this, too, is not a guarantee we make, but that’s a different
> matter). If a
> > > > > > > continuation never terminates, or if a thread sleeps forever,
> then there is
> > > > > > > no guarantee that finally blocks will ever run.
> > > > > > >
> > > > > > > It is up to the scheduler and blocking constructs — just as in
> the case of
> > > > > > > the OS — to make any kind of liveness guarantees (or attempts).
> > > > > > >
> > > > > > > — Ron
> > > > > > >
> > > > > > >
> > > > > > > On 28 May 2020 at 12:30:45, Alex Otenko (
> oleksandr.otenko at gmail.com(mailto:oleksandr.otenko at gmail.com)(mailto:
> oleksandr.otenko at gmail.com)) wrote:
> > > > > > >
> > > > > > > > Yes, but the JVM offers safety of try-finally. So finally is
> always executed, if the enclosed block terminates. Implementing something
> of the kind every time it is needed in CPS is not easy.
> > > > > > > >
> > > > > > > > Alex
> > > > > > > > On Thu, 28 May 2020, 11:09 Ron Pressler, wrote:
> > > > > > > > > A continuation, or a virtual thread for that matter, that
> becomes unreachable before termination
> > > > > > > > > corresponds to an ordinary platform thread that sleeps
> forever. Neither the JDK nor the OS makes
> > > > > > > > > liveness guarantees about code.
> > > > > > > > >
> > > > > > > > > Having said that, a virtual thread can become unreachable
> before termination only due to a serious
> > > > > > > > > bug. When it is mounted, a reference to it is held by the
> scheduler or else it would be able to schedule
> > > > > > > > > it, and when blocked (and unmounted) a reference to it is
> held by the blocking construct, or else
> > > > > > > > > it would never be able to unblock it.
> > > > > > > > >
> > > > > > > > > — Ron
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > On 28 May 2020 at 07:43:09, 施慧 (kalinshi at qq.com(mailto:
> kalinshi at qq.com)(mailto:kalinshi at qq.com)) wrote:
> > > > > > > > >
> > > > > > > > > Hi All,
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > Trying to understand Loom continuation implementation. In
> following LeakTest, Continuation Object is unreachable after first yield,
> but its runnable target is not finished yet.
> > > > > > > > > There might be some resources allcoated during
> continuation run (native memory in this test case and free in finally block
> --- not cleaner way), when continuation object is collected, these
> resources are not closed or freed.
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > Is it possible to "clean up" a dead continuation which is
> not finished yet but collecting by GC?
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > Tested with code cloned from github today.
> > > > > > > > > javac --add-exports java.base/jdk.internal.ref=ALL-UNNAMED
> --add-exports java.base/jdk.internal.misc=ALL-UNNAMED LeakTest.java
> > > > > > > > > java --add-exports java.base/jdk.internal.ref=ALL-UNNAMED
> --add-exports java.base/jdk.internal.misc=ALL-UNNAMED LeakTest
> > > > > > > > > clean continuation
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > import jdk.internal.ref.Cleaner;
> > > > > > > > > import jdk.internal.misc.Unsafe;
> > > > > > > > > public class LeakTest {
> > > > > > > > > private static final Unsafe unsafe = Unsafe.getUnsafe();
> > > > > > > > > static ContinuationScope scope = new
> ContinuationScope("scope");
> > > > > > > > > public static void main(String[] args) throws Exception {
> > > > > > > > > bar();
> > > > > > > > > System.gc();
> > > > > > > > > Thread.sleep(1000);
> > > > > > > > > System.gc();
> > > > > > > > > Thread.sleep(1000);
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > public static void bar() {
> > > > > > > > > Continuation cont = new Continuation(scope, () -> {
> > > > > > > > > long mem = 0;
> > > > > > > > > try {
> > > > > > > > > // open file/socket
> > > > > > > > > mem = unsafe.allocateMemory(100);
> > > > > > > > > Continuation.yield(scope);
> > > > > > > > > } finally {
> > > > > > > > > unsafe.freeMemory(mem);
> > > > > > > > > System.out.println("release memory");
> > > > > > > > > }
> > > > > > > > > });
> > > > > > > > > Cleaner.create(cont, () -> { System.out.println("clean
> continuation"); });
> > > > > > > > > cont.run();
> > > > > > > > > //cont.run();
> > > > > > > > > }
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > Regards
> > >
>
>


More information about the loom-dev mailing list