Dynamic Extent in Java

In my work, I tend to borrow heavily from the Lisp tradition, even when writing in Java. One of my go to techniques from the ‘Lisp world’ is using Runnable‘s in API’s to explicitly enforce dynamic extent. Without getting too heavily into the Lisp-specific terminology, dynamic extent is what you need to write code for the following kinds of scenarios:

  • ‘Ensure that an opened file is closed, after it’s been used’
  • ‘Ensure that this lock is released at the end of this critical section’.

These scenarios are common enough that Java provides explicit support for dynamic extent with the try...finally and synchronized(...) { ...} statements. However, both of these have to be explicitly written by the caller of an API, at each call site. There’s no way for a file API to use these constructs to automatically enforce that all opened files are eventually closed. The API is limited to providing open and closed and trusting that the caller will use try...finally. The technique I’m about to describe makes it possible to write an an API that removes that burden from the caller.

To see what it looks like in code, here’s a simple implementation of a function that calls a Runnable, bracketing it with code that establishes and tears down the dynamic extent.

   void callWithDynamicExtent(Runnable runnable)
   {
      try {
         // establish pre-condition here (open file, acquirelock, etc.)
         establishPreCondition();

         runnable.run();

      } finally {
         // establish post-condition here (close file, release lock, etc.)
         establishPostCondition();
      }
   }

Aside from the use of Runnable, this is idiomatic code for performing this type of task in Java. The difference here is that the try...finally machinery can be localized in a single function within the implementation of an API. The API can use this to enforce the need for dynamic extent and lower the burden on callers to remember how to write all the try...finally logic at each call site. For scenarios where the caller needs the additional control, the API can still expose the manual establishPreCondition and establishPostCondition calls. (Use of these could be monitored via project code review policy, etc.)

One additional refinement of this technique is to use nested Runnable‘s to enforce dynamic extent, but defer the actual invocation until a later time. Consider a case when a Runnable is being passed into a TaskExecutor. The next implementation of callWithDynamicExtent provides two entry points: one that immediately executes the Runnable and a second that allows the dynamic extent to be enforced at one point, with the actual invocation deferred until later.

   Runnable bindRunnable(final Runnable runnable)
   {
      return new Runnable() {
         public void run() {
            try {
               // establish pre-condition here (open file, acquirelock, etc.)
               establishPreCondition();

               runnable.run();

            } finally {
               // establish post-condition here (close file, release lock, etc.)
               establishPostCondition();
            }
      };
   }

   void callWithDynamicExtent(Runnable runnable)
   {
      bindRunnable(runnable).run();
   }

Java 7 adds additional support for this scenario in the form of try...with...resources.

Tags: , , Categories: Java
Trackback URL for this post: https://www.ksmpartners.com/2013/06/dynamic-extent-in-java/trackback/

One Response to Dynamic Extent in Java

  1. Pingback: Thread Local State and its Interaction with Thread Pools |

Leave a Reply

Your email address will not be published. Required fields are marked *