/**
   * removeJobTasks: Cleanup mapTasks and reduceTasks for this job
   *
   * @param jobID
   */
  private void removeJobTasks(int jobID) {
    System.out.println(
        communicator.getLocalHostName() + " proceeding to remove map tasks for job " + jobID);
    mapLock.lock();
    Iterator<MapTask> itr = mapTasks.iterator();
    while (itr.hasNext()) {
      MapTask task = itr.next();
      if (task.getJobConf().getJobID() == jobID) {
        itr.remove();
      }
    }
    mapLock.unlock();

    System.out.println(
        communicator.getLocalHostName() + " proceeding to remove reduce tasks for job " + jobID);
    reduceLock.lock();
    Iterator<ReduceTask> itr1 = reduceTasks.iterator();
    while (itr1.hasNext()) {
      ReduceTask task = itr1.next();
      if (task.getJobConf().getJobID() == jobID) {
        itr1.remove();
      }
    }
    reduceLock.unlock();
  }
示例#2
0
 /**
  * Returns a cached copy of the given file. The cached copy is guaranteed to not be modified or
  * removed.
  *
  * @param original The source file.
  * @param baseDirFactory A factory that can provide a base directory for the file cache.
  * @return The cached file.
  */
 public File getCachedJar(File original, Factory<File> baseDirFactory) {
   File source = FileUtils.canonicalize(original);
   FileInfo fileInfo;
   // TODO - do some of this work concurrently
   lock.lock();
   try {
     fileInfo = cachedFiles.getIfPresent(source);
     if (fileInfo == null || !fileInfo.cachedFile.exists()) {
       // TODO - use the hashing service for this
       long lastModified = source.lastModified();
       long length = source.length();
       HashValue hashValue = HashUtil.createHash(source, "sha1");
       fileInfo = copyIntoCache(baseDirFactory, source, lastModified, length, hashValue);
     } else {
       // TODO - use the change detection service for this
       long lastModified = source.lastModified();
       long length = source.length();
       if (lastModified != fileInfo.lastModified || length != fileInfo.length) {
         HashValue hashValue = HashUtil.createHash(source, "sha1");
         if (!hashValue.equals(fileInfo.hashValue)) {
           fileInfo = copyIntoCache(baseDirFactory, source, lastModified, length, hashValue);
         }
       }
     }
   } finally {
     lock.unlock();
   }
   return fileInfo.cachedFile;
 }
示例#3
0
  /**
   * This method calculates the minimum view ID known by the current node. This method is only used
   * in a clustered cache, and only invoked when either a view change is detected, or a transaction
   * whose view ID is not the same as the current view ID.
   *
   * <p>This method is guarded by minViewRecalculationLock to prevent concurrent updates to the
   * minimum view ID field.
   *
   * @param idOfRemovedTransaction the view ID associated with the transaction that triggered this
   *     recalculation, or -1 if triggered by a view change event.
   */
  @GuardedBy("minViewRecalculationLock")
  private void calculateMinViewId(int idOfRemovedTransaction) {
    minViewRecalculationLock.lock();
    try {
      // We should only need to re-calculate the minimum view ID if the transaction being completed
      // has the same ID as the smallest known transaction ID, to check what the new smallest is.
      // We do this check
      // again here, since this is now within a synchronized method.
      if (idOfRemovedTransaction == -1
          || (idOfRemovedTransaction == minTxViewId && idOfRemovedTransaction < currentViewId)) {
        int minViewIdFound = currentViewId;

        for (CacheTransaction ct : localTransactions.values()) {
          int viewId = ct.getViewId();
          if (viewId < minViewIdFound) minViewIdFound = viewId;
        }
        for (CacheTransaction ct : remoteTransactions.values()) {
          int viewId = ct.getViewId();
          if (viewId < minViewIdFound) minViewIdFound = viewId;
        }
        if (minViewIdFound > minTxViewId) {
          log.tracef("Changing minimum view ID from %s to %s", minTxViewId, minViewIdFound);
          minTxViewId = minViewIdFound;
        } else {
          log.tracef("Minimum view ID still is %s; nothing to change", minViewIdFound);
        }
      }
    } finally {
      minViewRecalculationLock.unlock();
    }
  }
