Dead Continuation cause resource leak?

Alex Otenko oleksandr.otenko at gmail.com
Thu May 28 22:38:33 UTC 2020


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