@ConditionBlock
  public void within(double seconds, Closure<?> conditions) throws InterruptedException {
    long timeoutMillis = toMillis(seconds);
    long start = System.currentTimeMillis();
    // Thread.sleep(0) would yield the processor to higher-priority threads, which would slow down a polling condition
    // without initial delay unnecessarily.
    if (initialDelay > 0) {
      Thread.sleep(toMillis(initialDelay));
    }

    long currDelay = toMillis(delay);
    int attempts = 0;
    long elapsedTime = 0;
    Throwable testException = null;

    while (elapsedTime <= timeoutMillis) {
      try {
        attempts++;
        GroovyRuntimeUtil.invokeClosure(conditions);
        return;
      }
      catch (Throwable e) {
        elapsedTime = System.currentTimeMillis() - start;
        testException = e;
        final long sleepMillis = Math.min(currDelay, start + timeoutMillis - System.currentTimeMillis());
        // Thread.sleep(0) would yield the processor to higher-priority threads, which would slow down the polling
        // condition unnecessarily. Thread.sleep(negativeValue) would even yield an exception.
        if (sleepMillis > 0) {
          Thread.sleep(sleepMillis);
        }
        currDelay *= factor;
      }
    }

    String msg = String.format(Locale.ENGLISH, "Condition not satisfied after %1.2f seconds and %d attempts", elapsedTime / 1000d, attempts);
    if (timeoutMessage != null) {
      msg = String.format(Locale.ENGLISH, "%s: %s", msg, GroovyRuntimeUtil.invokeClosure(timeoutMessage, testException));
    }
    throw new SpockTimeoutError(seconds, msg, testException);
  }
Repeatedly evaluates the specified conditions until they are satisfied or the specified timeout (in seconds) has elapsed.

@param conditions the conditions to evaluate

@throws InterruptedException if evaluation is interrupted
spock-core/src/main/java/spock/util/concurrent/PollingConditions.java