示例#4
0
  protected void flush(final Address new_coord) throws InterruptedException {
    // wait until all threads currently sending messages have returned (new threads after
    // flushing=true) will block
    // flushing is set to true in startFlusher()
    while (flushing && running) {
      if (in_flight_sends.get() == 0) break;
      Thread.sleep(100);
    }

    send_lock.lockInterruptibly();
    try {
      if (log.isTraceEnabled())
        log.trace(local_addr + ": coord changed from " + coord + " to " + new_coord);
      coord = new_coord;
      is_coord = Objects.equals(local_addr, coord);
      flushMessagesInForwardTable();
    } finally {
      if (log.isTraceEnabled()) log.trace(local_addr + ": flushing completed");
      flushing = false;
      ack_mode = true; // go to ack-mode after flushing
      num_acks = 0;
      send_cond.signalAll();
      send_lock.unlock();
    }
  }
 private void onFinishCommand() {
   lock.lock();
   try {
     String execution = currentCommandExecution;
     LOGGER.debug("onFinishCommand() called while execution = {}", execution);
     currentCommandExecution = null;
     onDisconnect = null;
     updateActivityTimestamp();
     switch (state) {
       case Running:
         try {
           onFinishCommand.run();
           condition.signalAll();
         } catch (Throwable throwable) {
           setState(State.Broken);
           throw UncheckedException.throwAsUncheckedException(throwable);
         }
         break;
       case StopRequested:
         stop();
         break;
       case Stopped:
         break;
       default:
         throw new IllegalStateException("Daemon is in unexpected state: " + state);
     }
   } finally {
     lock.unlock();
   }
 }
 /**
  * getReduceLoad: send current number of reduce tasks running on the node
  *
  * @return
  */
 public int getReduceLoad() {
   int size;
   reduceLock.lock();
   size = reduceTasks.size();
   reduceLock.unlock();
   return (size);
 }
示例#7
0
  public <T> T useCache(String operationDisplayName, Factory<? extends T> factory) {
    if (lockOptions != null && lockOptions.getMode() == FileLockManager.LockMode.Shared) {
      throw new UnsupportedOperationException("Not implemented yet.");
    }

    takeOwnership(operationDisplayName);
    boolean wasStarted = false;
    try {
      wasStarted = onStartWork();
      return factory.create();
    } finally {
      lock.lock();
      try {
        try {
          if (wasStarted) {
            onEndWork();
          }
        } finally {
          releaseOwnership();
        }
      } finally {
        lock.unlock();
      }
    }
  }
示例#8
0
  public final V get(K key) {
    if (key == null) {
      throw new NullPointerException("key == null");
    }

    // check to see if we already have a value
    readLock.lock();
    try {
      V value = map.get(key);
      if (value != null) {
        return value;
      }
    } finally {
      readLock.unlock();
    }

    // create a new value.  this may race and we might create more than one instance, but that's ok
    V newValue = create(key);
    if (newValue == null) {
      throw new NullPointerException("create returned null");
    }

    // write the new value and return it
    writeLock.lock();
    try {
      map.put(key, newValue);
      return newValue;
    } finally {
      writeLock.unlock();
    }
  }
  // 消息处理
  public void onEventMainThread(GetLastMonitorInfoEven even) {

    lock.lock();
    carId = even.getCarId();
    condition.signal();
    lock.unlock();
  }
 /**
  * Animates a markerWithPosition some time in the future, and removes it when the animation is
  * complete.
  *
  * @param marker the markerWithPosition to animate.
  * @param from the position to animate from.
  * @param to the position to animate to.
  */
 public void animateThenRemove(MarkerWithPosition marker, LatLng from, LatLng to) {
   lock.lock();
   AnimationTask animationTask = new AnimationTask(marker, from, to);
   animationTask.removeOnAnimationComplete(mClusterManager.getMarkerManager());
   mAnimationTasks.add(animationTask);
   lock.unlock();
 }
    @Override
    public void handleMessage(Message msg) {
      if (!mListenerAdded) {
        Looper.myQueue().addIdleHandler(this);
        mListenerAdded = true;
      }
      removeMessages(BLANK);

      lock.lock();
      try {

        // Perform up to 10 tasks at once.
        // Consider only performing 10 remove tasks, not adds and animations.
        // Removes are relatively slow and are much better when batched.
        for (int i = 0; i < 10; i++) {
          performNextTask();
        }

        if (!isBusy()) {
          mListenerAdded = false;
          Looper.myQueue().removeIdleHandler(this);
          // Signal any other threads that are waiting.
          busyCondition.signalAll();
        } else {
          // Sometimes the idle queue may not be called - schedule up some work regardless
          // of whether the UI thread is busy or not.
          // TODO: try to remove this.
          sendEmptyMessageDelayed(BLANK, 10);
        }
      } finally {
        lock.unlock();
      }
    }
