What are the Semantics of Uninterruptible Code?
Declaring a method uninterruptible enables a Jikes RVM developer to prevent the Jikes RVM compilers from inserting "hidden" thread switch points in the compiled code for the method. As a result, the code can be written assuming that it cannot involuntarily "lose control" while executing due to a timer-driven thread switch. In particular, neither yield points nor stack overflow checks will be generated for uninterruptible methods.
When writing uninterruptible code, the programmer is restricted to a subset of the Java language. The following are the restrictions on uninterruptible code.
- Because a stack overflow check represents a potential yield point (if GC is triggered when the stack is grown), stack overflow checks are omitted from the prologues of uninterruptible code. As a result, all uninterruptible code must be able to execute in the stack space available to them when the first uninterruptible method on the call stack is invoked. This is typically about 8K for uninterruptible regions called from mutator code. The collector threads must preallocate enough stack space, since all collector code is uninterruptible. As a result, using recursive methods in the GC subsystem is a bad idea.
- Since no yield points are inserted in uninterruptible code, there will be no timer-driven thread switches while executing it. So, if possible, one should avoid "long running" uninterruptible methods outside of the GC subsystem.
- Certain bytecodes are forbidden in uninterruptible code, because Jikes RVM cannot implement them in a manner that ensures uninterruptibility. The forbidden bytecodes are: aastore ; invokeinterface ; new ; newarray ; anewarray ; athrow ; checkcast and instanceof unless the LHS type is a final class ; monitorenter , monitorexit , multianewarray.
- Uninterruptible code cannot cause class loading and thus must not contain unresolved getstatic, putstatic, getfield, putfield ,invokevirtual, or invokestatic bytecodes.
- Uninterruptible code cannot contain calls to interruptible code. As a consequence, it is illegal to override an uninterruptible virtual method with an interruptible method.
- Uninterruptible methods cannot be synchronized.
We have augmented the baseline compiler to print a warning message when one of these restrictions is violated. If uninterruptible code were to raise a runtime exception such as NullPointerException, ArrayIndexOutOfBoundsException, or ClassCastException, then it could be interrupted. We assume that such conditions are a programming error and do not flag bytecodes that might result in one of these exceptions being raised as a violation of uninterruptibility. Checking for a particular method can be disabled by annotation the method with org.vmmagic.pragmas.LogicallyUninterruptible
. This should be done with extreme care, but in a few cases is necessary to avoid spurious warning messages.
The following rules determine whether or not a method is uninterruptible.
- All class initializers are interruptible, since they can only be invoked during class loading.
- All object constructors are interruptible, since they an only be invoked as part of the implementation of the new bytecode.
- If a method is annotated with
org.vmmagic.pragmas.Interruptible
then it is interruptible. - If none of the above rules apply and a method is annotated with
org.vmmagic.pragmas.Uninterruptible
, then it is uninterruptible. - If none of the above rules apply and the declaring class is annotated with
org.vmmagic.pragmas.Uninterruptible
then it is uninterruptible.
Whether to annotate a class or a method with org.vmmagic.pragmas.Uninterruptible
is a matter of taste and mainly depends on the ratio of interruptible to uninterruptible methods in a class. If most methods of the class should be uninterruptible, then annotated the class is preferred.