Class ReentryGuard<T>

java.lang.Object
generic.concurrent.ReentryGuard<T>
Type Parameters:
T - the type used to record information about a violation. It cannot be Void.

public abstract class ReentryGuard<T> extends Object
A means of detecting and handling reentrant conditions.

One example where this has been applied deals with updating actions upon changes in context. If, in the course of determining which actions are enabled, one of the isEnabled methods displays an error dialog, the Swing thread reenters its main loop while that dialog is showing, but before isEnabled has returned. This can cause all sorts of unexpected behaviors. Namely, a timer could fire, context could change again, etc., and the list of actions being updated may also change. At worst, this could result in many exceptions being thrown, because a data structure has been modified concurrently. At best, if the loop is allowed to finish, there's a lot of wasted time updating actions that will never be displayed.

In that example, the loop that updates the actions would be the "guarded block." Any point at which the list of actions is modified might result in "reentrant access" and should be checked.

This class provides a primitive for instrumenting, detecting, and properly reacting to such conditions. For example, if the modification should not be allowed at all, the guard can throw an exception at the moment of reentrant access. Alternatively, if the modification should be allowed, the guard would simply set a flag, then the guarded block can check that flag and terminate early.

This implementation is not thread safe. It is designed to check for reentrant access, not concurrent access. The client must ensure that only one thread enters the guarded block or calls checkAccess() at a time. Otherwise, the behavior is undefined.

 public class ActionManager {
        private final ReentryGuard<Throwable> reentryGuard = new ReentryGuard<>() {
                @Override
                public Throwable violated(boolean nested, Throwable previous) {
                        if (previous != null) {
                                return previous;
                        }
                        return new Throwable(); // record the stack of the violation
                }
        };
        private final List<Action> actions;
 
        public void addAction(Action action) {
                // Notify the guard we've committed some reentrant behavior.
                // Would need to add this to anything that modifies the action list. 
                reentryGuard.checkAccess();
                actions.add(action);
        }
 
        public void updateActions(Context ctx) {
                try (Guarded guarded = reentryGuard.enter()) {
                        // There is no need to create a copy, since we'll bail before the next iteration
                        for (Action action : actions) {
                                boolean enabled = action.isEnabledForContext(ctx);
                                if (reentryGuard.getViolation() != null) {
                                        break; // Actions has been modified. Bail.
                                        // NOTE: This leaves the update incomplete.
                                        // Something has to call updateActions again.
                                }
                                actions.setEnabled(enabled);
                        }
                }
        }
 }
 
  • Constructor Details

    • ReentryGuard

      public ReentryGuard()
  • Method Details

    • enter

      public ReentryGuard.Guarded enter()
      Notify the guard of entry into the guarded block

      This should always be used in a try-with-resources block. This will ensure that the guard is notified of exit from the guarded block, even in exceptional circumstances.

      NOTE: Re-entering the guarded portion is itself a violation.

      Returns:
      a closeable for notifying the guard of exit from the guarded block, or null if reentering the guarded block
    • checkAccess

      public void checkAccess()
      Notify the guard of access to some resource used by the guarded block

      If the access turns out to be reentrant, i.e., the thread's current stack includes a frame in the guarded block, this will call violated(boolean, Object) and record the result. It can be inspected later via getViolation().

    • violated

      protected abstract T violated(boolean nested, T previous)
      Record a violation

      This method is called if checkAccess() or enter() is called while already inside a guarded block. Its return value is stored for later inspection by getViolation(). It's possible multiple violations occur within one execution of the guarded block. The previous return value of this method is provided, if that is the case. To record only the first violation, this method should just return previous when it is non-null. To record only the last violation, this method should disregard previous. To record all violations, T will need to be a collection, and this method will need to create and/or append to the collection.

      Parameters:
      nested - true if the violation is a nested call to enter(); false if the violation is a call to checkAccess()
      previous - the previous return value of this method, on the occasion of multiple violations
      Returns:
      the record of the violation
    • getViolation

      public T getViolation()
      Retrieve a violation, if applicable

      Calling this method outside of a guarded block has undefined behavior.

      Returns:
      the violation; or null to indicate no violation
    • isViolated

      public boolean isViolated()
      Check if there is a violation

      This is equivalent to checking if getViolation() returns non-null.

      Returns:
      true if there is a violation.