示例#12
0
  private void filterQuarantined() {
    if (null == filter) {
      return;
    }

    bucketWriteLock.lock();
    try {
      ItemsFilter<E> itemsFilter = this.filter;
      if (itemsFilter != null) {
        debug(
            getThreadName()
                + " : filterQuarantined: filtering "
                + toolkitList.size()
                + " quarantined items");
        itemsFilter.filter(toolkitList);
        debug(
            getThreadName()
                + " : filterQuarantined: retained "
                + toolkitList.size()
                + " quarantined items");
      }
    } finally {
      bucketWriteLock.unlock();
    }
  }
示例#13
0
 // Do not take any clustered write lock in this path.
 public void add(final E item) {
   if (null == item) return;
   int maxQueueSize = config.getMaxQueueSize();
   bucketWriteLock.lock();
   boolean interrupted = false;
   try {
     if (maxQueueSize != UNLIMITED_QUEUE_SIZE) {
       while (!isCancelled() && toolkitList.size() >= maxQueueSize) {
         try {
           bucketNotFull.await();
         } catch (final InterruptedException e) {
           interrupted = true;
         }
       }
     }
     boolean signalNotEmpty = toolkitList.isEmpty();
     toolkitList.unlockedAdd(item);
     if (signalNotEmpty) {
       bucketNotEmpty.signalAll();
     }
   } finally {
     bucketWriteLock.unlock();
     if (interrupted) {
       Thread.currentThread().interrupt();
     }
   }
 }
示例#14
0
    public void run() {

      otherMethod(l1);
      otherMethod(l2);
      l1.unlock();
      l2.unlock();
    }
  public void waitForLSPaddition(long lspId, long timeWait) {
    Lock lock;
    Condition lspEstablished;
    try {
      lock = lockList.get(lspId);
      if (lock == null) log.info("Lock is NULL!");
      lspEstablished = conditionList.get(lspId);
    } catch (Exception e) {
      return;
    }

    lock.lock();
    try {
      if (established == false) {
        log.info("Waiting " + timeWait + " ms  for LSP " + lspId + " to be established");
        lspEstablished.await(timeWait, TimeUnit.MILLISECONDS);
      } else {
        log.info("Inside waitForLSPaddition lockList.remove");
        // FIXME: Revisar esto
        lockList.remove(lspId);
        conditionList.remove(lspId);
      }
      log.info("LSP " + lspId + " has been established");
    } catch (InterruptedException e) {
      return;
    } finally {
      lock.unlock();
    }
  }
  /*
   * checkUpdateState
   *
   * checkUpdateState will only allow one thread to update the state at one time.
   * Those threads who want to access the state will wait on a lock until the updating thread finishes
   * the attempt to update.
   *
   * In the event there's an exception when a thread updates the state, there is no side-effect on the state itself
   * or on the trackerclients. Other threads will attempt the update the state as if the previous attempt did not happen.
   *
   * @param clusterGenerationId
   * @param trackerClients
   */
  private void checkUpdateState(long clusterGenerationId, List<TrackerClient> trackerClients) {
    DegraderLoadBalancerStrategyConfig config = getConfig();

    if (!_state.isInitialized()) {
      // threads attempt to access the state would block here if state is not initialized
      _lock.lock();
      try {
        if (!_state.isInitialized()) {
          debug(_log, "initializing load balancer strategy state");
          updateState(clusterGenerationId, trackerClients, config);
          if (!getState().isInitialized()) {
            _log.error("Failed to initialize state");
          }
        }
      } finally {
        _lock.unlock();
      }
    } else if (shouldUpdate(clusterGenerationId, _state, config, _updateEnabled)) {
      // threads attempt to update the state would return immediately if some thread is already in
      // the updating process
      if (_lock.tryLock()) {
        try {
          if (shouldUpdate(clusterGenerationId, _state, config, _updateEnabled)) {
            debug(_log, "updating for cluster generation id: ", clusterGenerationId);
            debug(_log, "old state was: ", _state);
            updateState(clusterGenerationId, trackerClients, config);
          }
        } finally {
          _lock.unlock();
        }
      }
    }
  }
  public void markServerDown(String id) {
    boolean triggered = false;

    id = Server.normalizeId(id);
    if (id == null) {
      return;
    }

    Lock writeLock = upServerLock.writeLock();
    try {
      writeLock.lock();

      final List<Server> changedServers = new ArrayList<Server>();

      for (Server svr : upServerList) {
        if (svr.isAlive() && (svr.getId().equals(id))) {
          triggered = true;
          svr.setAlive(false);
          changedServers.add(svr);
        }
      }

      if (triggered) {
        logger.error("LoadBalancer:  markServerDown called on [" + id + "]");
        notifyServerStatusChangeListener(changedServers);
      }
    } finally {
      try {
        writeLock.unlock();
      } catch (Exception e) { // NOPMD
      }
    }
  }
  /**
   * Returns a list of catalogs for the current session.
   *
   * <p>The cache is stored in the platform's caches in the region {@link #CATALOG_CACHE_REGION}. It
   * is also segmented by locale, but we only return the correct sub-region according to the session
   * passed as a parameter.
   */
  @SuppressWarnings("unchecked")
  protected synchronized List<IOlapService.Catalog> getCache(IPentahoSession session) {
    // Create the cache region if necessary.
    final ICacheManager cacheMgr = PentahoSystem.getCacheManager(session);
    final Object cacheKey = makeCacheSubRegionKey(getLocale());

    final Lock writeLock = cacheLock.writeLock();
    try {

      writeLock.lock();

      if (!cacheMgr.cacheEnabled(CATALOG_CACHE_REGION)) {
        // Create the region. This requires write access.
        cacheMgr.addCacheRegion(CATALOG_CACHE_REGION);
      }

      if (cacheMgr.getFromRegionCache(CATALOG_CACHE_REGION, cacheKey) == null) {
        // Create the sub-region. This requires write access.
        cacheMgr.putInRegionCache(
            CATALOG_CACHE_REGION, cacheKey, new ArrayList<IOlapService.Catalog>());
      }

      return (List<IOlapService.Catalog>)
          cacheMgr.getFromRegionCache(CATALOG_CACHE_REGION, cacheKey);

    } finally {
      writeLock.unlock();
    }
  }
