Article Discussion
From JavaOne 2009: On Trust and Types
Summary: The JVM's promise of write once, run anywhere works most of the time. But it can also make programmers complacent about cases where the WORA promise fails, says Gwyn Fisher, CTO of Klocwork, maker of the eponymous code analysis tool. One such case is resource management, where the JVM's latent garbage collector necessitates that developers think about the machine behind the VM. In this interview with Artima, Fisher describes cases where well-known Java APIs work differently based on deployment platform, and how well-defined type systems can make resource management more reliable:
31 posts on 3 pages.      
The ability to add new comments in this discussion is temporarily disabled.
Most recent reply: October 6, 2009 0:43 PM by
Mark
Posts: 48 / Nickname: mthornton / Registered: October 16, 2005 11:22 PM
Re: From JavaOne 2009: On Trust and Types
June 14, 2009 0:50 PM      
No CloseableStack is just an idea, but I can't see any difficulty in implementing it.
James
Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
Re: From JavaOne 2009: On Trust and Types
June 14, 2009 5:05 PM      
> The advantage of the 'synchronized solution' is that you
> can actually enforce the 'correct' usage.

I have to disagree. There's nothing forcing me to use close-later. The solution I posted doesn't give the client code the option to not close properly. That's enforced by the solution. Of course, as I wrote it, no one is forced to wrap their stream with this object. The idea, though, is that if you started with this kind of approach instead of the more imperative approach you eliminate the possibility of not releasing resources properly. I don't see any corresponding possibilities with the synchronized approach or with Mark's approach. Those two solutions require that the client explicitly invoke them.
Thomas
Posts: 12 / Nickname: thomasm / Registered: January 27, 2009 1:41 AM
Re: From JavaOne 2009: On Trust and Types
June 14, 2009 9:16 PM      
> There's nothing forcing me to use close-later

The library calls it (I thought that's clear).

> The solution I posted doesn't give the
> client code the option to not close properly.

I know. The only problem is that it's hard to use.

> I don't see any corresponding possibilities
> with the synchronized approach

Well, I do: if the library calls closeLater.

> Those two solutions require that the client
> explicitly invoke them.

Not if the library calls closeLater and only exposes getStream(Object sync).
Michael
Posts: 1 / Nickname: michid / Registered: March 9, 2008 3:25 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 1:49 AM      
Another approach

interface Block {
    void execute();
}
interface Resource {
    void close();
}
 
public static <R extends Resource> void with(R resource, Block block) {
    try {
        block.execute();
    }
    finally {
        resource.close();
    }
}


Assuming we have a Resource r which can doSomething:

with(r, new Block() { public void execute() {
    r.doSomething();
}});
Achilleas
Posts: 98 / Nickname: achilleas / Registered: February 3, 2005 2:57 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 3:54 AM      
It seems Java needs C++'s RAII...
James
Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 5:16 AM      
> > There's nothing forcing me to use close-later
>
> The library calls it (I thought that's clear).

I think you mean each and every library.

> > The solution I posted doesn't give the
> > client code the option to not close properly.
>
> I know. The only problem is that it's hard to use.

When you wrote "The advantage of the 'synchronized solution' is that you can actually enforce the 'correct' usage" I thought you were implying that the closure solution couldn't enforce correct usage. Otherwise, how would it be an advantage?

It really isn't hard to use. It's probably just different from what you are used to.

> > I don't see any corresponding possibilities
> > with the synchronized approach
>
> Well, I do: if the library calls closeLater.
>
> > Those two solutions require that the client
> > explicitly invoke them.
>
> Not if the library calls closeLater and only exposes
> getStream(Object sync).

If I call getStream(Object sync), I need to synchronize on sync within a second, right? And I need to maintain that synchronization until I am done with the stream.

Also, each library needs to do this. So the synchronized solution doesn't enforce anything. Each library that uses it is required to enforce this.
Thomas
Posts: 12 / Nickname: thomasm / Registered: January 27, 2009 1:41 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 7:03 AM      
> the closure solution couldn't enforce correct usage

It does. You can enforce using the closure in a library, but it would be quite hard to use.

> It really isn't hard to use.
> It's probably just different from what you are used to.

I'm used to. Runnable, Arrays.sort(.., Comparator) and so on. It is hard to use: How do you change a variable of the outer block in the inner block? It's possible (for example using one-element arrays) but it's ugly. Even accessing a variable of the main block is complicated (you need to make it final). The closure (Block.execute in the example above) needs to declare exception(s) the block can throw, and the caller has to catch that (even if he doesn't throw any). Or you don't declare, but then the caller can't use checked exceptions. Then the size of the code is bigger.

> If I call getStream(Object sync),
> I need to synchronize on sync within a second, right?

No, you need to sync on the object _before_ calling getStream. Example:
synchronized void exampleClient() throws Exception {
    // anything
    InputStream in = myApi.getStream(this);
    // read...
    // no need to call in.close() (but you can)
    // the stream is closed even if you throw exceptions
}

In this case 'this' is the sync object. Another example:
Binary b = node.getProperty("jcr:content").getBinary();
synchronized(b) {
  InputStream in = b.getStream();
  // read... same as above
}

In this case the sync object is not even passed. Instead, the sync object is 'b' (the object where you call getStream()).

> I think you mean each and every library.
> Also, each library needs to do this.

Well, obviously. I'm quite sure you didn't understand my solution, otherwise you wouldn't ask. The 'synchronized' solution enforces using synchronized in the client code, if the 'synchronized' solution is used in the library.
James
Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 8:13 AM      
> > the closure solution couldn't enforce correct usage

> It does. You can enforce using the closure in a library, but it would be quite hard to use.

I know it does. I was pointing out that you implied it didn't. What is so difficult about it?

> I'm used to. Runnable, Arrays.sort(.., Comparator) and so
> on. It is hard to use: How do you change a variable of the
> outer block in the inner block? It's possible (for example
> using one-element arrays) but it's ugly. Even accessing a
> variable of the main block is complicated (you need to
> make it final). The closure (Block.execute in the example
> above) needs to declare exception(s) the block can throw,
> and the caller has to catch that (even if he doesn't throw
> any). Or you don't declare, but then the caller can't use
> checked exceptions. Then the size of the code is bigger.

