Hi Christoph,

On 05/16/2016 10:37 AM, Christoph Engelbert wrote:
Hey Peter,

First of all, I really love this Cleanup API!
Anyhow I think it would make sense to have Cleanable extend AutoCloseable for 
exactly the use case you rendered in your JavaDoc of the Cleaner class.

Made up example:
public <T> T execute(BufferAction bufferAction) {
   Buffer buffer = allocateBuffer();
   try (Cleanable wrapper = cleaner.register(buffer, Buffer::deallocate) {
     bufferAction.execute(buffer);
   }
}

I agree that you probably want to encapsulate the cleaner registration and 
cleaning action but for frameworks it might be beneficial to have a common 
utility method implementing a pattern such as the one above.

Chris

I think the Cleaner API is more suitable to be used inside various classes that themselves implement AutoCloseable rather than as an (optional) aspect on top of them. So your example would simply read:

public <T> T execute(BufferAction bufferAction) {
  try (Buffer buffer = allocateBuffer()) {
    bufferAction.execute(buffer);
  }
}

There's no point in doing both things as in your example: using try-with-resources + the Cleaner API on top. try-with-resources already guarantees cleanup. Cleanable API is meant to be used inside otherwise AutoCleanable implementations to shield the user from native resource leaks if the user forgets to use or can't use try-with-resources.

Regards, Peter


On 16 May 2016, at 00:08, Peter Levart <peter.lev...@gmail.com> wrote:

Hi Roger and others,

When the new Cleaner API was created the implementation of Cleanable(s) was 
split into the low-level abstract [Soft|Weak|Phantom]Cleanable classes to be 
used internally for purposes where the footprint matters and their 
corresponding CleanerImpl.[Soft|Weak|Phantom]CleanableRef subclasses used as 
implementations that take a Runnable cleanup action and are exposed via the 
public Cleaner API.

When thinking of possible JDK internal use cases for the low-level API, I came 
to the conclusion that [Soft|Weak|Phantom]Cleanable classes are not suitable as 
is, because in cases where footprint matters, it is usually also the case that 
the number of [Soft|Weak|Phantom]Cleanable instances created is larger and that 
construction performance also matters. Especially multi-threaded construction. 
I'm thinking of the use cases of auto-cleanable concurrent data structures. In 
such use cases, the present features of [Soft|Weak|Phantom]Cleanable classes, 
namely the guaranteed just-once cleanup action invocation and keeping the 
Cleanable instance reachable until the cleanup action is performed, are 
actually not needed and just present footprint and performance (contention) 
overhead. They also present an overhead as they don't allow GC to automatically 
collect the Cleanable instances if the data structure containing them becomes 
unreachable and corresponding registered cleanup actions obsolete.

The mentioned features are important for public Cleaner.Cleanable instances as 
they are usually used for cleanup of native resources where the performance of 
their creation is not so drastically important and where there is no intrinsic 
data structure to hold them reachable.

I propose to move those features from the [Soft|Weak|Phantom]Cleanable classes 
down the hierarchy to the CleanerImpl.[Soft|Weak|Phantom]CleanableRef 
subclasses:

http://cr.openjdk.java.net/~plevart/jdk9-dev/Cleaner.cleanup/webrev.01/


In this change I also removed the CleanerImpl.[Soft|Weak]CleanableRef 
subclasses as they are not needed and I believe will never be. I also renamed 
the CleanerImpl.PhantomCleanableRef subclass to 
CleanerImpl.PhantomCleanableImpl.

Changes to the implementation are straightforward. The most work was put into 
the corresponding test. I did some clean-up to it and also changed it to 
accommodate for the new behavior of [Soft|Weak|Phantom]Cleanable classes. The 
changes speak for itself. One of the not-so obvious changes was to replace the 
CleanableCase.clearRef() action with the CleanableCase.releaseReferent() 
action. The old clearRef() action did not serve any purpose. Whether this 
method was called or not, the behavior of the corresponding Cleanable was 
unchanged as the Reference instance (referenced from the 'ref' field) was 
always of the same strength as the Cleanable itself. So clearing it could not 
affect the behavior of the Cleanable.

I changed 'ref' to hold a direct reference to the referent and renamed the 
field to 'referent'. I changed the EV_XXX int constants to Event enum constants 
with helper methods used in CleanableCase.expectedCleanups() method that now 
returns the number of expected cleanup invocations - in the 
PhantomCleanableImpl case this is the number of expected cleanup action 
invocations while in the plain XxxCleanable subclass cases it is the number of 
Cleanable.clean() method invocations. I added the no-actions case to both 
PhantomCleanableImpl and XxxCleanable cases and extended the number and 
combinations of XxxCleanable cases.

The checkCleaned() method was extended to verify that the number of cleanup 
invocations is *no more* and no less then the expected.

See how WeakKey test is now simplified. This is the typical use-case for 
WeakCleanable I was talking about.


So, what do you think of this cleanup?


Regards, Peter



Reply via email to