示例#19
0
  public <K, V> MultiProcessSafePersistentIndexedCache<K, V> newCache(
      final PersistentIndexedCacheParameters<K, V> parameters) {
    Factory<BTreePersistentIndexedCache<K, V>> indexedCacheFactory =
        new Factory<BTreePersistentIndexedCache<K, V>>() {
          public BTreePersistentIndexedCache<K, V> create() {
            return doCreateCache(
                parameters.getCacheFile(),
                parameters.getKeySerializer(),
                parameters.getValueSerializer());
          }
        };
    MultiProcessSafePersistentIndexedCache<K, V> indexedCache =
        parameters.decorate(
            new DefaultMultiProcessSafePersistentIndexedCache<K, V>(
                indexedCacheFactory, fileAccess));

    lock.lock();
    try {
      caches.add(indexedCache);
      if (fileLock != null) {
        indexedCache.onStartWork(operations.getDescription(), stateAtOpen);
      }
    } finally {
      lock.unlock();
    }
    return indexedCache;
  }
  public List<IOlapService.Catalog> getCatalogs(IPentahoSession session)
      throws IOlapServiceException {

    // Make sure the cache is initialized.
    initCache(session);
    final List<Catalog> cache = getCache(session);

    final Lock readLock = cacheLock.readLock();
    try {
      readLock.lock();

      final List<IOlapService.Catalog> catalogs = new ArrayList<IOlapService.Catalog>();
      for (Catalog catalog : cache) {
        if (hasAccess(catalog.name, EnumSet.of(RepositoryFilePermission.READ), session)) {
          catalogs.add(catalog);
        }
      }

      // Do not leak the cache list.
      // Do not allow modifications on the list.
      return Collections.unmodifiableList(new ArrayList<IOlapService.Catalog>(cache));

    } finally {
      readLock.unlock();
    }
  }
 /**
  * getMapLoad: Send current number of map tasks running on the node
  *
  * @return
  */
 public int getMapLoad() {
   int size;
   mapLock.lock();
   size = mapTasks.size();
   mapLock.unlock();
   return (size);
 }