I don't see how declaring a variable final is complicated. It's just an extra keyword. In fact, I think Java would be a much better language if variables defaulted to final and had to be explicitly declared to be writable.

I use this approach all the time. The need to modify a local variable from the rarely comes up and you can always use a named local class, inner class, or nested class to avoid the issue entirely.

Exception handling is a downside but again, in the infrequent cases that I need to, it's worth wrapping and unwrapping the runtime errors. The size of the code is not a real issue, in my opinion.

> > I think you mean each and every library.
> > Also, each library needs to do this.
>
> Well, obviously. I'm quite sure you didn't understand my
> solution, otherwise you wouldn't ask. The 'synchronized'
> solution enforces using synchronized in the client code,
> if the 'synchronized' solution is used in the library.

I understand the approach perfectly and I'm not asking. I'm clarifying. As stated you made it sound as if the solution enforced correct usage in itself which you are now readily admitting that it does not. The closure solution does enforce correct usage in itself.

Basically, the solution you are proposing requires that all work with the resource object be executed within the same stack as the request to get the resource object. You can't easily pass this resource to another class for it to use. This limits the kinds of designs you can use with this approach. You can't easily reuse this resource object or share it with other parts of the code.

The real problem I see with the synchronization solution, other than being inelegant is that synchronization is associated with many types of usage errors in itself. Many of these usage errors are extremely difficult to find in code and can depend on the JVM, platform, or hardware.
Thomas
Posts: 12 / Nickname: thomasm / Registered: January 27, 2009 1:41 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 9:30 AM      
> I was pointing out that you implied it didn't

I'm sorry if you think I implied that.

> The size of the code is not a real issue, in my opinion

Let's say we have different opinions.

> you made it sound as if the solution enforced
> correct usage in itself

I'm sorry if you think it does.

> The closure solution does enforce correct usage in itself.

You seem to use a different meaning for "in itself". Closures only solve the problem if they are used. I avoid closures in Java because they are cumbersome to use.

> within the same stack as the request to get
> the resource object

Only if the API enforces it. The same with closures, if the API enforces to use closures.

> The real problem I see with the synchronization
> solution, other than being inelegant

How is it inelegant? If a solution is inelegant in Java, then it's the closures solution :-)

> many types of usage errors in itself

You probably mean Java level deadlocks. Yes, that's a problem. To avoid it, you need to synchronized on a different objects for each resource.

> Many of these usage errors are extremely difficult to find

Multithreading is complicated, no question.
James
Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 9:49 AM      
> How is it inelegant? If a solution is inelegant in Java,
> then it's the closures solution :-)

I guess if you think elegance is about how your code looks, then can see that. I mean that it's inelegant from a design perspective.

It probably won't be a big deal from a usage perspective but one thing I noticed is that the synchronized solution can be subverted even if the library only provides a getResource(lock) method.

The simple way is to call System.exit() from within the synchronized block. A more general way follows. Granted, someone would probably have to go out of their way to do this but it's possible that this kind of scenario could arise in multi-threaded code unintentionally:

class Blocker<E>
{
  private volatile Exception exception;
  private volatile E it;
  
  public void start(Action<E> action)
  {
    new Thread(new BlockingRunnable(action)).start();
  }
  
  public E get() throws Exception
  {
    while (it == null && exception == null) {
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        /* buzz off */
      }
    }
    
    if (exception != null) throw exception;
    
    return it;
  }
  
  private class BlockingRunnable implements Runnable
  {
    private final Action<E> action;
    
    BlockingRunnable(Action<E> action)
    {
      this.action = action;
    }
    
    public void run () {
      synchronized(Blocker.this) {
        try {
          it = action.execute();
        } catch (Exception e) {
          exception = e;
        }
        
        try {  
          while(true) {
            Thread.sleep(3600000);
          }
        } catch (InterruptedException e) {
            // snooze...
        }
      }
    }
  }
  
  public interface Action<E>
  {
    E execute() throws Exception;
  }
}


