This has the same problem, doesn't it? The bottom line is if the lambda is () -> <access a field in any manner> you're getting a capture of `this`.
On Tue, Dec 8, 2015 at 5:08 PM, Roger Riggs <roger.ri...@oracle.com> wrote: > Hi, > > Another option that should always capture is to define a specific static > method with the needed values as arguments: > > > public static class CleanerExample implements AutoCloseable { > > FileDescriptor fd = ...; > > private static final Cleaner cleaner = Cleaner.create(); > > private final Cleaner.Cleanable cleanable = > cleaner.register(this,*() -> cleanup(fd)*); > > @Override > public void close() { > cleanable.clean(); > } > > *static void cleanup(FileDescriptor fd ...) {** > ** fd.close();** > ** }* > > } > > On 12/8/2015 4:24 PM, Peter Levart wrote: > >> >> >> On 12/08/2015 08:08 PM, Steven Schlansker wrote: >> >>> On Dec 8, 2015, at 10:51 AM, Peter Levart<peter.lev...@gmail.com> >>> wrote: >>> >>>> On 12/08/2015 04:34 PM, Roger Riggs wrote: >>>> >>>>> private final Cleaner.Cleanable cleanable = >>>>> cleaner.register(this, () -> fd.close()); >>>>> >>>> Sorry Roger, but this example is flawed. This is tricky! The lambda "() >>>> -> fd.close()" captures 'this', not only 'fd' as can be seen by running the >>>> following example: >>>> To correct that, but still use lambda, you would have to capture a >>>> local variable >>>> >>> It looks like using "fd::close" might also work, and is more concise: >>> >>> IntSupplier x = () -> 10; >>> IntSupplier xS = x::getAsInt; >>> >>> @Test >>> public void test() { >>> System.out.println(xS.getAsInt()); >>> x = () -> 15; >>> System.out.println(xS.getAsInt()); >>> } >>> >>> 10 >>> 10 >>> >> >> Yes, good idea. This is a pre-bound method reference (the part on the >> left of '::' is evaluated immediately). Contrast to lambda, where >> "fd.close()" is an expression in the lambda body which is evaluated when >> lambda is invoked and that expression is composed of a field get + method >> invocation. In order to get an instance field, the object containing it >> must be captured. >> >> So for Roger's example, this will work: >> >> >> public static class CleanerExample implements AutoCloseable { >> >> FileDescriptor fd = ...; >> >> private static final Cleaner cleaner = Cleaner.create(); >> >> private final Cleaner.Cleanable cleanable = >> cleaner.register(this, fd::close); >> >> @Override >> public void close() { >> cleanable.clean(); >> } >> } >> >> >> ...if FileDescriptor.close() is an instance method with no arguments and >> doesn't throw any checked exceptions. >> >> >> Regards, Peter >> >> I'll work the example into the javadoc. >>>>> >>>>> Roger >>>>> >>>>> >>>>> On 12/08/2015 07:25 AM, Peter Levart wrote: >>>>> >>>>>> On 12/08/2015 09:22 AM, David Holmes wrote: >>>>>> >>>>>>> Actually I'm having more doubts about this API. >>>>>>> >>>>>>> Library writers use finalize() as a last ditch cleanup mechanism in >>>>>>> case the user doesn't explicitly call any "cleanup" method. So as a >>>>>>> library writer I would think I am now expected to register my >>>>>>> instances with a Cleaner and provide a Runnable that does what >>>>>>> finalize() would have done. But in that usage pattern the end user of >>>>>>> my objects never has any access to my Cleanables so can never call >>>>>>> clean() themselves - instead they should be calling the cleanup >>>>>>> function directly, just as they would previously. So the whole >>>>>>> "invoke >>>>>>> at most once" for the clean() method seems somewhat unnecessary; and >>>>>>> the way we should write the cleanup method and the Runnable need to >>>>>>> be >>>>>>> more cleary explained as the idempotentcy of the cleanup needs to be >>>>>>> handled in the library writers code not the Cleaner/Clenable >>>>>>> implementation. >>>>>>> >>>>>>> David >>>>>>> >>>>>> Hi David, (once again for the list) >>>>>> >>>>>> I agree that an example would be most helpful. Here's how a normal >>>>>> finalizable object is typically coded: >>>>>> >>>>>> public class FinalizeExample implements AutoCloseable { >>>>>> >>>>>> private boolean closed; >>>>>> >>>>>> @Override >>>>>> public synchronized void close() { >>>>>> if (!closed) { >>>>>> closed = true; >>>>>> // cleanup actions accessing state of >>>>>> FinalizeExample, executed at most once >>>>>> } >>>>>> } >>>>>> >>>>>> @Override >>>>>> protected void finalize() throws Throwable { >>>>>> close(); >>>>>> } >>>>>> } >>>>>> >>>>>> >>>>>> Re-factoring to use Cleaner is a process that extracts the state >>>>>> representing native resource from the user-facing class into a private >>>>>> nested static class and makes the user-facing object just a facade that >>>>>> has >>>>>> access to the state object and is registered with a Cleaner. The >>>>>> Cleaner.Cleanable instance is also made accessible from the user-facing >>>>>> object, so it can provide the on-demand cleaning: >>>>>> >>>>>> public static class CleanerExample implements AutoCloseable { >>>>>> >>>>>> private static class State implements Runnable { >>>>>> @Override >>>>>> public void run() { >>>>>> // cleanup actions accessing State, executed at most >>>>>> once >>>>>> } >>>>>> } >>>>>> >>>>>> private static final Cleaner cleaner = Cleaner.create(); >>>>>> >>>>>> private final State state = new State(); >>>>>> private final Cleaner.Cleanable cleanable = >>>>>> cleaner.register(this, state); >>>>>> >>>>>> @Override >>>>>> public void close() { >>>>>> cleanable.clean(); >>>>>> } >>>>>> } >>>>>> >>>>>> >>>>>> Regards, Peter >>>>>> >>>>>> On 8/12/2015 6:09 PM, David Holmes wrote: >>>>>>> >>>>>>>> Hi Roger, >>>>>>>> >>>>>>>> Sorry I had no choice but to look at this more closely ... and >>>>>>>> apologies >>>>>>>> as this is very late feedback ... I only looked at the API not the >>>>>>>> details of the implementation. >>>>>>>> >>>>>>>> On 8/12/2015 4:50 AM, Roger Riggs wrote: >>>>>>>> >>>>>>>>> Hi David, >>>>>>>>> >>>>>>>>> Thanks for the comments, >>>>>>>>> >>>>>>>>> Updated the javadoc and webrev with editorial changes. >>>>>>>>> >>>>>>>>> [1]http://cr.openjdk.java.net/~rriggs/webrev-cleaner-8138696/ >>>>>>>>> [2]http://cr.openjdk.java.net/~rriggs/cleaner-doc/index.html >>>>>>>>> >>>>>>>> Should cleaning and cleanables be mentioned as part of the >>>>>>>> package-doc >>>>>>>> for java.lang.ref? Else they seem to be an overlooked add-on not >>>>>>>> part of >>>>>>>> the core reference related functionality. Perhaps even state how >>>>>>>> they >>>>>>>> are preferred to use of finalization? >>>>>>>> >>>>>>>> Cleaner.Cleanable: >>>>>>>> >>>>>>>> It was unclear to me what the usage model was for this. I'm assuming >>>>>>>> that the intent is that rather than register a "thunk" (lets call >>>>>>>> it an >>>>>>>> "action") that can be invoked directly by user-code, the user should >>>>>>>> invoke the action via the call to clean(). In which case I think it >>>>>>>> should be explained somewhat more clearly - see below. >>>>>>>> >>>>>>>> I would describe the Cleanable class as: >>>>>>>> >>>>>>>> Cleanable: Represents an object that has been registered for >>>>>>>> cleanup by >>>>>>>> a Cleaner. The object can be cleaned directly, by a call to >>>>>>>> clean(), if >>>>>>>> it is no longer to be used, else it will be cleaned automatically >>>>>>>> when >>>>>>>> the object becomes phantom-reachable. >>>>>>>> >>>>>>>> Cleanable.clean: Unregisters this Cleanable and performs the cleanup >>>>>>>> action that was associated with it. If this Cleanable has already >>>>>>>> been >>>>>>>> unregistered nothing happens. The cleanup action is invoked at most >>>>>>>> once >>>>>>>> per registered Cleanable, regardless of the number of calls to >>>>>>>> clean(). >>>>>>>> >>>>>>>> --- >>>>>>>> >>>>>>>> Looking at Cleaner .... >>>>>>>> >>>>>>>> >>>>>>>> "Cleaner manages a set of object references and corresponding >>>>>>>> cleaning >>>>>>>> functions" >>>>>>>> >>>>>>>> I would say "cleaning actions" rather than functions as they yield >>>>>>>> no >>>>>>>> value. This change needs to be made throughout. >>>>>>>> >>>>>>>> "The most efficient use is to explicitly invoke the clean method >>>>>>>> when >>>>>>>> the object is closed or no longer needed. The cleaning function is a >>>>>>>> Runnable to be invoked at most once when the object is no longer >>>>>>>> reachable unless it has already been explicitly cleaned." >>>>>>>> >>>>>>>> To me this doesn't quite capture the need to not use the Runnable >>>>>>>> directly. I would rephrase: >>>>>>>> >>>>>>>> "In normal use a object should be cleaned up when no longer >>>>>>>> required, by >>>>>>>> invoking the clean() method of the associated Cleanable. This >>>>>>>> guarantees >>>>>>>> that the cleaning action will be performed at most once per object: >>>>>>>> either explicitly, or automatically if it becomes >>>>>>>> phantom-reachable. If >>>>>>>> cleaned explicitly the object should not be used again. Note that >>>>>>>> the >>>>>>>> cleaning action must not refer to the object ..." >>>>>>>> >>>>>>>> --- >>>>>>>> >>>>>>>> Question: what happens if an object is registered simultaneously >>>>>>>> with >>>>>>>> multiple Cleaners? Do we need to warn the user against that? >>>>>>>> >>>>>>>> --- >>>>>>>> >>>>>>>> The phrase "process the unreachable objects and to invoke cleaning >>>>>>>> functions" doesn't quite seem right to me. The objects themselves >>>>>>>> are >>>>>>>> never processed, or even touched - right? So really the thread is >>>>>>>> started to "invoke the cleanup actions for unreachable objects". >>>>>>>> >>>>>>>> create(): can also throw SecurityException if not allowed to >>>>>>>> create/start threads. >>>>>>>> >>>>>>>> register(Object obj, Runnable thunk): thunk -> action >>>>>>>> >>>>>>>> Thanks, >>>>>>>> David >>>>>>>> >>>>>>>> >>>>>>>> On 12/6/15 7:46 PM, David Holmes wrote: >>>>>>>>> >>>>>>>>>> Hi Roger, >>>>>>>>>> >>>>>>>>>> Sorry to be late here but was trying not to get involved :) >>>>>>>>>> >>>>>>>>>> It is already implicit that ThreadFactory.newThread should return >>>>>>>>>> unstarted threads - that is what a new Thread is - so I don't >>>>>>>>>> think >>>>>>>>>> IllegalThreadStateException needs to be documented here as it is >>>>>>>>>> documenting behaviour of a broken ThreadFactory (and a broken >>>>>>>>>> ThreadFactory could throw anything) . >>>>>>>>>> >>>>>>>>> It does seem that new is fairly well understood but one can read of >>>>>>>>> ThreadFactory is as a bit ambiguous, lacking a direct reference to >>>>>>>>> the >>>>>>>>> Thread.State of the new thread >>>>>>>>> and since it allows various attributes of the thread to be modified >>>>>>>>> after the constructor. >>>>>>>>> Since the runnable is supplied as an argument it is ready to be >>>>>>>>> started, >>>>>>>>> why not. >>>>>>>>> It seemed useful to reinforce the salient points. >>>>>>>>> >>>>>>>>>> Also the no-arg cleaner() can also throw SecurityException. >>>>>>>>>> >>>>>>>>> The thread construction is done in doPriv so it should not throw. >>>>>>>>> Did I miss some edge case? >>>>>>>>> >>>>>>>>>> Also this: >>>>>>>>>> >>>>>>>>>> 127 * On each call the {@link >>>>>>>>>> ThreadFactory#newThread(Runnable) >>>>>>>>>> thread factory} >>>>>>>>>> 128 * should return a {@link Thread.State#NEW new thread} >>>>>>>>>> with >>>>>>>>>> an appropriate >>>>>>>>>> 129 * {@linkplain Thread#getContextClassLoader context class >>>>>>>>>> loader}, >>>>>>>>>> 130 * {@linkplain Thread#getName() name}, >>>>>>>>>> 131 * {@linkplain Thread#getPriority() priority}, >>>>>>>>>> 132 * permissions, etc. >>>>>>>>>> >>>>>>>>>> then begs the questions as to what is "appropriate". I think this >>>>>>>>>> can >>>>>>>>>> be said much more simply as: "The ThreadFactory must provide a >>>>>>>>>> Thread >>>>>>>>>> that is suitable for performing the cleaning work". Though even >>>>>>>>>> that >>>>>>>>>> raises questions. I'm not sure why a ThreadFactory is actually >>>>>>>>>> needed >>>>>>>>>> here ?? Special security context? If so that could be mentioned, >>>>>>>>>> but I >>>>>>>>>> don't think name or priority need to be discussed. >>>>>>>>>> >>>>>>>>> It was intended to prod the client to be deliberate about the >>>>>>>>> threadFactory. >>>>>>>>> Since the client is integrating the Cleaner and respective cleaning >>>>>>>>> functions >>>>>>>>> with the client code, the ThreadFactory makes it possible for the >>>>>>>>> client to >>>>>>>>> initialize a suitable thread and the comments serve as a reminder. >>>>>>>>> I agree that the phrase 'suitable for performing the cleaning >>>>>>>>> work' is >>>>>>>>> the operative one. >>>>>>>>> >>>>>>>>> Thanks, Roger >>>>>>>>> >>>>>>>>>> Thanks, >>>>>>>>>> David >>>>>>>>>> >>>>>>>>>> >> >