/**
   * Invokes the object after acquiring the read lock (if necessary). If invoked when the read lock
   * has not yet been acquired, then the lock is acquired for the duration of the call. If the lock
   * has already been acquired, then the status of the lock is not changed.
   *
   * <p>TODO: Check to see if the write lock is acquired and <em>not</em> acquire the read lock in
   * that situation. Currently this code is not re-entrant. If a write lock is already acquired and
   * the thread attempts to get the read lock, then the thread will hang. For the moment, all the
   * uses of ConcurrentBarrier are coded in such a way that reentrant locks are not a problem.
   *
   * @param <T>
   * @param invokable
   * @return the result of invoking the invokable
   */
  public <T> T withRead(Invokable<T> invokable) {
    boolean readLockedAtEntry;

    synchronized (threadHasReadLock) {
      readLockedAtEntry = threadHasReadLock.get();
    }

    if (!readLockedAtEntry) {
      lock.readLock().lock();

      synchronized (threadHasReadLock) {
        threadHasReadLock.set(true);
      }
    }

    try {
      return invokable.invoke();
    } finally {
      if (!readLockedAtEntry) {
        lock.readLock().unlock();

        synchronized (threadHasReadLock) {
          threadHasReadLock.remove();
        }
      }
    }
  }
  @Override
  public <T> Future<T> invoke(Invokable<T> invocable) {
    final T result = invocable.invoke();

    return new Future<T>() {
      @Override
      public boolean cancel(boolean mayInterruptIfRunning) {
        return false;
      }

      @Override
      public boolean isCancelled() {
        return false;
      }

      @Override
      public boolean isDone() {
        return true;
      }

      @Override
      public T get() throws InterruptedException, ExecutionException {
        return result;
      }

      @Override
      public T get(long timeout, TimeUnit unit)
          throws InterruptedException, ExecutionException, TimeoutException {
        return result;
      }
    };
  }
  /**
   * Acquires the exclusive write lock before invoking the Invokable. The code will be executed
   * exclusively, no other reader or writer threads will exist (they will be blocked waiting for the
   * lock). If the current thread has a read lock, it is released before attempting to acquire the
   * write lock, and re-acquired after the write lock is released. Note that in that short window,
   * between releasing the read lock and acquiring the write lock, it is entirely possible that some
   * other thread will sneak in and do some work, so the {@link Invokable} object should be prepared
   * for cases where the state has changed slightly, despite holding the read lock. This usually
   * manifests as race conditions where either a) some parallel unrelated bit of work has occured or
   * b) duplicate work has occured. The latter is only problematic if the operation is very
   * expensive.
   *
   * @param <T>
   * @param invokable
   */
  public <T> T withWrite(Invokable<T> invokable) {
    boolean readLockedAtEntry = releaseReadLock();

    lock.writeLock().lock();

    try {
      return invokable.invoke();
    } finally {
      lock.writeLock().unlock();
      restoreReadLock(readLockedAtEntry);
    }
  }
 @Override
 public <T> T invoke(Class<T> proxyType, Invokable<T> invocable) {
   return invocable.invoke();
 }