An example of how this could be used:

  public static void main(final String[] args)
  {
    try
    {
      final Blocker<InputStream> blocker = new Blocker<InputStream>();
      
      blocker.start(new Action<InputStream>()
      {
        @Override
        public InputStream execute() throws FileNotFoundException
        {
          return StreamProvider.getStream(blocker, args[0]);
        }
      });
      
      System.err.println("Begin access outside of synchronized block");
      
      InputStream stream = blocker.get();
      
      for (int b; (b = stream.read()) > -1;)
      {
        System.out.print((char) b);
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      System.exit(0);
    }
  }
Thomas
Posts: 12 / Nickname: thomasm / Registered: January 27, 2009 1:41 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 10:33 AM      
> it's inelegant from a design perspective.

You are completely right. The closure solution would be a lot better technically, too bad that Java doesn't support it well. The 'synchronized' solution is an incomplete workaround, or you can call it a hack. The main problem is probably that you can't guarantee when exactly the resource is closed.

The language I like most at the moment is Scala. It nicely supports closures. The only problem in Scala was that it doesn't support 'break' (which I use from time to time). Until I found out you can implement 'break' yourself (by throwing an exception). In any case, 'break' will be supported in the next version. I think that's great. The only remaining problem for Scala is the poor tools support (Eclipse plugin :-)
James
Posts: 128 / Nickname: watson / Registered: September 7, 2005 3:37 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 11:00 AM      
> > it's inelegant from a design perspective.
>
> You are completely right. The closure solution would be a
> lot better technically, too bad that Java doesn't support
> it well. The 'synchronized' solution is an incomplete
> workaround, or you can call it a hack. The main problem is
> probably that you can't guarantee when exactly the
> resource is closed.

Do you know what's going on with the closure proposals for Java 7? I was following that for a while but I got tired of all the different proposals coming out of the woodwork. It seems to me that you need a short anonymous inner class syntax. A checked exception pass-through mechanism would be cool and a way to write to local would really top things off. In order to do that, however, it seems that the language needs a way to distinguish between synchronous-one-time closures and everything else.

> The language I like most at the moment is Scala. It nicely
> supports closures. The only problem in Scala was that it
> doesn't support 'break' (which I use from time to time).
> Until I found out you can implement 'break' yourself (by
> throwing an exception). In any case, 'break' will be
> supported in the next version. I think that's great. The
> only remaining problem for Scala is the poor tools support
> (Eclipse plugin :-)

Scala's a cool language but I kind of feel that the complexity level is bit higher than what I would like for team development.
Carson
Posts: 18 / Nickname: cgross / Registered: October 16, 2006 3:21 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 3:38 PM      
> It seems Java needs C++'s RAII...

++

In GScript:


using ( var in = ...
var out = ...) {
// reading and writing
}


The 'using' statement guarantees that resources are closed appropriately. GC'd languages that don't support stack-derived life spans for objects absolutely need this feature.

It makes resource-based API's much more brain dead.

Cheers,
Carson
Thomas
Posts: 12 / Nickname: thomasm / Registered: January 27, 2009 1:41 AM
Re: From JavaOne 2009: On Trust and Types
June 15, 2009 10:19 PM      
> closure proposals for Java 7?

As far as I know, it will not be implemented (reasons: lack of time, risk of making stuff too complicated). The only changes that are planned are Project Coin, and I don't think it's decided yet: http://openjdk.java.net/projects/coin - Automatic Resource Allocation is one of the proposed features.

> Scala ... complexity level

I agree. Some features should be removed, I'm not sure which ones. 'Removed' could also mean 'not allowed to use', basically you could restrict yourself, with tools similar to Checkstyle / FindBugs.
paulo
Posts: 3 / Nickname: i30817 / Registered: June 26, 2007 0:24 PM
Re: From JavaOne 2009: On Trust and Types
June 16, 2009 1:27 PM      
    /**
     * Close closeables. Use this in a finally clause.
     */
    public static void close(Closeable... closeables) {
        for (Closeable c : closeables) {
            if (c != null) {
                try {
                    c.close();
                } catch (Exception ex) {
                    Logger.getLogger(IoUtils.class.getName()).log(Level.WARNING, "Couldnt close Closeable", ex);
                }
            }
        }
    }
 
    /**
     * Sockets are not closeable... wtf
     */
    public static void close(Socket socket, Closeable... closeables) {
        for (Closeable c : closeables) {
            if (c != null) {
                try {
                    c.close();
                } catch (Exception ex) {
                    Logger.getLogger(IoUtils.class.getName()).log(Level.WARNING, "Couldn't close Closeable.", ex);
                }
            }
        }
        if (socket != null) {
            try {
                socket.close();
            } catch (Exception ex) {
                Logger.getLogger(IoUtils.class.getName()).log(Level.WARNING, "Couldn't close Socket.", ex);
            }
        }
    }
 





import static IoUtils.*;
 
void whatever(){
  Resource wtfClosureFetishists = null;
  try{
   ...
 }finally{
    close(wtfClosureFetishists);
 }
 
}
31 posts on 3 pages.