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