示例#22
0
  @SuppressWarnings("unchecked")
  private <E extends Event> void doPost(E e) {
    ListenerBucket<E> b;
    Listener<? super E>[] ls = null;

    Lock l = locks.get(e);
    l.lock();
    try {
      if (retained) {
        if ((b = (ListenerBucket<E>) handlers.get(e)) == null)
          handlers.put(e, b = (ListenerBucket<E>) bucketSupplier.get());
        ls = b.post(e);
      } else {
        if ((b = (ListenerBucket<E>) handlers.get(e)) != null) {
          ls = b.post(e);
          if (b.isEmpty()) handlers.remove(e);
        }
      }
    } finally {
      l.unlock();
    }

    if (ls != null) {
      for (Listener<? super E> li : ls) {
        try {
          li.execute(e);
        } catch (RejectedExecutionException ex) {
          Log.get().postSevere("Event listener rejected! Did the executor shut down?", ex);
        }
      }
    }
  }
  private void onStartCommand(String commandDisplayName, Runnable onDisconnect) {
    lock.lock();
    try {
      switch (state) {
        case Broken:
          throw new DaemonUnavailableException("This daemon is in a broken state and will stop.");
        case StopRequested:
          throw new DaemonUnavailableException("This daemon is currently stopping.");
        case Stopped:
          throw new DaemonUnavailableException("This daemon has stopped.");
      }
      if (currentCommandExecution != null) {
        throw new DaemonUnavailableException(
            String.format("This daemon is currently executing: %s", currentCommandExecution));
      }

      LOGGER.debug(
          "onStartCommand({}) called after {} minutes of idle",
          commandDisplayName,
          getIdleMinutes());
      try {
        onStartCommand.run();
        currentCommandExecution = commandDisplayName;
        this.onDisconnect = onDisconnect;
        updateActivityTimestamp();
        condition.signalAll();
      } catch (Throwable throwable) {
        setState(State.Broken);
        throw UncheckedException.throwAsUncheckedException(throwable);
      }
    } finally {
      lock.unlock();
    }
  }
 /**
  * Tries to call the given consistent operation while holding the given lock.
  *
  * <p>If this is the first execution of this method on the call stack of the current thread, then
  * the lock gets acquired using {@link Lock#lock()}. Once the lock has been acquired the operation
  * gets called. If this fails for some reason and the thrown exception chain contains a {@link
  * FsNeedsLockRetryException}, then the lock gets temporarily released and the current thread gets
  * paused for a small random time interval before this procedure starts over again. Otherwise, the
  * exception chain gets just passed on to the caller.
  *
  * <p>If this is <em>not</em> the first execution of this method on the call stack of the current
  * thread, then the lock gets acquired using {@link Lock#tryLock()} instead. If this fails, an
  * {@code FsNeedsLockRetryException} gets created and passed to the given exception handler for
  * mapping before finally throwing the resulting exception by executing {@code throw
  * handler.fail(new FsNeedsLockRetryException())}. Once the lock has been acquired the operation
  * gets called. If this fails for some reason then the exception chain gets just passed on to the
  * caller.
  *
  * <p>This algorithm prevents dead locks effectively by temporarily unwinding the stack and
  * releasing all locks for a small random time interval. Note that this requires some minimal
  * cooperation by the operation: Whenever it throws an exception, it MUST leave its resources in a
  * consistent state so that it can get retried again! Mind that this is standard requirement for
  * any {@link FsController}.
  *
  * @param  <T> The return type of the operation.
  * @param operation The atomic operation.
  * @param lock The lock to hold while calling the operation.
  * @return The result of the operation.
  * @throws IOException As thrown by the operation.
  * @throws FsNeedsLockRetryException See above.
  */
 private <T> T locked(final Operation<T> operation, final Lock lock) throws IOException {
   final Account account = accounts.get();
   if (0 < account.lockCount) {
     if (!lock.tryLock()) throw FsNeedsLockRetryException.get();
     account.lockCount++;
     try {
       return operation.call();
     } finally {
       account.lockCount--;
       lock.unlock();
     }
   } else {
     try {
       while (true) {
         try {
           lock.lock();
           account.lockCount++;
           try {
             return operation.call();
           } finally {
             account.lockCount--;
             lock.unlock();
           }
         } catch (FsNeedsLockRetryException ex) {
           account.pause();
         }
       }
     } finally {
       accounts.remove();
     }
   }
 }
示例#25
0
 public static List<Server> getServers() {
   readlock.lock();
   @SuppressWarnings("unchecked")
   ArrayList<Server> ss = (ArrayList<Server>) ((ArrayList<Server>) servers).clone();
   readlock.unlock();
   return ss;
 }
