Dead Continuation cause resource leak?

Ron Pressler ron.pressler at oracle.com
Thu May 28 16:32:04 UTC 2020


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) 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

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, <ron.pressler at oracle.com> 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) 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, <ron.pressler at oracle.com> 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) 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