Proposal: Automatic Resource Management

Matthias Ernst matthias at mernst.org
Fri Mar 13 15:17:13 PDT 2009


On Fri, Mar 13, 2009 at 5:26 PM, Joshua Bloch <jjb at google.com> wrote:
> Matthias,
>
> Hi. This would work, but I'd have concerns about the performance. Locks have
> to be really fast.

Hi Josh, I also don't think we should bend ARM backwards to make Locks work.
But I actually believe the way I sketched it both avoids allocation
for the builtin lock types and should allow the VM to optimize to the
point that it's no more expensive than a traditional
lock/try/finally/unlock sequence (ok plus 2 extra loads,
AbstractLock#locked#this$0).

Since it's a straightforward translation, we can try it out today.
If it works, great, and I would argue to allow expressions in try and
add to j.u.c accordingly. If not, this use case will not be supported.
That would be fine then, too.

Cheers
Matthias

>  If we want to solve the locking case (and I'm not so
> sure we do), I believe that we should do so with another construct.  I made
> a "Preproposal" for such a construct, but no one showed any enthusiasm.
>
>           Josh
>
> On Fri, Mar 13, 2009 at 2:47 AM, Matthias Ernst <matthias at mernst.org> wrote:
>>
>> On Sun, Mar 8, 2009 at 10:39 AM, Peter Mount <peter at retep.org.uk> wrote:
>> > On Sun, Mar 8, 2009 at 9:00 AM, Reinier Zwitserloot
>> > <reinier at zwitserloot.com
>> >> wrote:
>> >
>> >> We're veering waaay offtopic here; the ARM proposal does not address
>> >> locks. Period. Would everyone be happy if this was added in the 'major
>> >> disadvantages' section?
>> >
>> >
>> > Yes this is becoming more offtopic but hopefully my comments have
>> > brought up
>> > two points:
>> >
>> > 1: Although Locks are a form of resource, the ARM proposal as is cannot
>> > support them
>> > 2: Someone will abuse it to support Lock's at some point with
>> > potentially
>> > disastrous consequences.
>>
>> What would be the problem with:
>>
>> package j.u.c:
>> abstract class AbstractLock implements Lock {
>>  private final AutoCloseable locked = new AutoCloseable() {
>>    public void close() { AbstractLock.this.unlock(); }
>>  }
>>
>>
>>  public AutoCloseable locked() {
>>    lock();
>>    return locked;
>>  }
>> }
>>
>> Have the j.u.c locks inherit from AbstractLock.
>>
>> Add in class LockSupport:
>>  public static AutoCloseable locked(final Lock lock) {
>>    return (lock instanceof AbstractLock) ?
>> ((AbstractLock)lock).locked() : new AutoCloseable() {
>>      public void close() { lock.unlock(); }
>>    };
>>  }
>>
>> Use:
>>
>> import static LockSupport.locked;
>>
>> try(locked(guard)) {
>>
>> }
>>
>>
>> Matthias
>>
>> >
>> >
>> >>
>> >> If locks are truly deemed crucial, or the consensus is that,
>> >> eventhough evidently ARM-as-is can't do locks, people will try anyway
>> >> and the resulting confusion must be avoided, I suggest that the ARM
>> >> proposal is updated to do the right thing when an expression of type
>> >> Lock shows up in a try ( lockShowsUpHere ) { code} block. Someone else
>> >> proposed this before and I can't see any downside to this. It would be
>> >> bad if, for every release, a bunch more types get special uniquely
>> >> defined semantics in relation to the ARM expression, but that seems
>> >> very unlikely. Nobody else has named a use-case that doesn't work in
>> >> vanilla ARM yet seems likely to be abused, other than Lock, IIRC.
>> >
>> >
>> > Yes I did yesterday.
>> >
>> > The one thing I've learned from experience is that a lot of programmers
>> > are
>> > lazy when it comes to resources. Although for some resources are cleaned
>> > up
>> > occasionally by the garbage collector, most are not and unless the
>> > system is
>> > under heavy load the problem doesn't show itself until it's in the live
>> > environment. For this reason I do feel that the ARM proposal would
>> > alleviate
>> > this as long as the programmers know that the new construct exists.
>> >
>> > Now I do feel that Locks are a form of resource but one that is actively
>> > short lived. Although it seems simple to just write them manually with a
>> > try
>> > finally block you'll be surprised how easy it is for someone to miss the
>> > finally block or refactor it out without noticing. In a large system
>> > this
>> > can be costly (my gaming background coming in here).
>> >
>> > Over the last few years I've ended up wasting so much time debugging a
>> > complex system to locate a deadlock just to find that someone has
>> > forgotten
>> > to release a lock. It is this reason why I think the ARM proposal should
>> > support Lock's as a separate use-case.
>> >
>> > The main issues with Disposable and Lock that I can see are:
>> >
>> > 1: Lock is an interface and cannot extend Disposable as that would break
>> > existing code. Also implementing Disposable in the Lock implementations
>> > would not work if you use try( Lock ) as javac would not see it as being
>> > Disposable.
>> >
>> > 2: Normal resources are already open/active before the try so with
>> > Disposable the try(Disposable) would simply call Disposable.close() at
>> > the
>> > end. With Locks they are not active until lock() is called so try(Lock)
>> > would have to call Lock.lock() before the method body and then
>> > Lock.unlock()
>> > at the end.
>> >
>> > As I said in an earlier email I implement this pattern in JDK1.6 by
>> > having a
>> > set of annotations which with a processor inject the try...finally block
>> > around a method thats locked (yes I know annotation processors shouldn't
>> > do
>> > this but it's removed the problem completely). Having the ARM proposal
>> > would
>> > not just obsolete the hack but make it more flexible (as the hack only
>> > works
>> > at the method level).
>> >
>> > As for any other use-cases that don't work with the current proposal
>> > other
>> > than Lock, I can't think of any at the moment.
>> >
>> >
>> >>
>> >>  --Reinier Zwitserloot
>> >>
>> >>
>> >> On Mar 8, 2009, at 09:03, Peter Mount wrote:
>> >>
>> >> > On Sun, Mar 8, 2009 at 7:58 AM, Jeremy Manson
>> >> > <jeremy.manson at gmail.com>wrote:
>> >> >
>> >> >> I am aware Lock is an interface.  You wouldn't actually change the
>> >> >> Lock interface, you would change the classes.  Just as they retrofit
>> >> >> Iterable everywhere.  That's why I put "class Lock" there; perhaps
>> >> >> it
>> >> >> would have been clearer if it said "class MyLock".
>> >> >
>> >> >
>> >> > What about when someone just references the lock as Lock rather than
>> >> > the
>> >> > implementing class? Javac won't be able to determine that the lock
>> >> > implements Disposable so in that case it will fail..
>> >> >
>> >> >
>> >> >
>> >> >
>> >> >>
>> >> >>
>> >> >> Jeremy
>> >> >>
>> >> >> On Sat, Mar 7, 2009 at 10:23 AM, Stephen Colebourne
>> >> >> <jodastephen at gmail.com> wrote:
>> >> >>> Jeremy Manson wrote:
>> >> >>>> The "right" fix, if we want to support this pattern, is to allow
>> >> >>>> the
>> >> >>>> try resource statement to accept expressions that return
>> >> >>>> Disposables,
>> >> >>>> and to retrofit the relevant lock APIs with disposables and lock
>> >> >>>> methods that return this:
>> >> >>>>
>> >> >>>> class Lock implements Disposable {
>> >> >>>>  public Lock dlock() {
>> >> >>>>    return this;
>> >> >>>>  }
>> >> >>>>  @Override public void dispose() {
>> >> >>>>    unlock();
>> >> >>>>  }
>> >> >>>> }
>> >> >>>>
>> >> >>> Lock is an interface. No changes are possible.
>> >> >>>
>> >> >>> Stephen
>> >> >>>
>> >> >>>
>> >> >>>
>> >> >>
>> >> >>
>> >> >
>> >> >
>> >> > --
>> >> > Peter Mount
>> >> > e: peter at retep.org.uk
>> >> > w: http://retep.org
>> >> > Jabber/GTalk: peter at retep.org MSN: retep207 at hotmail.com
>> >> >
>> >>
>> >>
>> >>
>> >
>> >
>> > --
>> > Peter Mount
>> > e: peter at retep.org.uk
>> > w: http://retep.org
>> > Jabber/GTalk: peter at retep.org MSN: retep207 at hotmail.com
>> >
>> >
>>
>
>



More information about the coin-dev mailing list