示例#26
0
 @Override
 public void handleEvents(com.sun.jdi.event.EventSet eventSet) throws DebuggerException {
   boolean resume = true;
   try {
     for (com.sun.jdi.event.Event event : eventSet) {
       LOG.debug("New event: {}", event);
       if (event instanceof com.sun.jdi.event.BreakpointEvent) {
         lock.lock();
         try {
           resume = processBreakPointEvent((com.sun.jdi.event.BreakpointEvent) event);
         } finally {
           lock.unlock();
         }
       } else if (event instanceof com.sun.jdi.event.StepEvent) {
         lock.lock();
         try {
           resume = processStepEvent((com.sun.jdi.event.StepEvent) event);
         } finally {
           lock.unlock();
         }
       } else if (event instanceof com.sun.jdi.event.VMDisconnectEvent) {
         resume = processDisconnectEvent();
       } else if (event instanceof com.sun.jdi.event.ClassPrepareEvent) {
         resume = processClassPrepareEvent((com.sun.jdi.event.ClassPrepareEvent) event);
       }
     }
   } finally {
     if (resume) {
       eventSet.resume();
     }
   }
 }
示例#27
0
  protected void forwardToCoord(long seqno, Message msg) {
    if (is_coord) {
      forward(msg, seqno, false);
      return;
    }

    if (!running || flushing) {
      forward_table.put(seqno, msg);
      return;
    }

    if (!ack_mode) {
      forward_table.put(seqno, msg);
      forward(msg, seqno, false);
      return;
    }

    send_lock.lock();
    try {
      forward_table.put(seqno, msg);
      while (running && !flushing) {
        ack_promise.reset();
        forward(msg, seqno, true);
        if (!ack_mode || !running || flushing) break;
        Long ack = ack_promise.getResult(500);
        if ((Objects.equals(ack, seqno)) || !forward_table.containsKey(seqno)) break;
      }
    } finally {
      send_lock.unlock();
    }
  }
示例#28
0
  /**
   * Method openMe.
   *
   * @param opener Player
   * @param autoClose boolean
   * @return boolean
   */
  public boolean openMe(Player opener, boolean autoClose) {
    _openLock.lock();

    try {
      if (!setOpen(true)) {
        return false;
      }

      setGeoOpen(true);
    } finally {
      _openLock.unlock();
    }
    broadcastStatusUpdate();

    if (autoClose && (getTemplate().getCloseTime() > 0)) {
      scheduleAutoAction(false, getTemplate().getCloseTime() * 1000L);
    }

    getAI().onEvtOpen(opener);

    for (Listener<Creature> l : getListeners().getListeners()) {
      if (l instanceof OnOpenCloseListener) {
        ((OnOpenCloseListener) l).onOpen(this);
      }
    }

    return true;
  }
示例#29
0
  void doRemoveAddress(Address deadAddress, boolean destroyConnection) {
    if (!ensureMemberIsRemovable(deadAddress)) {
      return;
    }

    lock.lock();
    try {
      if (deadAddress.equals(node.getMasterAddress())) {
        assignNewMaster();
      }
      if (node.isMaster()) {
        clusterJoinManager.removeJoin(new MemberInfo(deadAddress));
      }
      Connection conn = node.connectionManager.getConnection(deadAddress);
      if (destroyConnection && conn != null) {
        node.connectionManager.destroyConnection(conn);
      }
      MemberImpl deadMember = getMember(deadAddress);
      if (deadMember != null) {
        removeMember(deadMember);
        logger.info(membersString());
      }
    } finally {
      lock.unlock();
    }
  }
示例#30
0
  public List<State> getBlue(Graph graph, State state) {

    List<State> result = graph.post(state);

    try {
      blueLock.lock();

      if (!blueMap.containsKey(state) || blueMap.get(state).size() == 0) {

        ArrayList<State> succesors = (ArrayList<State>) graph.post(state);
        ArrayList<ArrayList<State>> permutations = new ArrayList<ArrayList<State>>();
        if (succesors.size() > 1) {
          permute(succesors, 0, succesors.size() - 1, permutations);
        } else {
          permutations.add(succesors);
        }
        blueMap.put(state, permutations);
      }
    } finally {
      blueLock.unlock();
    }

    try {
      blueLock.lock();
      result = (List<State>) blueMap.get(state).get(blueMap.get(state).size() - 1);
    } finally {
      blueLock.unlock();
    }

    return result;
  }