public void run() {

    while (true) {

      IResponse resp = responseQueue.removeResponse();

      Integer respId = (Integer) resp.getId();
      SPFSession session = sessions.remove(respId);
      FutureSPFResult result = results.remove(respId);

      DNSLookupContinuation cont =
          (DNSLookupContinuation) session.getAttribute(ATTRIBUTE_STAGED_EXECUTOR_CONTINUATION);

      DNSResponse response;
      if (resp.getException() != null) {
        response = new DNSResponse((TimeoutException) resp.getException());
      } else {
        response = new DNSResponse(resp.getValue());
      }

      try {
        cont = cont.getListener().onDNSResponse(response, session);

        if (cont != null) {
          invokeAsynchService(session, result, cont, false);
        } else {
          execute(session, result, false);
        }

      } catch (Exception e) {
        SPFChecker checker = null;
        while (e != null) {
          while (checker == null || !(checker instanceof SPFCheckerExceptionCatcher)) {
            checker = session.popChecker();
          }
          try {
            ((SPFCheckerExceptionCatcher) checker).onException(e, session);
            e = null;
          } catch (SPFResultException ex) {
            e = ex;
          } finally {
            checker = null;
          }
        }
        execute(session, result, false);
      }
    }
  }
 /**
  * throttle should be true only when the caller thread is the client and not the worker thread. We
  * could even remove the throttle parameter and check the currentThread. This way the worker is
  * never "blocked" while outside callers will be blocked if our queue is too big (so this is not
  * fully "asynchronous").
  */
 private synchronized void invokeAsynchService(
     SPFSession session, FutureSPFResult result, DNSLookupContinuation cont, boolean throttle) {
   while (throttle && results.size() > 50) {
     try {
       this.wait(100);
     } catch (InterruptedException e) {
     }
   }
   int nextId = nextId();
   sessions.put(new Integer(nextId), session);
   results.put(new Integer(nextId), result);
   session.setAttribute(ATTRIBUTE_STAGED_EXECUTOR_CONTINUATION, cont);
   dnsProbe.getRecordsAsynch(cont.getRequest(), nextId, responseQueue);
 }