Proposal: Automatic Resource Management

Marek Kozieł develop4lasu at gmail.com
Thu Apr 2 12:33:10 PDT 2009


2009/4/2 Peter Levart <peter.levart at gmail.com>:
> Hello Coiners!
>
> The debate about ARM proposal has calmed down lately and I was thinking
> about it last few days. Initially I submitted a comment that got lost by
> list processor and was about using a "marker" interface as a supertype of
> sigle-method disposable interfaces like this:
>
>
> package java.lang;
>
> public interface AutomaticResource {}
>
>
> ... and for example, retroffiting java.io.Closeable:
>
>
> package java.io;
>
> public interface Closeable extends AutomaticResource {
>    void close() throws IOException;
> }
>
>
> ... this way even the name of the method would not be "coined". There would
> have to be some rules that compiler will enforce to make sure it can
> uniquely identify the method to be called on resource disposal (for example:
> the static type of the resource should not directly or indirectly implement
> or extend two different single-method interfaces marked with
> AutomaticResource marker superinterface).
>
> But then I thought. Why would this language feature even have to be tied to
> a particular interface. The "foreach" loop did take this approach with
> java.lang.Iterable, but this had nothing to do with exceptions and catching
> them and ignoring them silently. A language construct that hides this often
> cited "bad practice" under the carpet is no good!
>
> For example, this snippet of code, written to the proposed ARM spec.,
> submited by Bob Lee in this thread a month ago, asking Neal Gafter to
> demonstrate how the equivalent BGGA version would look like:
>
>  try (InputStream in = new FileInputStream(src);
>      OutputStream out = new FileOutputStream(dest)) {
>    byte[] buf = new byte[8 * 1024];
>    int n;
>    while ((n = in.read(buf)) >= 0)
>      out.write(buf, 0, n);
>  } catch (IOException e) {
>    showDialog("Copy failed.");
>  }
>
> ... is flawed. This code does not guarantee that in the absence of shown
> exception the file has been copied in it's intirety.
>
> Ignoring exception thrown on an InputStream.close() might be desireable, but
> ignoring exception thrown on OutputStream.close() migh mean that you don't
> mind missing writing some final bytes into the file. Closing OutputStream
> should not be part of automatic resource disposal but part of main code
> block. This brings us to the question how is ARM supposed to be able do
> differentiate InputStream from OutputStream if both implement Closeable?
>
> So I thought how to circumvent these two weeknesses. My take on this is
> something like the following:
>
>
>        try (InputStream in = new FileInputStream("inputFile");
>             OutputStream out = new FileOutputStream("outputFile"))
>        {
>          // try body
>        }
>        catch (IOException e)
>        {
>          // catch body
>        }
>        finally (IOException e1 : in.close();
>                 IOException e2 : out.close())
>        {
>          // finally body
>        }
>
>
> ... would be translated to ...
>
>
>        {
>            IOException e1 = null;
>            IOException e2 = null;
>
>            try
>            {
>                InputStream in = new FileInputStream("inputFile");
>
>                try
>                {
>                    OutputStream out = new FileOutputStream("outputFile");
>
>                    try
>                    {
>                        // try body
>                    }
>                    finally
>                    {
>                        try
>                        {
>                            out.close();
>                        }
>                        catch (IOException $$e)
>                        {
>                            e2 = $$e;
>                        }
>                    }
>                }
>                finally
>                {
>                    try
>                    {
>                        in.close();
>                    }
>                    catch (IOException $$e)
>                    {
>                        e1 = $$e;
>                    }
>                }
>            }
>            catch (IOException e)
>            {
>                // catch body
>            }
>            finally
>            {
>                // finally body
>            }
>        }
>
>
>
> ... no special interfaces are used here. The code to dispose of resources is
> clearly visible (it has to be writen, yes, but that also means that it can
> be read, which is a good thing).
>
> The above example is the "full monty" version. The thing to note is that a
> try (...; ...; ...) construct is to be matched by an optional finally (...;
> ...; ...) construct in that both, if the second is specified, must have an
> equal number of semicolons. Each "statement" in the try construct gets it's
> own slot that must be matched by the coresponding slot in the finally (...;
> ...; ...) construct. Any slot in finally construct can be left empty (like
> any of the 3 slots in the for (;;) statement).
>
> The following minimal version is also possible (in this example no catching
> of exceptions on resource cleanup is attempted):
>
> int i, j;
> ReadWriteLock iLock = ...;
> ReadWriteLock jLock = ...;
>
> // ...
>
> try (jLock.readLock().lock(); iLock.writeLock().lock()) {
>  i += j;
> }
> finally (jLock.readLock().unlock(); iLock.writeLock().unlock())
>
>
> ... gets translated to:
>
>
> {
>    jLock.readLock().lock();
>    try {
>      iLock.writeLock().lock();
>      try {
>        i += j;
>      }
>      finally {
>        iLock.writeLock().unlock();
>      }
>    }
>    finally {
>      jLock.readLock().unlock();
>    }
> }
>
>
> All this will have to be formalized. This is just an idea to keep the debate
> going.
>
> Regards, Peter
>
>

If I can I would suggest:
       try (InputStream in = new FileInputStream("inputFile");
            OutputStream out = new FileOutputStream("outputFile"))
       {
         // try body
       }
       catch (IOException e)
       {
         // catch body
       }
       finally (IOException e1 : in.close();
                IOException e2 : out.close())
       {
         // finally body
       }

change to:
catch:
catch(variable = Type);
catch(variable = Type exceptionVariable){};
catch(Type exceptionVariable){};

allow mix try with new :
new try Constructor catch ....;
variable would be null is exception occurs.


IOException exception = null;
InputStream in = new try FileInputStream("inputFile") catch (exception
= IOException);
if (in == null) return ...;
OutputStream out = new try FileOutputStream("outputFile")) catch
(exception = IOException);
if ( (in != null) && (out != null) ) try{
     ...
     }
catch (exception = IOException);
if (in != null)  try {in.close(); } catch (exception = IOException);
if (out != null)  try {out.close(); } catch (exception = IOException);
if (exception != null) throw  exception;
...

But I didn't analyzed all interactions.


for locks I still think that they should be done by:
synchronize.write(lock){}


-- 
Pozdrowionka. / Regards.
Lasu aka Marek Kozieł

http://lasu2string.blogspot.com/



More information about the coin-dev mailing list