Exception Handling

12.1. Exceptions and Asynchrony

In the asynchronous environment provided by ProActive, exceptions cannot be handled the same as in a sequential environment. Let's see the problem with exceptions and asynchrony in a piece of code:

try {
   Result r = someAO.someMethodCall(); // Asynchronous method call that can throw an exception
   // ...
   doSomethingWith(r);
}  catch (SomeException se) {
   doSomethingWithMyException(se);
}  

As the method call in line 2 is asynchronous, the program continues execution without waiting for its completion. So, it is possible for the control flow to exit the try. In this case, if the method call finishes with an exception, we cannot throw it back in the code anymore because we are no more in the try block. That is why ProActive method calls with potential exceptions are handled synchronously by default.

12.1.1. Barriers around try blocks

The ProActive solution to this problem is to put barriers around try/catch blocks. This way the control flow cannot exit the block, the exception can be handled in the appropriate catch block, and the call is asynchronous within the block.

With this configuration, the potential exception can be thrown at several points:

  • when accessing a future

  • inside the barrier

  • by using the provided API (see after)

Let's reuse the previous example to see how to use these barriers

PAException.tryWithCatch(SomeException.class);
try {
   Result r = someAO.someMethodCall(); // Asynchronous method call that can throw an exception
   // ...
   doSomethingWith(r);
   PAException.endTryWithCatch();
} catch (SomeException se) {
   doSomethingWithMyException(se);
} finally {
   PAException.removeTryWithCatch();
} 

With this code, the call in line 3 will be asynchronous, and the exception will be handled in the correct catch block. Even if this implies waiting at the end of the try block for the completion of the call.

Let's see in detail the needed modifications to the code:

  • PAException.tryWithCatch() call right before the try block. The parameter is either the caught exception class or an array of these classes if there are many

  • PAException.endTryWithCatch() at the end of the try block

  • PAException.removeTryWithCatch() at the beginning of the finally block, so the block becomes mandatory

12.1.2. TryWithCatch Annotator

These needed annotations can be seen as cumbersome, so we provide a tool to add them automatically to a given source file. It transforms the first example code in the second. Here is a sample session with the tool:

$ ProActive/bin/trywithcatch.sh MyClass.java 
--- ProActive TryWithCatch annotator -----------------------
$ diff -u MyClass.java~ MyClass.java
--- MyClass.java~
+++ MyClass.java
@@ -1,9 +1,13 @@
 public class MyClass {
   public MyClass someMethod(AnotherClass a) {
+     PAException.tryWithCatch(AnException.class);
         try {
            return a.aMethod();
+           PAException.endTryWithCatch();
         } catch (AnException ae) {
            return null;
+        } finally {
+           PAException.removeTryWithCatch();
         }
   }
 }

As we can see, ProActive method calls are added to make sure all calls within try/catch blocks are handled asynchronously. The tool can be found in ProActive/scripts/unix/.

12.1.3. throwArrivedException() and waitForPotentialException()

We have seen the 3 methods mandatory to perform asynchronous calls with exceptions, however the complete API includes two more calls. So far the blocks boundaries define the barries. But, some control over the barrier is provided thanks to two additional methods.

The first method is PAException.throwArrivedException(). During a computation an exception may be raised but there is no point from where the exception can be thrown (a future or a barrier). The solution is to call the PAException.throwArrivedException() method which simply queries ProActive to see if an exception has arrived with no opportunity of being thrown back in the user code. In this case, the exception is thrown by this method.

The method behaviour is thus dependent on the timing. That is, calling this method may or may not result in an exception being thrown, depending on the time for an exception to come back. That is why another method is provided: PAException.waitForPotentialException(). Unlike the previous PAException.throwArrivedException(), this method is blocking. After calling this method, either an exception is thrown or it is assured that all previous calls in the block completed successfully, so no exception can be thrown from the previous calls.