private int getNumTuples() {
    long elapsedTimeMs = System.currentTimeMillis() - intervalStartTime;
    int desired = (int) (elapsedTimeMs * rate / 1000 - intervalNumTuplesSent);

    // correct rate = rate / 10
    int max = (rate / 10 + 1) * 12 / 10;

    if (desired > max) return max;

    return desired;
  }
  public void unpause() {
    assert (state == PAUSED || state == END);

    synchronized (this) {
      if (state == END || state == TERMINATED) return;

      state = RUNNING;
    }

    intervalNumTuplesSent = 0;
    intervalStartTime = System.currentTimeMillis();
  }
  public void setRate(int newRate) {
    assert (state == START || state == RUNNING || state == END || state == PAUSED);

    if (state == END) return;

    if (rate != newRate) {
      rate = newRate;

      if (state == RUNNING) {
        intervalStartTime = System.currentTimeMillis();
        intervalNumTuplesSent = 0;
      }
    }
  }
  public void start() {
    assert (state == START);

    numTuplesSent = 0;
    startTime = System.currentTimeMillis();

    state = RUNNING;
    intervalStartTime = startTime;
    intervalNumTuplesSent = 0;

    lastOutputTs = 0;

    sendSchema();

    timer = new Timer();
    timer.schedule(new FeederTask(), 1, SLEEP_TIME);
  }
 private int getCurTs() {
   return (int) ((System.currentTimeMillis() - startTime) / 1000);
 }